From wxt at rock-chips.com  Fri Jul  1 00:36:08 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Fri,  1 Jul 2016 15:36:08 +0800
Subject: [PATCH 0/2] arm64: dts: rockchip: Support PMU for rk3399 SoCs
Message-ID: <1467358570-24417-1-git-send-email-wxt@rock-chips.com>

Hello Heiko, Marc & ARM guys

When Jay first submitted the rk3399.dtsi upstream
<https://patchwork.kernel.org/patch/8885821/> he had the PMU node in there,
but then took it out because the upstream binding wasn't done yet.
It looks as if the upstream stuff has landed, since in linux/master I see:
287e9357abcc DT/arm,gic-v3: Documment PPI partition support
e3825ba1af3a irqchip/gic-v3: Add support for partitioned PPIs
9e2c986cb460 irqchip: Add per-cpu interrupt partitioning library
222df54fd8b7 genirq: Allow the affinity of a percpu interrupt to be set/retrieved
651e8b54abde irqdomain: Allow domain matching on irq_fwspec

This series patches add to support the rk3399 SoCs PMU.
As my do some tests with ChromeOs for my rk3399 board.

TEST=$perf list |grep Hardware
cpu-cycles OR cycles                               [Hardware event]
instructions                                       [Hardware event]
cache-references                                   [Hardware event]
branch-instructions OR branches                    [Hardware event]
branch-misses                                      [Hardware event]
..
Also,
localhost / # perf stat sleep 1
watch the Hardware event.
e.g.:
2375647      instructions              #    0.79  insns per cycle
18482      branch-misses             #    0.00% of all branches
--

And perf top/stat to watch the hardware event. That work for me on now.

-Caesar



Caesar Wang (2):
  arm64: dts: rockchip: change all interrupts cells for 4 on rk3399 SoCs
  arm64: dts: rockchip: support the pmu node for rk3399

 arch/arm64/boot/dts/rockchip/rk3399.dtsi | 124 +++++++++++++++++++------------
 1 file changed, 75 insertions(+), 49 deletions(-)

-- 
1.9.1



From wxt at rock-chips.com  Fri Jul  1 00:36:09 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Fri,  1 Jul 2016 15:36:09 +0800
Subject: [PATCH 1/2] arm64: dts: rockchip: change all interrupts cells for 4
 on rk3399 SoCs
In-Reply-To: <1467358570-24417-1-git-send-email-wxt@rock-chips.com>
References: <1467358570-24417-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1467358570-24417-2-git-send-email-wxt@rock-chips.com>

Add the interrupts cells value for 4, and the 4th cell is zero.

Due to the doc[0] said:" the system requires describing PPI affinity,
then the value must be at least 4"
The 4th cell is a phandle to a node describing a set of CPUs this
interrupt is affine to. The interrupt must be a PPI, and the node
pointed must be a subnode of the "ppi-partitions" subnode. For
interrupt types other than PPI or PPIs that are not partitionned,
this cell must be zero. See the "ppi-partitions" node description
below.

[0]:
Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt

Signed-off-by: Caesar Wang <wxt at rock-chips.com>
Cc: Heiko Stuebner <heiko at sntech.de>
Cc: Will Deacon <will.deacon at arm.com>
Cc: Marc Zyngier <marc.zyngier at arm.com>
CC: linux-arm-kernel at lists.infradead.org
---

 arch/arm64/boot/dts/rockchip/rk3399.dtsi | 98 ++++++++++++++++----------------
 1 file changed, 49 insertions(+), 49 deletions(-)

diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index a6dd623..8f0a069 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -159,10 +159,10 @@
 
 	timer {
 		compatible = "arm,armv8-timer";
-		interrupts = <GIC_PPI 13 IRQ_TYPE_LEVEL_LOW>,
-			     <GIC_PPI 14 IRQ_TYPE_LEVEL_LOW>,
-			     <GIC_PPI 11 IRQ_TYPE_LEVEL_LOW>,
-			     <GIC_PPI 10 IRQ_TYPE_LEVEL_LOW>;
+		interrupts = <GIC_PPI 13 IRQ_TYPE_LEVEL_LOW 0>,
+			     <GIC_PPI 14 IRQ_TYPE_LEVEL_LOW 0>,
+			     <GIC_PPI 11 IRQ_TYPE_LEVEL_LOW 0>,
+			     <GIC_PPI 10 IRQ_TYPE_LEVEL_LOW 0>;
 	};
 
 	xin24m: xin24m {
@@ -181,8 +181,8 @@
 		dmac_bus: dma-controller at ff6d0000 {
 			compatible = "arm,pl330", "arm,primecell";
 			reg = <0x0 0xff6d0000 0x0 0x4000>;
-			interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>,
-				     <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
+			interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH 0>,
+				     <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH 0>;
 			#dma-cells = <1>;
 			clocks = <&cru ACLK_DMAC0_PERILP>;
 			clock-names = "apb_pclk";
@@ -191,8 +191,8 @@
 		dmac_peri: dma-controller at ff6e0000 {
 			compatible = "arm,pl330", "arm,primecell";
 			reg = <0x0 0xff6e0000 0x0 0x4000>;
-			interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>,
-				     <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
+			interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH 0>,
+				     <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH 0>;
 			#dma-cells = <1>;
 			clocks = <&cru ACLK_DMAC1_PERILP>;
 			clock-names = "apb_pclk";
@@ -203,7 +203,7 @@
 		compatible = "rockchip,rk3399-dw-mshc",
 			     "rockchip,rk3288-dw-mshc";
 		reg = <0x0 0xfe310000 0x0 0x4000>;
-		interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH 0>;
 		clock-freq-min-max = <400000 150000000>;
 		clocks = <&cru HCLK_SDIO>, <&cru SCLK_SDIO>,
 			 <&cru SCLK_SDIO_DRV>, <&cru SCLK_SDIO_SAMPLE>;
@@ -216,7 +216,7 @@
 		compatible = "rockchip,rk3399-dw-mshc",
 			     "rockchip,rk3288-dw-mshc";
 		reg = <0x0 0xfe320000 0x0 0x4000>;
-		interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH 0>;
 		clock-freq-min-max = <400000 150000000>;
 		clocks = <&cru HCLK_SDMMC>, <&cru SCLK_SDMMC>,
 			 <&cru SCLK_SDMMC_DRV>, <&cru SCLK_SDMMC_SAMPLE>;
@@ -228,7 +228,7 @@
 	sdhci: sdhci at fe330000 {
 		compatible = "rockchip,rk3399-sdhci-5.1", "arasan,sdhci-5.1";
 		reg = <0x0 0xfe330000 0x0 0x10000>;
-		interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH 0>;
 		arasan,soc-ctl-syscon = <&grf>;
 		assigned-clocks = <&cru SCLK_EMMC>;
 		assigned-clock-rates = <200000000>;
@@ -244,7 +244,7 @@
 	usb_host0_ehci: usb at fe380000 {
 		compatible = "generic-ehci";
 		reg = <0x0 0xfe380000 0x0 0x20000>;
-		interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH 0>;
 		clocks = <&cru HCLK_HOST0>, <&cru HCLK_HOST0_ARB>;
 		clock-names = "hclk_host0", "hclk_host0_arb";
 		status = "disabled";
@@ -253,7 +253,7 @@
 	usb_host0_ohci: usb at fe3a0000 {
 		compatible = "generic-ohci";
 		reg = <0x0 0xfe3a0000 0x0 0x20000>;
-		interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH 0>;
 		clocks = <&cru HCLK_HOST0>, <&cru HCLK_HOST0_ARB>;
 		clock-names = "hclk_host0", "hclk_host0_arb";
 		status = "disabled";
@@ -262,7 +262,7 @@
 	usb_host1_ehci: usb at fe3c0000 {
 		compatible = "generic-ehci";
 		reg = <0x0 0xfe3c0000 0x0 0x20000>;
-		interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH 0>;
 		clocks = <&cru HCLK_HOST1>, <&cru HCLK_HOST1_ARB>;
 		clock-names = "hclk_host1", "hclk_host1_arb";
 		status = "disabled";
@@ -271,7 +271,7 @@
 	usb_host1_ohci: usb at fe3e0000 {
 		compatible = "generic-ohci";
 		reg = <0x0 0xfe3e0000 0x0 0x20000>;
-		interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH 0>;
 		clocks = <&cru HCLK_HOST1>, <&cru HCLK_HOST1_ARB>;
 		clock-names = "hclk_host1", "hclk_host1_arb";
 		status = "disabled";
@@ -279,7 +279,7 @@
 
 	gic: interrupt-controller at fee00000 {
 		compatible = "arm,gic-v3";
-		#interrupt-cells = <3>;
+		#interrupt-cells = <4>;
 		#address-cells = <2>;
 		#size-cells = <2>;
 		ranges;
@@ -290,7 +290,7 @@
 		      <0x0 0xfff00000 0 0x10000>, /* GICC */
 		      <0x0 0xfff10000 0 0x10000>, /* GICH */
 		      <0x0 0xfff20000 0 0x10000>; /* GICV */
-		interrupts = <GIC_PPI 9 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_PPI 9 IRQ_TYPE_LEVEL_HIGH 0>;
 		its: interrupt-controller at fee20000 {
 			compatible = "arm,gic-v3-its";
 			msi-controller;
@@ -305,7 +305,7 @@
 		assigned-clock-rates = <200000000>;
 		clocks = <&cru SCLK_I2C1>, <&cru PCLK_I2C1>;
 		clock-names = "i2c", "pclk";
-		interrupts = <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH 0>;
 		pinctrl-names = "default";
 		pinctrl-0 = <&i2c1_xfer>;
 		#address-cells = <1>;
@@ -320,7 +320,7 @@
 		assigned-clock-rates = <200000000>;
 		clocks = <&cru SCLK_I2C2>, <&cru PCLK_I2C2>;
 		clock-names = "i2c", "pclk";
-		interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH 0>;
 		pinctrl-names = "default";
 		pinctrl-0 = <&i2c2_xfer>;
 		#address-cells = <1>;
@@ -335,7 +335,7 @@
 		assigned-clock-rates = <200000000>;
 		clocks = <&cru SCLK_I2C3>, <&cru PCLK_I2C3>;
 		clock-names = "i2c", "pclk";
-		interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH 0>;
 		pinctrl-names = "default";
 		pinctrl-0 = <&i2c3_xfer>;
 		#address-cells = <1>;
@@ -350,7 +350,7 @@
 		assigned-clock-rates = <200000000>;
 		clocks = <&cru SCLK_I2C5>, <&cru PCLK_I2C5>;
 		clock-names = "i2c", "pclk";
-		interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH 0>;
 		pinctrl-names = "default";
 		pinctrl-0 = <&i2c5_xfer>;
 		#address-cells = <1>;
@@ -365,7 +365,7 @@
 		assigned-clock-rates = <200000000>;
 		clocks = <&cru SCLK_I2C6>, <&cru PCLK_I2C6>;
 		clock-names = "i2c", "pclk";
-		interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH 0>;
 		pinctrl-names = "default";
 		pinctrl-0 = <&i2c6_xfer>;
 		#address-cells = <1>;
@@ -380,7 +380,7 @@
 		assigned-clock-rates = <200000000>;
 		clocks = <&cru SCLK_I2C7>, <&cru PCLK_I2C7>;
 		clock-names = "i2c", "pclk";
-		interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH 0>;
 		pinctrl-names = "default";
 		pinctrl-0 = <&i2c7_xfer>;
 		#address-cells = <1>;
@@ -393,7 +393,7 @@
 		reg = <0x0 0xff180000 0x0 0x100>;
 		clocks = <&cru SCLK_UART0>, <&cru PCLK_UART0>;
 		clock-names = "baudclk", "apb_pclk";
-		interrupts = <GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH 0>;
 		reg-shift = <2>;
 		reg-io-width = <4>;
 		pinctrl-names = "default";
@@ -406,7 +406,7 @@
 		reg = <0x0 0xff190000 0x0 0x100>;
 		clocks = <&cru SCLK_UART1>, <&cru PCLK_UART1>;
 		clock-names = "baudclk", "apb_pclk";
-		interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH 0>;
 		reg-shift = <2>;
 		reg-io-width = <4>;
 		pinctrl-names = "default";
@@ -419,7 +419,7 @@
 		reg = <0x0 0xff1a0000 0x0 0x100>;
 		clocks = <&cru SCLK_UART2>, <&cru PCLK_UART2>;
 		clock-names = "baudclk", "apb_pclk";
-		interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH 0>;
 		reg-shift = <2>;
 		reg-io-width = <4>;
 		pinctrl-names = "default";
@@ -432,7 +432,7 @@
 		reg = <0x0 0xff1b0000 0x0 0x100>;
 		clocks = <&cru SCLK_UART3>, <&cru PCLK_UART3>;
 		clock-names = "baudclk", "apb_pclk";
-		interrupts = <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH 0>;
 		reg-shift = <2>;
 		reg-io-width = <4>;
 		pinctrl-names = "default";
@@ -445,7 +445,7 @@
 		reg = <0x0 0xff1c0000 0x0 0x1000>;
 		clocks = <&cru SCLK_SPI0>, <&cru PCLK_SPI0>;
 		clock-names = "spiclk", "apb_pclk";
-		interrupts = <GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH 0>;
 		pinctrl-names = "default";
 		pinctrl-0 = <&spi0_clk &spi0_tx &spi0_rx &spi0_cs0>;
 		#address-cells = <1>;
@@ -458,7 +458,7 @@
 		reg = <0x0 0xff1d0000 0x0 0x1000>;
 		clocks = <&cru SCLK_SPI1>, <&cru PCLK_SPI1>;
 		clock-names = "spiclk", "apb_pclk";
-		interrupts = <GIC_SPI 53 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 53 IRQ_TYPE_LEVEL_HIGH 0>;
 		pinctrl-names = "default";
 		pinctrl-0 = <&spi1_clk &spi1_tx &spi1_rx &spi1_cs0>;
 		#address-cells = <1>;
@@ -471,7 +471,7 @@
 		reg = <0x0 0xff1e0000 0x0 0x1000>;
 		clocks = <&cru SCLK_SPI2>, <&cru PCLK_SPI2>;
 		clock-names = "spiclk", "apb_pclk";
-		interrupts = <GIC_SPI 52 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 52 IRQ_TYPE_LEVEL_HIGH 0>;
 		pinctrl-names = "default";
 		pinctrl-0 = <&spi2_clk &spi2_tx &spi2_rx &spi2_cs0>;
 		#address-cells = <1>;
@@ -484,7 +484,7 @@
 		reg = <0x0 0xff1f0000 0x0 0x1000>;
 		clocks = <&cru SCLK_SPI4>, <&cru PCLK_SPI4>;
 		clock-names = "spiclk", "apb_pclk";
-		interrupts = <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH 0>;
 		pinctrl-names = "default";
 		pinctrl-0 = <&spi4_clk &spi4_tx &spi4_rx &spi4_cs0>;
 		#address-cells = <1>;
@@ -497,7 +497,7 @@
 		reg = <0x0 0xff200000 0x0 0x1000>;
 		clocks = <&cru SCLK_SPI5>, <&cru PCLK_SPI5>;
 		clock-names = "spiclk", "apb_pclk";
-		interrupts = <GIC_SPI 132 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 132 IRQ_TYPE_LEVEL_HIGH 0>;
 		pinctrl-names = "default";
 		pinctrl-0 = <&spi5_clk &spi5_tx &spi5_rx &spi5_cs0>;
 		#address-cells = <1>;
@@ -577,7 +577,7 @@
 	tsadc: tsadc at ff260000 {
 		compatible = "rockchip,rk3399-tsadc";
 		reg = <0x0 0xff260000 0x0 0x100>;
-		interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH 0>;
 		assigned-clocks = <&cru SCLK_TSADC>;
 		assigned-clock-rates = <750000>;
 		clocks = <&cru SCLK_TSADC>, <&cru PCLK_TSADC>;
@@ -611,7 +611,7 @@
 		reg = <0x0 0xff350000 0x0 0x1000>;
 		clocks = <&pmucru SCLK_SPI3_PMU>, <&pmucru PCLK_SPI3_PMU>;
 		clock-names = "spiclk", "apb_pclk";
-		interrupts = <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH 0>;
 		pinctrl-names = "default";
 		pinctrl-0 = <&spi3_clk &spi3_tx &spi3_rx &spi3_cs0>;
 		#address-cells = <1>;
@@ -624,7 +624,7 @@
 		reg = <0x0 0xff370000 0x0 0x100>;
 		clocks = <&pmucru SCLK_UART4_PMU>, <&pmucru PCLK_UART4_PMU>;
 		clock-names = "baudclk", "apb_pclk";
-		interrupts = <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH 0>;
 		reg-shift = <2>;
 		reg-io-width = <4>;
 		pinctrl-names = "default";
@@ -639,7 +639,7 @@
 		assigned-clock-rates = <200000000>;
 		clocks = <&pmucru SCLK_I2C0_PMU>, <&pmucru PCLK_I2C0_PMU>;
 		clock-names = "i2c", "pclk";
-		interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH 0>;
 		pinctrl-names = "default";
 		pinctrl-0 = <&i2c0_xfer>;
 		#address-cells = <1>;
@@ -654,7 +654,7 @@
 		assigned-clock-rates = <200000000>;
 		clocks = <&pmucru SCLK_I2C4_PMU>, <&pmucru PCLK_I2C4_PMU>;
 		clock-names = "i2c", "pclk";
-		interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH 0>;
 		pinctrl-names = "default";
 		pinctrl-0 = <&i2c4_xfer>;
 		#address-cells = <1>;
@@ -669,7 +669,7 @@
 		assigned-clock-rates = <200000000>;
 		clocks = <&pmucru SCLK_I2C8_PMU>, <&pmucru PCLK_I2C8_PMU>;
 		clock-names = "i2c", "pclk";
-		interrupts = <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH 0>;
 		pinctrl-names = "default";
 		pinctrl-0 = <&i2c8_xfer>;
 		#address-cells = <1>;
@@ -778,13 +778,13 @@
 		compatible = "snps,dw-wdt";
 		reg = <0x0 0xff840000 0x0 0x100>;
 		clocks = <&cru PCLK_WDT>;
-		interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH 0>;
 	};
 
 	rktimer: rktimer at ff850000 {
 		compatible = "rockchip,rk3399-timer";
 		reg = <0x0 0xff850000 0x0 0x1000>;
-		interrupts = <GIC_SPI 81 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 81 IRQ_TYPE_LEVEL_HIGH 0>;
 		clocks = <&cru PCLK_TIMER0>, <&cru SCLK_TIMER00>;
 		clock-names = "pclk", "timer";
 	};
@@ -792,7 +792,7 @@
 	spdif: spdif at ff870000 {
 		compatible = "rockchip,rk3399-spdif";
 		reg = <0x0 0xff870000 0x0 0x1000>;
-		interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH 0>;
 		dmas = <&dmac_bus 7>;
 		dma-names = "tx";
 		clock-names = "mclk", "hclk";
@@ -806,7 +806,7 @@
 		compatible = "rockchip,rk3399-i2s", "rockchip,rk3066-i2s";
 		reg = <0x0 0xff880000 0x0 0x1000>;
 		rockchip,grf = <&grf>;
-		interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH 0>;
 		dmas = <&dmac_bus 0>, <&dmac_bus 1>;
 		dma-names = "tx", "rx";
 		clock-names = "i2s_clk", "i2s_hclk";
@@ -819,7 +819,7 @@
 	i2s1: i2s at ff890000 {
 		compatible = "rockchip,rk3399-i2s", "rockchip,rk3066-i2s";
 		reg = <0x0 0xff890000 0x0 0x1000>;
-		interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH 0>;
 		dmas = <&dmac_bus 2>, <&dmac_bus 3>;
 		dma-names = "tx", "rx";
 		clock-names = "i2s_clk", "i2s_hclk";
@@ -832,7 +832,7 @@
 	i2s2: i2s at ff8a0000 {
 		compatible = "rockchip,rk3399-i2s", "rockchip,rk3066-i2s";
 		reg = <0x0 0xff8a0000 0x0 0x1000>;
-		interrupts = <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH 0>;
 		dmas = <&dmac_bus 4>, <&dmac_bus 5>;
 		dma-names = "tx", "rx";
 		clock-names = "i2s_clk", "i2s_hclk";
@@ -852,7 +852,7 @@
 			compatible = "rockchip,gpio-bank";
 			reg = <0x0 0xff720000 0x0 0x100>;
 			clocks = <&pmucru PCLK_GPIO0_PMU>;
-			interrupts = <GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>;
+			interrupts = <GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH 0>;
 
 			gpio-controller;
 			#gpio-cells = <0x2>;
@@ -865,7 +865,7 @@
 			compatible = "rockchip,gpio-bank";
 			reg = <0x0 0xff730000 0x0 0x100>;
 			clocks = <&pmucru PCLK_GPIO1_PMU>;
-			interrupts = <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>;
+			interrupts = <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH 0>;
 
 			gpio-controller;
 			#gpio-cells = <0x2>;
@@ -878,7 +878,7 @@
 			compatible = "rockchip,gpio-bank";
 			reg = <0x0 0xff780000 0x0 0x100>;
 			clocks = <&cru PCLK_GPIO2>;
-			interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
+			interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH 0>;
 
 			gpio-controller;
 			#gpio-cells = <0x2>;
@@ -891,7 +891,7 @@
 			compatible = "rockchip,gpio-bank";
 			reg = <0x0 0xff788000 0x0 0x100>;
 			clocks = <&cru PCLK_GPIO3>;
-			interrupts = <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;
+			interrupts = <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH 0>;
 
 			gpio-controller;
 			#gpio-cells = <0x2>;
@@ -904,7 +904,7 @@
 			compatible = "rockchip,gpio-bank";
 			reg = <0x0 0xff790000 0x0 0x100>;
 			clocks = <&cru PCLK_GPIO4>;
-			interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>;
+			interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH 0>;
 
 			gpio-controller;
 			#gpio-cells = <0x2>;
-- 
1.9.1



From wxt at rock-chips.com  Fri Jul  1 00:36:10 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Fri,  1 Jul 2016 15:36:10 +0800
Subject: [PATCH 2/2] arm64: dts: rockchip: support the pmu node for rk3399
In-Reply-To: <1467358570-24417-1-git-send-email-wxt@rock-chips.com>
References: <1467358570-24417-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1467358570-24417-3-git-send-email-wxt@rock-chips.com>

As the Marc posted the patches [0] to support Partitioning per-cpu
interrupts. Let's add this patch to match it on rk3399 SoCs.

[0]:
https://lkml.org/lkml/2016/4/11/182

Signed-off-by: Caesar Wang <wxt at rock-chips.com>
Cc: Heiko Stuebner <heiko at sntech.de>
Cc: Will Deacon <will.deacon at arm.com>
Cc: Marc Zyngier <marc.zyngier at arm.com>
CC: linux-arm-kernel at lists.infradead.org

---

 arch/arm64/boot/dts/rockchip/rk3399.dtsi | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index 8f0a069..b260f62 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -165,6 +165,22 @@
 			     <GIC_PPI 10 IRQ_TYPE_LEVEL_LOW 0>;
 	};
 
+	pmu_a53 {
+		compatible = "arm,cortex-a53-pmu";
+		interrupts = <GIC_PPI 7 IRQ_TYPE_LEVEL_LOW &part0>;
+		interrupt-affinity = <&cpu_l0>,
+				     <&cpu_l1>,
+				     <&cpu_l2>,
+				     <&cpu_l3>;
+	};
+
+	pmu_a72 {
+		compatible = "arm,cortex-a72-pmu";
+		interrupts = <GIC_PPI 7 IRQ_TYPE_LEVEL_LOW &part1>;
+		interrupt-affinity = <&cpu_b0>,
+				     <&cpu_b1>;
+	};
+
 	xin24m: xin24m {
 		compatible = "fixed-clock";
 		clock-frequency = <24000000>;
@@ -296,6 +312,16 @@
 			msi-controller;
 			reg = <0x0 0xfee20000 0x0 0x20000>;
 		};
+
+		ppi-partitions {
+			part0: interrupt-partition-0 {
+				affinity = <&cpu_l0 &cpu_l1 &cpu_l2 &cpu_l3>;
+			};
+
+			part1: interrupt-partition-1 {
+				affinity = <&cpu_b0 &cpu_b1>;
+			};
+		};
 	};
 
 	i2c1: i2c at ff110000 {
-- 
1.9.1



From broonie at kernel.org  Fri Jul  1 01:06:14 2016
From: broonie at kernel.org (Mark Brown)
Date: Fri, 1 Jul 2016 09:06:14 +0100
Subject: [PATCH] regulator: pwm: Fix regulator ramp delay for continuous
 mode
In-Reply-To: <1467089591-7631-1-git-send-email-dianders@chromium.org>
References: <1467089591-7631-1-git-send-email-dianders@chromium.org>
Message-ID: <20160701080614.GA9196@sirena.org.uk>

On Mon, Jun 27, 2016 at 09:53:11PM -0700, Douglas Anderson wrote:

> Note that this patch is atop Boris's recent PWM regulator fixes.  If
> desired it wouldn't be too hard to write it atop the old code, though
> quite honestly anyone using a PWM regulator should probably be using his
> new code.

Given that those don't seem likely to go in any time soon and aren't
going to go in as a fix can you please respin against current code?
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160701/59101b7b/attachment.sig>

From heiko at sntech.de  Fri Jul  1 01:31:54 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Fri, 01 Jul 2016 10:31:54 +0200
Subject: [PATCH] clk: rockchip: fix the rk3399 spdif incorrect bit for DPTX
In-Reply-To: <1467253139-21112-1-git-send-email-zhengxing@rock-chips.com>
References: <1467253139-21112-1-git-send-email-zhengxing@rock-chips.com>
Message-ID: <2167990.nRjYb2NtK4@phil>

Am Donnerstag, 30. Juni 2016, 10:18:59 schrieb Xing Zheng:
> The CLKSEL_CON32 bit_0 is controlled for spdif_8ch, not spdif_rec_dptx,
> it should be bit_8, let's fix it.
> 
> Reported-by: Chris Zhong <zyw at rock-chips.com>
> Tested-by: Chris Zhong <zyw at rock-chips.com>
> Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>

applied to my clk branch for 4.8


From ykk at rock-chips.com  Fri Jul  1 02:18:04 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri,  1 Jul 2016 17:18:04 +0800
Subject: [PATCH v3 0/4] Add PSR function support for Analogix/Rockchip DP
Message-ID: <1467364685-21390-1-git-send-email-ykk@rock-chips.com>

Hi all,

The full name of PSR is Panel Self Refresh, panel device could refresh
itself with the hardware framebuffer in panel, this would make a lots
of sense to save the power consumption.

This v3 version have splited an common PSR driver for Rockchip, which is
biggest changes from v2.

This thread is based on Mark's RK3399 VOP thread[0] and my RK3399 eDP
thread[1].

[0]: https://patchwork.kernel.org/patch/8886041/
[1]: https://patchwork.kernel.org/patch/9204497/

Thanks,
- Yakir


Changes in v3:
- Remove the notify for waiting line_flag event (Daniel)
- implement the 'fb->dirty' callback function (Daniel)
- avoid to use notify to acqiure for vact event (Daniel)
- split the psr flow into an common abstracted PSR driver
- Export the 'rockchip_drm_wait_line_flag' symbol, and document it.
- Add 'line_flag_num_0' for RK3288/RK3036
- remove psr_active() callback which introduce in v2
- split analogix_dp_enable_psr(), make it more clearly
    analogix_dp_detect_sink_psr()
    analogix_dp_enable_sink_psr()
- remove some nosie register setting comments
- split the common psr logic into a seperate driver, make this to a
  simple sub-psr device driver.

Changes in v2:
- Introduce in v2, split VOP line flag changes out
- introduce in v2, splite the common Analogix DP changes out
- remove vblank notify out (Daniel)
- create a psr_active() callback in vop data struct.

Yakir Yang (4):
  drm/rockchip: vop: export line flag function
  drm/rockchip: add an common abstracted PSR driver
  drm/bridge: analogix_dp: add the PSR function support
  drm/rockchip: analogix_dp: implement PSR function

 drivers/gpu/drm/bridge/analogix/analogix_dp_core.c |  64 +++++++
 drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |   4 +
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  |  54 ++++++
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h  |  28 +++
 drivers/gpu/drm/rockchip/Makefile                  |   2 +-
 drivers/gpu/drm/rockchip/analogix_dp-rockchip.c    |  52 ++++++
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h        |   3 +
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c         |  12 ++
 drivers/gpu/drm/rockchip/rockchip_drm_psr.c        | 200 +++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_psr.h        |  12 ++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c        | 127 +++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h        |   3 +
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c        |   4 +
 include/drm/bridge/analogix_dp.h                   |   3 +
 14 files changed, 567 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.h

-- 
1.9.1




From ykk at rock-chips.com  Fri Jul  1 02:19:14 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri,  1 Jul 2016 17:19:14 +0800
Subject: [PATCH v3 2/4] drm/rockchip: add an common abstracted PSR driver
In-Reply-To: <1467364685-21390-1-git-send-email-ykk@rock-chips.com>
References: <1467364685-21390-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1467364754-21674-1-git-send-email-ykk@rock-chips.com>

The PSR driver have exported four symbols for specific device driver:
- rockchip_drm_psr_register()
- rockchip_drm_psr_unregister()
- rockchip_drm_psr_enable()
- rockchip_drm_psr_disable()
- rockchip_drm_psr_flush()

Encoder driver should call the register/unregister interfaces to hook
itself into common PSR driver, encoder have implement the 'psr_set'
callback which use the set PSR state in hardware side.

Crtc driver would call the enable/disable interfaces when vblank is
enable/disable, after that the common PSR driver would call the encoder
registered callback to set the PSR state.

Fb driver would call the flush interface in 'fb->dirty' callback, this
helper function would force all PSR enabled encoders to exit from PSR
for 3 seconds.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
Changes in v3:
- split the psr flow into an common abstracted PSR driver
- implement the 'fb->dirty' callback function (Daniel)
- avoid to use notify to acqiure for vact event (Daniel)
- remove psr_active() callback which introduce in v2

Changes in v2: None

 drivers/gpu/drm/rockchip/Makefile           |   2 +-
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c  |  12 ++
 drivers/gpu/drm/rockchip/rockchip_drm_psr.c | 200 ++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_psr.h |  12 ++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c |  24 ++++
 5 files changed, 249 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.h

diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index 05d0713..9746365 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -3,7 +3,7 @@
 # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
 
 rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
-		rockchip_drm_gem.o rockchip_drm_vop.o
+		rockchip_drm_gem.o rockchip_drm_psr.o rockchip_drm_vop.o
 rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
 
 obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
index 20f12bc..0fec18f 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
@@ -21,6 +21,7 @@
 
 #include "rockchip_drm_drv.h"
 #include "rockchip_drm_gem.h"
+#include "rockchip_drm_psr.h"
 
 #define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
 
@@ -66,9 +67,20 @@ static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
 				     rockchip_fb->obj[0], handle);
 }
 
+static int rockchip_drm_fb_dirty(struct drm_framebuffer *fb,
+				 struct drm_file *file,
+				 unsigned int flags, unsigned int color,
+				 struct drm_clip_rect *clips,
+				 unsigned int num_clips)
+{
+	rockchip_drm_psr_flush();
+	return 0;
+}
+
 static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
 	.destroy	= rockchip_drm_fb_destroy,
 	.create_handle	= rockchip_drm_fb_create_handle,
+	.dirty		= rockchip_drm_fb_dirty,
 };
 
 static struct rockchip_drm_fb *
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
new file mode 100644
index 0000000..c044443
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
@@ -0,0 +1,200 @@
+#include <drm/drm_crtc_helper.h>
+
+#include "rockchip_drm_psr.h"
+
+#define PSR_FLUSH_TIMEOUT	msecs_to_jiffies(3000) /* 3 seconds */
+
+static LIST_HEAD(psr_list);
+static DEFINE_MUTEX(psr_list_mutex);
+
+enum psr_state {
+	PSR_FLUSH,
+	PSR_ENABLE,
+	PSR_DISABLE,
+};
+
+struct psr_drv {
+	struct list_head list;
+	enum psr_state state;
+	struct mutex state_mutex;
+
+	struct timer_list flush_timer;
+
+	struct drm_encoder *encoder;
+	int (*set)(struct drm_encoder *encoder, bool enable);
+};
+
+static struct psr_drv *find_psr_by_crtc(struct drm_crtc *crtc)
+{
+	struct psr_drv *psr;
+
+	mutex_lock(&psr_list_mutex);
+	list_for_each_entry(psr, &psr_list, list) {
+		if (psr->encoder->crtc == crtc) {
+			mutex_unlock(&psr_list_mutex);
+			return psr;
+		}
+	}
+	mutex_unlock(&psr_list_mutex);
+
+	return ERR_PTR(-ENODEV);
+}
+
+static void psr_enable(struct psr_drv *psr)
+{
+	if (psr->state == PSR_ENABLE)
+		return;
+
+	mutex_lock(&psr->state_mutex);
+	psr->state = PSR_ENABLE;
+	psr->set(psr->encoder, true);
+	mutex_unlock(&psr->state_mutex);
+}
+
+static void psr_disable(struct psr_drv *psr)
+{
+	if (psr->state == PSR_DISABLE)
+		return;
+
+	mutex_lock(&psr->state_mutex);
+	psr->state = PSR_DISABLE;
+	psr->set(psr->encoder, false);
+	mutex_unlock(&psr->state_mutex);
+}
+
+static void psr_flush_handler(unsigned long data)
+{
+	struct psr_drv *psr = (struct psr_drv *)data;
+
+	if (!psr || psr->state != PSR_FLUSH)
+		return;
+
+	psr_enable(psr);
+}
+
+/**
+ * rockchip_drm_psr_enable - enable the encoder PSR which bind to given CRTC
+ * @crtc: CRTC to obtain the PSR encoder
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int rockchip_drm_psr_enable(struct drm_crtc *crtc)
+{
+	struct psr_drv *psr = find_psr_by_crtc(crtc);
+
+	if (IS_ERR(psr))
+		return PTR_ERR(psr);
+
+	psr_enable(psr);
+	return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_psr_enable);
+
+/**
+ * rockchip_drm_psr_disable - disable the encoder PSR which bind to given CRTC
+ * @crtc: CRTC to obtain the PSR encoder
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int rockchip_drm_psr_disable(struct drm_crtc *crtc)
+{
+	struct psr_drv *psr = find_psr_by_crtc(crtc);
+
+	if (IS_ERR(psr))
+		return PTR_ERR(psr);
+
+	psr_disable(psr);
+	return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_psr_disable);
+
+/**
+ * rockchip_drm_psr_flush - force to flush all registered PSR encoders
+ *
+ * Disable the PSR function for all registered encoders, and then enable the
+ * PSR function back after 5 second. If encoder PSR state have been changed
+ * during flush time, then keep the state no change after flush timeout.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+void rockchip_drm_psr_flush(void)
+{
+	struct psr_drv *psr;
+
+	mutex_lock(&psr_list_mutex);
+	list_for_each_entry(psr, &psr_list, list) {
+		if (psr->state == PSR_DISABLE)
+			continue;
+
+		mod_timer(&psr->flush_timer,
+			  round_jiffies_up(jiffies + PSR_FLUSH_TIMEOUT));
+
+		psr_disable(psr);
+		psr->state = PSR_FLUSH;
+	}
+	mutex_unlock(&psr_list_mutex);
+}
+EXPORT_SYMBOL(rockchip_drm_psr_flush);
+
+/**
+ * rockchip_drm_psr_register - register encoder to psr driver
+ * @encoder: encoder that obtain the PSR function
+ * @psr_set: call back to set PSR state
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int rockchip_drm_psr_register(struct drm_encoder *encoder,
+			      int (*psr_set)(struct drm_encoder *, bool enable))
+{
+	struct psr_drv *psr;
+
+	if (!encoder || !psr_set)
+		return -EINVAL;
+
+	psr = kzalloc(sizeof(struct psr_drv), GFP_KERNEL);
+	if (!psr)
+		return -ENOMEM;
+
+	setup_timer(&psr->flush_timer, psr_flush_handler, (unsigned long)psr);
+
+	mutex_init(&psr->state_mutex);
+
+	psr->state = PSR_DISABLE;
+	psr->encoder = encoder;
+	psr->set = psr_set;
+
+	mutex_lock(&psr_list_mutex);
+	list_add_tail(&psr->list, &psr_list);
+	mutex_unlock(&psr_list_mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_psr_register);
+
+/**
+ * rockchip_drm_psr_unregister - unregister encoder to psr driver
+ * @encoder: encoder that obtain the PSR function
+ * @psr_set: call back to set PSR state
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
+{
+	struct psr_drv *psr;
+
+	mutex_lock(&psr_list_mutex);
+	list_for_each_entry(psr, &psr_list, list) {
+		if (psr->encoder == encoder) {
+			del_timer(&psr->flush_timer);
+			list_del(&psr->list);
+			kfree(psr);
+		}
+	}
+	mutex_unlock(&psr_list_mutex);
+}
+EXPORT_SYMBOL(rockchip_drm_psr_unregister);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.h b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
new file mode 100644
index 0000000..622f605
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
@@ -0,0 +1,12 @@
+#ifndef __ROCKCHIP_DRM_PSR___
+#define __ROCKCHIP_DRM_PSR___
+
+void rockchip_drm_psr_flush(void);
+int rockchip_drm_psr_enable(struct drm_crtc *crtc);
+int rockchip_drm_psr_disable(struct drm_crtc *crtc);
+
+int rockchip_drm_psr_register(struct drm_encoder *encoder,
+			    int (*psr_set)(struct drm_encoder *, bool enable));
+void rockchip_drm_psr_unregister(struct drm_encoder *encoder);
+
+#endif /* __ROCKCHIP_DRM_PSR__ */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index cd3cac5..3c6dfc5 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -34,6 +34,7 @@
 #include "rockchip_drm_drv.h"
 #include "rockchip_drm_gem.h"
 #include "rockchip_drm_fb.h"
+#include "rockchip_drm_psr.h"
 #include "rockchip_drm_vop.h"
 
 #define __REG_SET_RELAXED(x, off, mask, shift, v, write_mask) \
@@ -121,6 +122,9 @@ struct vop {
 	/* protected by dev->event_lock */
 	struct drm_pending_vblank_event *event;
 
+	bool psr_enabled;
+	struct delayed_work psr_work;
+
 	struct completion line_flag_completion;
 
 	const struct vop_data *data;
@@ -911,6 +915,16 @@ static const struct drm_plane_funcs vop_plane_funcs = {
 	.atomic_destroy_state = vop_atomic_plane_destroy_state,
 };
 
+static void vop_psr_work(struct work_struct *work)
+{
+	struct vop *vop = container_of(work, typeof(*vop), psr_work.work);
+
+	if (vop->psr_enabled)
+		rockchip_drm_psr_enable(&vop->crtc);
+	else
+		rockchip_drm_psr_disable(&vop->crtc);
+}
+
 static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
 {
 	struct vop *vop = to_vop(crtc);
@@ -925,6 +939,9 @@ static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
 
 	spin_unlock_irqrestore(&vop->irq_lock, flags);
 
+	vop->psr_enabled = false;
+	schedule_delayed_work(&vop->psr_work, msecs_to_jiffies(10));
+
 	return 0;
 }
 
@@ -941,6 +958,9 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc)
 	VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0);
 
 	spin_unlock_irqrestore(&vop->irq_lock, flags);
+
+	vop->psr_enabled = true;
+	schedule_delayed_work(&vop->psr_work, msecs_to_jiffies(10));
 }
 
 static void vop_crtc_wait_for_update(struct drm_crtc *crtc)
@@ -1582,6 +1602,10 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
 		return ret;
 
 	pm_runtime_enable(&pdev->dev);
+
+	vop->psr_enabled = false;
+	INIT_DELAYED_WORK(&vop->psr_work, vop_psr_work);
+
 	return 0;
 }
 
-- 
1.9.1




From ykk at rock-chips.com  Fri Jul  1 02:19:05 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri,  1 Jul 2016 17:19:05 +0800
Subject: [PATCH v3 1/4] drm/rockchip: vop: export line flag function
In-Reply-To: <1467364685-21390-1-git-send-email-ykk@rock-chips.com>
References: <1467364685-21390-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1467364745-21621-1-git-send-email-ykk@rock-chips.com>

VOP have integrated a hardware counter which indicate the exact display
line that vop is scanning. And if we're interested in a specific line,
we can set the line number to vop line_flag register, and then vop would
generate a line_flag interrupt for it.

For example eDP PSR function is interested in the vertical blanking
period, then driver could set the line number to zero.

This patch have exported a symbol that allow other driver to listen the
line flag event with given timeout limit:
-  rockchip_drm_wait_line_flag()

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
Changes in v3:
- Export the 'rockchip_drm_wait_line_flag' symbol, and document it.
- Add 'line_flag_num_0' for RK3288/RK3036
- Remove the notify for waiting line_flag event (Daniel)

Changes in v2:
- Introduce in v2, split VOP line flag changes out

 drivers/gpu/drm/rockchip/rockchip_drm_drv.h |   3 +
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 103 ++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   3 +
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c |   4 ++
 4 files changed, 113 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index ea39329..239b830 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -70,4 +70,7 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
 				   struct device *dev);
 void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
 				    struct device *dev);
+int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
+				unsigned int mstimeout);
+
 #endif /* _ROCKCHIP_DRM_DRV_H_ */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index c8a62a8..cd3cac5 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -121,6 +121,8 @@ struct vop {
 	/* protected by dev->event_lock */
 	struct drm_pending_vblank_event *event;
 
+	struct completion line_flag_completion;
+
 	const struct vop_data *data;
 
 	uint32_t *regsbak;
@@ -431,6 +433,59 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop)
 	spin_unlock_irqrestore(&vop->irq_lock, flags);
 }
 
+/*
+ * (1) each frame starts at the start of the Vsync pulse which is signaled by
+ *     the "FRAME_SYNC" interrupt.
+ * (2) the active data region of each frame ends at dsp_vact_end
+ * (3) we should program this same number (dsp_vact_end) into dsp_line_frag_num,
+ *      to get "LINE_FLAG" interrupt at the end of the active on screen data.
+ *
+ * VOP_INTR_CTRL0.dsp_line_frag_num = VOP_DSP_VACT_ST_END.dsp_vact_end
+ * Interrupts
+ * LINE_FLAG -------------------------------+
+ * FRAME_SYNC ----+                         |
+ *                |                         |
+ *                v                         v
+ *                | Vsync | Vbp |  Vactive  | Vfp |
+ *                        ^     ^           ^     ^
+ *                        |     |           |     |
+ *                        |     |           |     |
+ * dsp_vs_end ------------+     |           |     |   VOP_DSP_VTOTAL_VS_END
+ * dsp_vact_start --------------+           |     |   VOP_DSP_VACT_ST_END
+ * dsp_vact_end ----------------------------+     |   VOP_DSP_VACT_ST_END
+ * dsp_total -------------------------------------+   VOP_DSP_VTOTAL_VS_END
+ */
+
+static void vop_line_flag_irq_enable(struct vop *vop, int line_num)
+{
+	unsigned long flags;
+
+	if (WARN_ON(!vop->is_enabled))
+		return;
+
+	spin_lock_irqsave(&vop->irq_lock, flags);
+
+	VOP_CTRL_SET(vop, line_flag_num_0, line_num);
+	VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1);
+	vop_cfg_done(vop);
+
+	spin_unlock_irqrestore(&vop->irq_lock, flags);
+}
+
+static void vop_line_flag_irq_disable(struct vop *vop)
+{
+	unsigned long flags;
+
+	if (WARN_ON(!vop->is_enabled))
+		return;
+
+	spin_lock_irqsave(&vop->irq_lock, flags);
+
+	VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 0);
+
+	spin_unlock_irqrestore(&vop->irq_lock, flags);
+}
+
 static void vop_enable(struct drm_crtc *crtc)
 {
 	struct vop *vop = to_vop(crtc);
@@ -1157,6 +1212,13 @@ static irqreturn_t vop_isr(int irq, void *data)
 		ret = IRQ_HANDLED;
 	}
 
+	if (active_irqs & LINE_FLAG_INTR) {
+		if (!completion_done(&vop->line_flag_completion))
+			complete(&vop->line_flag_completion);
+		active_irqs &= ~LINE_FLAG_INTR;
+		ret = IRQ_HANDLED;
+	}
+
 	if (active_irqs & FS_INTR) {
 		drm_crtc_handle_vblank(crtc);
 		vop_handle_vblank(vop);
@@ -1255,6 +1317,7 @@ static int vop_create_crtc(struct vop *vop)
 
 	init_completion(&vop->dsp_hold_completion);
 	init_completion(&vop->wait_update_complete);
+	init_completion(&vop->line_flag_completion);
 	crtc->port = port;
 	rockchip_register_crtc_funcs(crtc, &private_crtc_funcs);
 
@@ -1411,6 +1474,46 @@ static void vop_win_init(struct vop *vop)
 	}
 }
 
+/**
+ * rockchip_drm_wait_line_flag - acqiure the give line flag event
+ * @crtc: CRTC to enable line flag
+ * @line_num: interested line number
+ * @mstimeout: millisecond for timeout
+ *
+ * Driver would hold here until the interested line flag interrupt have
+ * happened or timeout to wait.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
+				unsigned int mstimeout)
+{
+	struct vop *vop = to_vop(crtc);
+	unsigned long jiffies_left;
+
+	if (!crtc || !vop->is_enabled)
+		return -ENODEV;
+
+	if (line_num > crtc->mode.vtotal || mstimeout <= 0)
+		return -EINVAL;
+
+	reinit_completion(&vop->line_flag_completion);
+	vop_line_flag_irq_enable(vop, line_num);
+
+	jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion,
+						   msecs_to_jiffies(mstimeout));
+	vop_line_flag_irq_disable(vop);
+
+	if (jiffies_left == 0) {
+		dev_err(vop->dev, "Timeout waiting for IRQ\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_wait_line_flag);
+
 static int vop_bind(struct device *dev, struct device *master, void *data)
 {
 	struct platform_device *pdev = to_platform_device(dev);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index ff4f52e..34fcd03 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -61,6 +61,9 @@ struct vop_ctrl {
 	struct vop_reg hpost_st_end;
 	struct vop_reg vpost_st_end;
 
+	struct vop_reg line_flag_num_0;
+	struct vop_reg line_flag_num_1;
+
 	struct vop_reg cfg_done;
 };
 
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 6f42e56..e9211c9 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -122,6 +122,7 @@ static const struct vop_ctrl rk3036_ctrl_data = {
 	.hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0),
 	.vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
 	.vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0),
+	.line_flag_num_0 = VOP_REG(RK3036_INT_STATUS, 0xfff, 12),
 	.cfg_done = VOP_REG(RK3036_REG_CFG_DONE, 0x1, 0),
 };
 
@@ -221,6 +222,7 @@ static const struct vop_ctrl rk3288_ctrl_data = {
 	.vact_st_end = VOP_REG(RK3288_DSP_VACT_ST_END, 0x1fff1fff, 0),
 	.hpost_st_end = VOP_REG(RK3288_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
 	.vpost_st_end = VOP_REG(RK3288_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
+	.line_flag_num_0 = VOP_REG(RK3288_INTR_CTRL0, 0x1fff, 12),
 	.cfg_done = VOP_REG(RK3288_REG_CFG_DONE, 0x1, 0),
 };
 
@@ -299,6 +301,8 @@ static const struct vop_ctrl rk3399_ctrl_data = {
 	.vact_st_end = VOP_REG(RK3399_DSP_VACT_ST_END, 0x1fff1fff, 0),
 	.hpost_st_end = VOP_REG(RK3399_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
 	.vpost_st_end = VOP_REG(RK3399_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
+	.line_flag_num_0 = VOP_REG(RK3399_LINE_FLAG, 0xffff, 0),
+	.line_flag_num_1 = VOP_REG(RK3399_LINE_FLAG, 0xffff, 16),
 	.cfg_done = VOP_REG_MASK(RK3399_REG_CFG_DONE, 0x1, 0),
 };
 
-- 
1.9.1




From ykk at rock-chips.com  Fri Jul  1 02:19:28 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri,  1 Jul 2016 17:19:28 +0800
Subject: [PATCH v3 3/4] drm/bridge: analogix_dp: add the PSR function support
In-Reply-To: <1467364685-21390-1-git-send-email-ykk@rock-chips.com>
References: <1467364685-21390-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1467364768-21741-1-git-send-email-ykk@rock-chips.com>

The full name of PSR is Panel Self Refresh, panel device could refresh
itself with the hardware framebuffer in panel, this would make lots of
sense to save the power consumption.

This patch have exported two symbols for platform driver to implement
the PSR function in hardware side:
- analogix_dp_active_psr()
- analogix_dp_inactive_psr()

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
Changes in v3:
- split analogix_dp_enable_psr(), make it more clearly
    analogix_dp_detect_sink_psr()
    analogix_dp_enable_sink_psr()
- remove some nosie register setting comments

Changes in v2:
- introduce in v2, splite the common Analogix DP changes out

 drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 64 ++++++++++++++++++++++
 drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |  4 ++
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  | 54 ++++++++++++++++++
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h  | 28 ++++++++++
 include/drm/bridge/analogix_dp.h                   |  3 +
 5 files changed, 153 insertions(+)

diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index 32715da..b557097 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -97,6 +97,66 @@ static int analogix_dp_detect_hpd(struct analogix_dp_device *dp)
 	return 0;
 }
 
+int analogix_dp_active_psr(struct device *dev)
+{
+	struct analogix_dp_device *dp = dev_get_drvdata(dev);
+
+	if (!dp->psr_support)
+		return -EINVAL;
+
+	analogix_dp_send_psr_spd(dp, EDP_VSC_PSR_STATE_ACTIVE |
+				 EDP_VSC_PSR_CRC_VALUES_VALID);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(analogix_dp_active_psr);
+
+int analogix_dp_inactive_psr(struct device *dev)
+{
+	struct analogix_dp_device *dp = dev_get_drvdata(dev);
+
+	if (!dp->psr_support)
+		return -EINVAL;
+
+	analogix_dp_send_psr_spd(dp, 0);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(analogix_dp_inactive_psr);
+
+static bool analogix_dp_detect_sink_psr(struct analogix_dp_device *dp)
+{
+	unsigned char psr_version;
+
+	analogix_dp_read_byte_from_dpcd(dp, DP_PSR_SUPPORT, &psr_version);
+	dev_info(dp->dev, "Panel PSR version : %x\n", psr_version);
+
+	return (psr_version & DP_PSR_IS_SUPPORTED) ? true : false;
+}
+
+static int analogix_dp_enable_sink_psr(struct analogix_dp_device *dp)
+{
+	unsigned char psr_en;
+
+	/* Disable psr function */
+	analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
+	psr_en &= ~DP_PSR_ENABLE;
+	analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
+
+	/* Main-Link transmitter remains active during PSR active states */
+	analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
+	psr_en = DP_PSR_MAIN_LINK_ACTIVE | DP_PSR_CRC_VERIFICATION;
+	analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
+
+	/* Enable psr function */
+	analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
+	psr_en = DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE |
+		 DP_PSR_CRC_VERIFICATION;
+	analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
+
+	analogix_dp_enable_psr_crc(dp);
+
+	return 0;
+}
+
 static unsigned char analogix_dp_calc_edid_check_sum(unsigned char *edid_data)
 {
 	int i;
@@ -921,6 +981,10 @@ static void analogix_dp_commit(struct analogix_dp_device *dp)
 
 	/* Enable video */
 	analogix_dp_start_video(dp);
+
+	dp->psr_support = analogix_dp_detect_sink_psr(dp);
+	if (dp->psr_support)
+		analogix_dp_enable_sink_psr(dp);
 }
 
 int analogix_dp_get_modes(struct drm_connector *connector)
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
index b456380..6ca5dde 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
@@ -177,6 +177,7 @@ struct analogix_dp_device {
 	int			hpd_gpio;
 	bool                    force_hpd;
 	unsigned char           edid[EDID_BLOCK_LENGTH * 2];
+	bool			psr_support;
 
 	struct analogix_dp_plat_data *plat_data;
 };
@@ -278,4 +279,7 @@ int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp);
 void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp);
 void analogix_dp_enable_scrambling(struct analogix_dp_device *dp);
 void analogix_dp_disable_scrambling(struct analogix_dp_device *dp);
+void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp);
+void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, int db1);
+
 #endif /* _ANALOGIX_DP_CORE_H */
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
index 48030f0..e8372c7 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
@@ -1322,3 +1322,57 @@ void analogix_dp_disable_scrambling(struct analogix_dp_device *dp)
 	reg |= SCRAMBLING_DISABLE;
 	writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
 }
+
+void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp)
+{
+	writel(PSR_VID_CRC_FLUSH | PSR_VID_CRC_ENABLE,
+	       dp->reg_base + ANALOGIX_DP_CRC_CON);
+
+	usleep_range(10, 20);
+
+	writel(PSR_VID_CRC_ENABLE, dp->reg_base + ANALOGIX_DP_CRC_CON);
+}
+
+void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, int db1)
+{
+	unsigned int val;
+
+	/* don't send info frame */
+	val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+	val &= ~IF_EN;
+	writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+
+	/* configure single frame update mode */
+	writel(0x3, dp->reg_base + ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL);
+
+	/* configure VSC HB0 ~ HB3 */
+	writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_HB0);
+	writel(0x07, dp->reg_base + ANALOGIX_DP_SPD_HB1);
+	writel(0x02, dp->reg_base + ANALOGIX_DP_SPD_HB2);
+	writel(0x08, dp->reg_base + ANALOGIX_DP_SPD_HB3);
+
+	/* configure VSC HB0 ~ HB3 */
+	writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_PB0);
+	writel(0x16, dp->reg_base + ANALOGIX_DP_SPD_PB1);
+	writel(0xCE, dp->reg_base + ANALOGIX_DP_SPD_PB2);
+	writel(0x5D, dp->reg_base + ANALOGIX_DP_SPD_PB3);
+
+	/* configure DB0 / DB1 values */
+	writel(0x00, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB0);
+	writel(db1, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB1);
+
+	/* set reuse spd inforframe */
+	val = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
+	val |= REUSE_SPD_EN;
+	writel(val, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
+
+	/* mark info frame update */
+	val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+	val = (val | IF_UP) & ~IF_EN;
+	writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+
+	/* send info frame */
+	val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+	val |= IF_EN;
+	writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+}
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
index cdcc6c5..a2698e4 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
@@ -22,6 +22,8 @@
 #define ANALOGIX_DP_VIDEO_CTL_8			0x3C
 #define ANALOGIX_DP_VIDEO_CTL_10		0x44
 
+#define ANALOGIX_DP_SPDIF_AUDIO_CTL_0		0xD8
+
 #define ANALOGIX_DP_PLL_REG_1			0xfc
 #define ANALOGIX_DP_PLL_REG_2			0x9e4
 #define ANALOGIX_DP_PLL_REG_3			0x9e8
@@ -30,6 +32,21 @@
 
 #define ANALOGIX_DP_PD				0x12c
 
+#define ANALOGIX_DP_IF_TYPE			0x244
+#define ANALOGIX_DP_IF_PKT_DB1			0x254
+#define ANALOGIX_DP_IF_PKT_DB2			0x258
+#define ANALOGIX_DP_SPD_HB0			0x2F8
+#define ANALOGIX_DP_SPD_HB1			0x2FC
+#define ANALOGIX_DP_SPD_HB2			0x300
+#define ANALOGIX_DP_SPD_HB3			0x304
+#define ANALOGIX_DP_SPD_PB0			0x308
+#define ANALOGIX_DP_SPD_PB1			0x30C
+#define ANALOGIX_DP_SPD_PB2			0x310
+#define ANALOGIX_DP_SPD_PB3			0x314
+#define ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL	0x318
+#define ANALOGIX_DP_VSC_SHADOW_DB0		0x31C
+#define ANALOGIX_DP_VSC_SHADOW_DB1		0x320
+
 #define ANALOGIX_DP_LANE_MAP			0x35C
 
 #define ANALOGIX_DP_ANALOG_CTL_1		0x370
@@ -103,6 +120,8 @@
 
 #define ANALOGIX_DP_SOC_GENERAL_CTL		0x800
 
+#define ANALOGIX_DP_CRC_CON			0x890
+
 /* ANALOGIX_DP_TX_SW_RESET */
 #define RESET_DP_TX				(0x1 << 0)
 
@@ -151,6 +170,7 @@
 #define VID_CHK_UPDATE_TYPE_SHIFT		(4)
 #define VID_CHK_UPDATE_TYPE_1			(0x1 << 4)
 #define VID_CHK_UPDATE_TYPE_0			(0x0 << 4)
+#define REUSE_SPD_EN				(0x1 << 3)
 
 /* ANALOGIX_DP_VIDEO_CTL_8 */
 #define VID_HRES_TH(x)				(((x) & 0xf) << 4)
@@ -376,4 +396,12 @@
 #define VIDEO_MODE_SLAVE_MODE			(0x1 << 0)
 #define VIDEO_MODE_MASTER_MODE			(0x0 << 0)
 
+/* ANALOGIX_DP_PKT_SEND_CTL */
+#define IF_UP					(0x1 << 4)
+#define IF_EN					(0x1 << 0)
+
+/* ANALOGIX_DP_CRC_CON */
+#define PSR_VID_CRC_FLUSH			(0x1 << 2)
+#define PSR_VID_CRC_ENABLE			(0x1 << 0)
+
 #endif /* _ANALOGIX_DP_REG_H */
diff --git a/include/drm/bridge/analogix_dp.h b/include/drm/bridge/analogix_dp.h
index 261b86d..183a336 100644
--- a/include/drm/bridge/analogix_dp.h
+++ b/include/drm/bridge/analogix_dp.h
@@ -38,6 +38,9 @@ struct analogix_dp_plat_data {
 			 struct drm_connector *);
 };
 
+int analogix_dp_active_psr(struct device *dev);
+int analogix_dp_inactive_psr(struct device *dev);
+
 int analogix_dp_resume(struct device *dev);
 int analogix_dp_suspend(struct device *dev);
 
-- 
1.9.1




From ykk at rock-chips.com  Fri Jul  1 02:19:36 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri,  1 Jul 2016 17:19:36 +0800
Subject: [PATCH v3 4/4] drm/rockchip: analogix_dp: implement PSR function
In-Reply-To: <1467364685-21390-1-git-send-email-ykk@rock-chips.com>
References: <1467364685-21390-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1467364776-21794-1-git-send-email-ykk@rock-chips.com>

Alway enable the PSR function for Rockchip analogix_dp driver. If panel
don't support PSR, then the core analogix_dp would ignore this setting.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
Changes in v3:
- split the common psr logic into a seperate driver, make this to a
  simple sub-psr device driver.

Changes in v2:
- remove vblank notify out (Daniel)
- create a psr_active() callback in vop data struct.

 drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 52 +++++++++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
index e81e19a..80a60a6 100644
--- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
@@ -32,6 +32,7 @@
 #include <drm/bridge/analogix_dp.h>
 
 #include "rockchip_drm_drv.h"
+#include "rockchip_drm_psr.h"
 #include "rockchip_drm_vop.h"
 
 #define RK3288_GRF_SOC_CON6		0x25c
@@ -68,11 +69,53 @@ struct rockchip_dp_device {
 	struct regmap            *grf;
 	struct reset_control     *rst;
 
+	struct delayed_work	 psr_work;
+	unsigned int		 psr_state;
+
 	const struct rockchip_dp_chip_data *data;
 
 	struct analogix_dp_plat_data plat_data;
 };
 
+static int analogix_dp_psr_set(struct drm_encoder *encoder, bool enabled)
+{
+	struct rockchip_dp_device *dp = to_dp(encoder);
+
+	dev_dbg(dp->dev, "%s PSR...\n", enabled ? "Entry" : "Exit");
+
+	if (enabled)
+		dp->psr_state = EDP_VSC_PSR_STATE_ACTIVE;
+	else
+		dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE;
+
+	schedule_delayed_work(&dp->psr_work, msecs_to_jiffies(10));
+
+	return 0;
+}
+
+static void analogix_dp_psr_work(struct work_struct *work)
+{
+	struct rockchip_dp_device *dp =
+				container_of(work, typeof(*dp), psr_work.work);
+	struct drm_crtc *crtc = dp->encoder.crtc;
+	int psr_state = dp->psr_state;
+	int vact_end;
+	int ret;
+
+	if (!crtc)
+		return;
+
+	vact_end = crtc->mode.vtotal - crtc->mode.vsync_start + crtc->mode.vdisplay;
+
+	ret = rockchip_drm_wait_line_flag(dp->encoder.crtc, vact_end, 100);
+	if (ret == 0) {
+		if (psr_state == EDP_VSC_PSR_STATE_ACTIVE)
+			analogix_dp_active_psr(dp->dev);
+		else
+			analogix_dp_inactive_psr(dp->dev);
+	}
+}
+
 static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
 {
 	reset_control_assert(dp->rst);
@@ -340,12 +383,21 @@ static int rockchip_dp_bind(struct device *dev, struct device *master,
 	dp->plat_data.power_off = rockchip_dp_powerdown;
 	dp->plat_data.get_modes = rockchip_dp_get_modes;
 
+	dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE;
+	INIT_DELAYED_WORK(&dp->psr_work, analogix_dp_psr_work);
+
+	rockchip_drm_psr_register(&dp->encoder, analogix_dp_psr_set);
+
 	return analogix_dp_bind(dev, dp->drm_dev, &dp->plat_data);
 }
 
 static void rockchip_dp_unbind(struct device *dev, struct device *master,
 			       void *data)
 {
+	struct rockchip_dp_device *dp = dev_get_drvdata(dev);
+
+	rockchip_drm_psr_unregister(&dp->encoder);
+
 	return analogix_dp_unbind(dev, master, data);
 }
 
-- 
1.9.1




From mark.rutland at arm.com  Fri Jul  1 03:26:12 2016
From: mark.rutland at arm.com (Mark Rutland)
Date: Fri, 1 Jul 2016 11:26:12 +0100
Subject: [PATCH 2/2] arm64: dts: rockchip: support the pmu node for rk3399
In-Reply-To: <1467358570-24417-3-git-send-email-wxt@rock-chips.com>
References: <1467358570-24417-1-git-send-email-wxt@rock-chips.com>
 <1467358570-24417-3-git-send-email-wxt@rock-chips.com>
Message-ID: <20160701102612.GB22953@leverpostej>

On Fri, Jul 01, 2016 at 03:36:10PM +0800, Caesar Wang wrote:
> As the Marc posted the patches [0] to support Partitioning per-cpu
> interrupts. Let's add this patch to match it on rk3399 SoCs.
> 
> [0]:
> https://lkml.org/lkml/2016/4/11/182

The core IRQ support is merged, but it's worth noting that the perf code
doesn't use it yet. So we still need a patch adding support to the perf
code before we can change the dts.

I think Marc had a prototype of that somewhere.

Marc?

> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
> Cc: Heiko Stuebner <heiko at sntech.de>
> Cc: Will Deacon <will.deacon at arm.com>
> Cc: Marc Zyngier <marc.zyngier at arm.com>
> CC: linux-arm-kernel at lists.infradead.org
> 
> ---
> 
>  arch/arm64/boot/dts/rockchip/rk3399.dtsi | 26 ++++++++++++++++++++++++++
>  1 file changed, 26 insertions(+)
> 
> diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> index 8f0a069..b260f62 100644
> --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> @@ -165,6 +165,22 @@
>  			     <GIC_PPI 10 IRQ_TYPE_LEVEL_LOW 0>;
>  	};
>  
> +	pmu_a53 {
> +		compatible = "arm,cortex-a53-pmu";
> +		interrupts = <GIC_PPI 7 IRQ_TYPE_LEVEL_LOW &part0>;
> +		interrupt-affinity = <&cpu_l0>,
> +				     <&cpu_l1>,
> +				     <&cpu_l2>,
> +				     <&cpu_l3>;
> +	};

I'm not keen on having to redundantly describe the affinity in
interrupt-affinity and the partition's affinity property. Those will
almost certainly be out-of-sync in some DTs, and it'll be very painful
to deal with.

I think that for partitioned PPIs the PMU driver should use the affinity
from the PPI, and not have an interrupt-affinity property. Hopefully
that's relatively simple to handle.

Thanks,
Mark.

> +
> +	pmu_a72 {
> +		compatible = "arm,cortex-a72-pmu";
> +		interrupts = <GIC_PPI 7 IRQ_TYPE_LEVEL_LOW &part1>;
> +		interrupt-affinity = <&cpu_b0>,
> +				     <&cpu_b1>;
> +	};
> +
>  	xin24m: xin24m {
>  		compatible = "fixed-clock";
>  		clock-frequency = <24000000>;
> @@ -296,6 +312,16 @@
>  			msi-controller;
>  			reg = <0x0 0xfee20000 0x0 0x20000>;
>  		};
> +
> +		ppi-partitions {
> +			part0: interrupt-partition-0 {
> +				affinity = <&cpu_l0 &cpu_l1 &cpu_l2 &cpu_l3>;
> +			};
> +
> +			part1: interrupt-partition-1 {
> +				affinity = <&cpu_b0 &cpu_b1>;
> +			};
> +		};
>  	};
>  
>  	i2c1: i2c at ff110000 {
> -- 
> 1.9.1
> 


From marc.zyngier at arm.com  Fri Jul  1 03:32:51 2016
From: marc.zyngier at arm.com (Marc Zyngier)
Date: Fri, 1 Jul 2016 11:32:51 +0100
Subject: [PATCH v4 2/2] PCI: Rockchip: Add Rockchip PCIe controller support
In-Reply-To: <1467336290-11282-2-git-send-email-shawn.lin@rock-chips.com>
References: <1467336290-11282-1-git-send-email-shawn.lin@rock-chips.com>
 <1467336290-11282-2-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <577646D3.7040207@arm.com>

On 01/07/16 02:24, Shawn Lin wrote:
> This patch adds Rockchip PCIe controller support found
> on RK3399 Soc platform.
> 
> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
> 
> ---

[...]

> +static void rockchip_pcie_legacy_int_handler(struct irq_desc *desc)
> +{
> +	struct irq_chip *chip = irq_desc_get_chip(desc);
> +	struct rockchip_pcie_port *port;
> +	u32 reg;
> +
> +	chained_irq_enter(chip, desc);
> +	port = irq_desc_get_handler_data(desc);
> +
> +	reg = pcie_read(port, PCIE_CLIENT_INT_STATUS);
> +	reg = (reg & ROCKCHIP_PCIE_RPIFR1_INTR_MASK) >>
> +	       ROCKCHIP_PCIE_RPIFR1_INTR_SHIFT;
> +	if (reg)
> +		generic_handle_irq(irq_find_mapping(port->irq_domain,
> +						    ffs(reg)));

Can't you get multiple pending interrupts at the same time? Here, you
seem to only process one interrupt at a time, which is going to be
pretty inefficient.

How about something like:

	while (reg) {
		int hwirq = ffs(reg);
		int irq;
		
		reg &= ~BIT(hwirq);
		irq = irq_find_mapping(port->irq_domain, hwirq);
		if (irq)
			generic_handle_irq(irq);
	}

> +	chained_irq_exit(chip, desc);
> +}
> +

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...


From marc.zyngier at arm.com  Fri Jul  1 05:50:36 2016
From: marc.zyngier at arm.com (Marc Zyngier)
Date: Fri, 1 Jul 2016 13:50:36 +0100
Subject: [PATCH 2/2] arm64: dts: rockchip: support the pmu node for rk3399
In-Reply-To: <20160701102612.GB22953@leverpostej>
References: <1467358570-24417-1-git-send-email-wxt@rock-chips.com>
 <1467358570-24417-3-git-send-email-wxt@rock-chips.com>
 <20160701102612.GB22953@leverpostej>
Message-ID: <5776671C.7000808@arm.com>

On 01/07/16 11:26, Mark Rutland wrote:
> On Fri, Jul 01, 2016 at 03:36:10PM +0800, Caesar Wang wrote:
>> As the Marc posted the patches [0] to support Partitioning per-cpu
>> interrupts. Let's add this patch to match it on rk3399 SoCs.
>>
>> [0]:
>> https://lkml.org/lkml/2016/4/11/182
> 
> The core IRQ support is merged, but it's worth noting that the perf code
> doesn't use it yet. So we still need a patch adding support to the perf
> code before we can change the dts.
> 
> I think Marc had a prototype of that somewhere.
> 
> Marc?

I posted something in the original discussion, but need to respin it.
Stay tuned.

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...


From marc.zyngier at arm.com  Fri Jul  1 06:01:09 2016
From: marc.zyngier at arm.com (Marc Zyngier)
Date: Fri, 1 Jul 2016 14:01:09 +0100
Subject: [PATCH v4 1/2] Documentation: bindings: add dt doc for Rockchip
 PCIe controller
In-Reply-To: <1467336290-11282-1-git-send-email-shawn.lin@rock-chips.com>
References: <1467336290-11282-1-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <57766995.9070302@arm.com>

On 01/07/16 02:24, Shawn Lin wrote:
> This patch adds a binding that describes the Rockchip PCIe controller
> found on Rockchip SoCs PCIe interface.
> 
> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
> 
> ---
> 
> Changes in v4:
> - fix example of adding intermediate interrupt controller for pcie
>   legacy interrrupt
> 
> Changes in v3:
> - fix example dts code suggested by Rob and Marc
> - remove driver's behaviour of regulator
> 
> Changes in v2:
> - fix lots clk/reset stuff suggested by Heiko
> - remove msi-parent and add msi-map suggested by Marc
> - drop phy related stuff
> - some others minor fixes
> 
>  .../devicetree/bindings/pci/rockchip-pcie.txt      | 91 ++++++++++++++++++++++
>  1 file changed, 91 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/pci/rockchip-pcie.txt
> 
> diff --git a/Documentation/devicetree/bindings/pci/rockchip-pcie.txt b/Documentation/devicetree/bindings/pci/rockchip-pcie.txt
> new file mode 100644
> index 0000000..8092fc5
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pci/rockchip-pcie.txt
> @@ -0,0 +1,91 @@
> +* Rockchip AXI PCIe Root Port Bridge DT description
> +
> +Required properties:
> +- #address-cells: Address representation for root ports, set to <3>
> +- #size-cells: Size representation for root ports, set to <2>
> +- #interrupt-cells: specifies the number of cells needed to encode an
> +		interrupt source. The value must be 1.
> +- compatible: Should contain "rockchip,rk3399-pcie"
> +- reg: Two register ranges as listed in the reg-names property
> +- reg-names: Must include the following names
> +	- "axi-base"
> +	- "apb-base"
> +- clocks: Must contain an entry for each entry in clock-names.
> +		See ../clocks/clock-bindings.txt for details.
> +- clock-names: Must include the following entries:
> +	- "aclk"
> +	- "aclk-perf"
> +	- "hclk"
> +	- "pm"
> +- msi-map: Maps a Requester ID to an MSI controller and associated.
> +		See ./pci-msi.txt
> +- phys: From PHY bindings: Phandle for the Generic PHY for PCIe.
> +- phy-names:  MUST be "pcie-phy".
> +- interrupts: Three interrupt entries must be specified.
> +- interrupt-names: Must include the following names
> +	- "sys"
> +	- "legacy"
> +	- "client"
> +- resets: Must contain five entries for each entry in reset-names.
> +	   See ../reset/reset.txt for details.
> +- reset-names: Must include the following names
> +	- "core"
> +	- "mgmt"
> +	- "mgmt-sticky"
> +	- "pipe"
> +- pinctrl-names : The pin control state names
> +- pinctrl-0: The "default" pinctrl state
> +- interrupt-map-mask and interrupt-map: standard PCI properties
> +- interrupt-controller: identifies the node as an interrupt controller
> +
> +Optional Property:
> +- ep-gpios: contain the entry for pre-reset gpio
> +- num-lanes: number of lanes to use
> +- vpcie3v3-supply: The phandle to the 3.3v regulator to use for pcie.
> +- vpcie1v8-supply: The phandle to the 1.8v regulator to use for pcie.
> +- vpcie0v9-supply: The phandle to the 0.9v regulator to use for pcie.
> +
> +Example:
> +
> +pcie0: pcie at f8000000 {
> +	compatible = "rockchip,rk3399-pcie";
> +	#address-cells = <3>;
> +	#size-cells = <2>;
> +	clocks = <&cru ACLK_PCIE>, <&cru ACLK_PERF_PCIE>,
> +		 <&cru PCLK_PCIE>, <&cru SCLK_PCIE_PM>;
> +	clock-names = "aclk", "aclk-perf",
> +		      "hclk", "pm";
> +	bus-range = <0x0 0x1>;
> +	interrupts = <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 50 IRQ_TYPE_LEVEL_HIGH>,
> +		     <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>;
> +	interrupt-names = "sys", "legacy", "client";
> +	assigned-clocks = <&cru SCLK_PCIEPHY_REF>;
> +	assigned-clock-parents = <&cru SCLK_PCIEPHY_REF100M>;
> +	assigned-clock-rates = <100000000>;
> +	ep-gpios = <&gpio3 13 GPIO_ACTIVE_HIGH>;
> +	ranges = <0x83000000 0x0 0xfa000000 0x0 0xfa000000 0x0 0x600000
> +		  0x81000000 0x0 0xfa600000 0x0 0xfa600000 0x0 0x100000>;
> +	num-lanes = <4>;
> +	msi-map = <0x0 &its 0x0 0x1000>;
> +	reg = < 0x0 0xf8000000 0x0 0x2000000 >, < 0x0 0xfd000000 0x0 0x1000000 >;
> +	reg-names = "axi-base", "apb-base";
> +	resets = <&cru SRST_PCIE_CORE>, <&cru SRST_PCIE_MGMT>,
> +		 <&cru SRST_PCIE_MGMT_STICKY>, <&cru SRST_PCIE_PIPE>;
> +	reset-names = "core", "mgmt", "mgmt-sticky", "pipe";
> +	phys = <&pcie_phy>;
> +	phy-names = "pcie-phy";
> +	pinctrl-names = "default";
> +	pinctrl-0 = <&pcie_clkreq>;
> +	#interrupt-cells = <1>;
> +	interrupt-controller;

So the pcie node itself is an interrupt controller...

> +	interrupt-map-mask = <0 0 0 7>;
> +	interrupt-map = <0 0 0 1 &pcie0_intc 1>,
> +			<0 0 0 2 &pcie0_intc 2>,
> +			<0 0 0 3 &pcie0_intc 3>,
> +			<0 0 0 4 &pcie0_intc 4>;
> +	pcie0_intc: interrupt-controller {
> +		interrupt-controller;
> +		#address-cells = <0>;
> +		#interrupt-cells = <1>;
> +	};

But there's also another here. I just don't understand how it works.

> +};
> 

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...


From marc.zyngier at arm.com  Fri Jul  1 06:21:31 2016
From: marc.zyngier at arm.com (Marc Zyngier)
Date: Fri,  1 Jul 2016 14:21:31 +0100
Subject: [PATCH] drivers/perf: arm-pmu: Handle per-interrupt affinity mask
Message-ID: <1467379291-18413-1-git-send-email-marc.zyngier@arm.com>

On a big-little system, PMUs can be wired to CPUs using per CPU
interrups (PPI). In this case, it is important to make sure that
the enable/disable do happen on the right set of CPUs.

So instead of relying on the interrupt-affinity property, we can
use the actual percpu affinity that DT exposes as part of the
interrupt specifier. The DT binding is also updated to reflect
the fact that the interrupt-affinity property shouldn't be used
in that case.

Signed-off-by: Marc Zyngier <marc.zyngier at arm.com>
---
 Documentation/devicetree/bindings/arm/pmu.txt |  4 +++-
 drivers/perf/arm_pmu.c                        | 22 +++++++++++++++++-----
 2 files changed, 20 insertions(+), 6 deletions(-)

diff --git a/Documentation/devicetree/bindings/arm/pmu.txt b/Documentation/devicetree/bindings/arm/pmu.txt
index 74d5417..61c8b46 100644
--- a/Documentation/devicetree/bindings/arm/pmu.txt
+++ b/Documentation/devicetree/bindings/arm/pmu.txt
@@ -39,7 +39,9 @@ Optional properties:
                        When using a PPI, specifies a list of phandles to CPU
 		       nodes corresponding to the set of CPUs which have
 		       a PMU of this type signalling the PPI listed in the
-		       interrupts property.
+		       interrupts property, unless this is already specified
+		       by the PPI interrupt specifier itself (in which case
+		       the interrupt-affinity property shouldn't be present).
 
                        This property should be present when there is more than
 		       a single SPI.
diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
index 140436a..065ccec 100644
--- a/drivers/perf/arm_pmu.c
+++ b/drivers/perf/arm_pmu.c
@@ -603,7 +603,8 @@ static void cpu_pmu_free_irq(struct arm_pmu *cpu_pmu)
 
 	irq = platform_get_irq(pmu_device, 0);
 	if (irq >= 0 && irq_is_percpu(irq)) {
-		on_each_cpu(cpu_pmu_disable_percpu_irq, &irq, 1);
+		on_each_cpu_mask(&cpu_pmu->supported_cpus,
+				 cpu_pmu_disable_percpu_irq, &irq, 1);
 		free_percpu_irq(irq, &hw_events->percpu_pmu);
 	} else {
 		for (i = 0; i < irqs; ++i) {
@@ -645,7 +646,9 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler)
 				irq);
 			return err;
 		}
-		on_each_cpu(cpu_pmu_enable_percpu_irq, &irq, 1);
+
+		on_each_cpu_mask(&cpu_pmu->supported_cpus,
+				 cpu_pmu_enable_percpu_irq, &irq, 1);
 	} else {
 		for (i = 0; i < irqs; ++i) {
 			int cpu = i;
@@ -961,9 +964,18 @@ static int of_pmu_irq_cfg(struct arm_pmu *pmu)
 		i++;
 	} while (1);
 
-	/* If we didn't manage to parse anything, claim to support all CPUs */
-	if (cpumask_weight(&pmu->supported_cpus) == 0)
-		cpumask_setall(&pmu->supported_cpus);
+	/* If we didn't manage to parse anything, try the interrupt affinity */
+	if (cpumask_weight(&pmu->supported_cpus) == 0) {
+		if (!using_spi) {
+			/* If using PPIs, check the affinity of the partition */
+			int irq = platform_get_irq(pdev, 0);
+			irq_get_percpu_devid_partition(irq,
+						       &pmu->supported_cpus);
+		} else {
+			/* Otherwise default to all CPUs */
+			cpumask_setall(&pmu->supported_cpus);
+		}
+	}
 
 	/* If we matched up the IRQ affinities, use them to route the SPIs */
 	if (using_spi && i == pdev->num_resources)
-- 
2.1.4



From wxt at rock-chips.com  Fri Jul  1 08:00:39 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Fri, 01 Jul 2016 23:00:39 +0800
Subject: [PATCH] drivers/perf: arm-pmu: Handle per-interrupt affinity mask
In-Reply-To: <1467379291-18413-1-git-send-email-marc.zyngier@arm.com>
References: <1467379291-18413-1-git-send-email-marc.zyngier@arm.com>
Message-ID: <57768597.2070606@rock-chips.com>

Hi Marc,

On 2016?07?01? 21:21, Marc Zyngier wrote:
> On a big-little system, PMUs can be wired to CPUs using per CPU
> interrups (PPI). In this case, it is important to make sure that
> the enable/disable do happen on the right set of CPUs.
>
> So instead of relying on the interrupt-affinity property, we can
> use the actual percpu affinity that DT exposes as part of the
> interrupt specifier. The DT binding is also updated to reflect
> the fact that the interrupt-affinity property shouldn't be used
> in that case.
>
> Signed-off-by: Marc Zyngier <marc.zyngier at arm.com>

Tested-by: Caesar Wang <wxt at rock-chips.com>

I pick this up on my local branch.
8bc671a FROMLIST: drivers/perf: arm-pmu: Handle per-interrupt affinity mask
3d723f4 FROMLIST: arm64: dts: rockchip: support the pmu node for rk3399
1359b92 FIXUP: FROMLIST: arm64: dts: rockchip: change all interrupts 
cells for 4 on rk3399 SoCs
...

Tested on rk3399 board.
localhost / # perf list

List of pre-defined events (to be used in -e):
cpu-cycles OR cycles [Hardware event]
instructions [Hardware event]
cache-references [Hardware event]
cache-misses [Hardware event]
branch-instructions OR branches [Hardware event]
branch-misses [Hardware event]
bus-cycles [Hardware event]
...

perf stat --cpu 0/1/2/3..... to minitor
e.g. cpu0;

localhost / # perf stat --cpu 0
^C
Performance counter stats for 'CPU(s) 0':

3374.917571 task-clock (msec) # 1.001 CPUs utilized [100.00%]
20 context-switches # 0.006 K/sec [100.00%]
2 cpu-migrations # 0.001 K/sec [100.00%]
55 page-faults # 0.016 K/sec
7151843 cycles # 0.002 GHz [100.00%]
<not supported> stalled-cycles-frontend
<not supported> stalled-cycles-backend
4272536 instructions # 0.60 insns per cycle [100.00%]
568406 branches # 0.168 M/sec [100.00%]
65652 branch-misses # 11.55% of all branches

Also, 'perf top' to monitor the PMU interrupts from cpus
---



> ---
>   Documentation/devicetree/bindings/arm/pmu.txt |  4 +++-
>   drivers/perf/arm_pmu.c                        | 22 +++++++++++++++++-----
>   2 files changed, 20 insertions(+), 6 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/arm/pmu.txt b/Documentation/devicetree/bindings/arm/pmu.txt
> index 74d5417..61c8b46 100644
> --- a/Documentation/devicetree/bindings/arm/pmu.txt
> +++ b/Documentation/devicetree/bindings/arm/pmu.txt
> @@ -39,7 +39,9 @@ Optional properties:
>                          When using a PPI, specifies a list of phandles to CPU
>   		       nodes corresponding to the set of CPUs which have
>   		       a PMU of this type signalling the PPI listed in the
> -		       interrupts property.
> +		       interrupts property, unless this is already specified
> +		       by the PPI interrupt specifier itself (in which case
> +		       the interrupt-affinity property shouldn't be present).
>   
>                          This property should be present when there is more than
>   		       a single SPI.
> diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
> index 140436a..065ccec 100644
> --- a/drivers/perf/arm_pmu.c
> +++ b/drivers/perf/arm_pmu.c
> @@ -603,7 +603,8 @@ static void cpu_pmu_free_irq(struct arm_pmu *cpu_pmu)
>   
>   	irq = platform_get_irq(pmu_device, 0);
>   	if (irq >= 0 && irq_is_percpu(irq)) {
> -		on_each_cpu(cpu_pmu_disable_percpu_irq, &irq, 1);
> +		on_each_cpu_mask(&cpu_pmu->supported_cpus,
> +				 cpu_pmu_disable_percpu_irq, &irq, 1);
>   		free_percpu_irq(irq, &hw_events->percpu_pmu);
>   	} else {
>   		for (i = 0; i < irqs; ++i) {
> @@ -645,7 +646,9 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler)
>   				irq);
>   			return err;
>   		}
> -		on_each_cpu(cpu_pmu_enable_percpu_irq, &irq, 1);
> +
> +		on_each_cpu_mask(&cpu_pmu->supported_cpus,
> +				 cpu_pmu_enable_percpu_irq, &irq, 1);
>   	} else {
>   		for (i = 0; i < irqs; ++i) {
>   			int cpu = i;
> @@ -961,9 +964,18 @@ static int of_pmu_irq_cfg(struct arm_pmu *pmu)
>   		i++;
>   	} while (1);
>   
> -	/* If we didn't manage to parse anything, claim to support all CPUs */
> -	if (cpumask_weight(&pmu->supported_cpus) == 0)
> -		cpumask_setall(&pmu->supported_cpus);
> +	/* If we didn't manage to parse anything, try the interrupt affinity */
> +	if (cpumask_weight(&pmu->supported_cpus) == 0) {
> +		if (!using_spi) {
> +			/* If using PPIs, check the affinity of the partition */
> +			int irq = platform_get_irq(pdev, 0);
> +			irq_get_percpu_devid_partition(irq,
> +						       &pmu->supported_cpus);
> +		} else {
> +			/* Otherwise default to all CPUs */
> +			cpumask_setall(&pmu->supported_cpus);
> +		}
> +	}
>   
>   	/* If we matched up the IRQ affinities, use them to route the SPIs */
>   	if (using_spi && i == pdev->num_resources)

-- 
caesar wang | software engineer | wxt at rock-chip.com




From seanpaul at chromium.org  Fri Jul  1 08:30:37 2016
From: seanpaul at chromium.org (Sean Paul)
Date: Fri, 1 Jul 2016 11:30:37 -0400
Subject: [PATCH v3 1/4] drm/rockchip: vop: export line flag function
In-Reply-To: <1467364745-21621-1-git-send-email-ykk@rock-chips.com>
References: <1467364685-21390-1-git-send-email-ykk@rock-chips.com>
 <1467364745-21621-1-git-send-email-ykk@rock-chips.com>
Message-ID: <CAOw6vb+vS3O+Db1_1RSGup7Y2tNjRkxyz7ah817R6nXceSHeFQ@mail.gmail.com>

On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk at rock-chips.com> wrote:
> VOP have integrated a hardware counter which indicate the exact display
> line that vop is scanning. And if we're interested in a specific line,
> we can set the line number to vop line_flag register, and then vop would
> generate a line_flag interrupt for it.
>
> For example eDP PSR function is interested in the vertical blanking
> period, then driver could set the line number to zero.
>
> This patch have exported a symbol that allow other driver to listen the
> line flag event with given timeout limit:
> -  rockchip_drm_wait_line_flag()
>
> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
> ---
> Changes in v3:
> - Export the 'rockchip_drm_wait_line_flag' symbol, and document it.
> - Add 'line_flag_num_0' for RK3288/RK3036
> - Remove the notify for waiting line_flag event (Daniel)
>
> Changes in v2:
> - Introduce in v2, split VOP line flag changes out
>
>  drivers/gpu/drm/rockchip/rockchip_drm_drv.h |   3 +
>  drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 103 ++++++++++++++++++++++++++++
>  drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   3 +
>  drivers/gpu/drm/rockchip/rockchip_vop_reg.c |   4 ++
>  4 files changed, 113 insertions(+)
>
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> index ea39329..239b830 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> @@ -70,4 +70,7 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
>                                    struct device *dev);
>  void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
>                                     struct device *dev);
> +int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
> +                               unsigned int mstimeout);
> +
>  #endif /* _ROCKCHIP_DRM_DRV_H_ */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> index c8a62a8..cd3cac5 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> @@ -121,6 +121,8 @@ struct vop {
>         /* protected by dev->event_lock */
>         struct drm_pending_vblank_event *event;
>
> +       struct completion line_flag_completion;
> +
>         const struct vop_data *data;
>
>         uint32_t *regsbak;
> @@ -431,6 +433,59 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop)
>         spin_unlock_irqrestore(&vop->irq_lock, flags);
>  }
>
> +/*
> + * (1) each frame starts at the start of the Vsync pulse which is signaled by
> + *     the "FRAME_SYNC" interrupt.
> + * (2) the active data region of each frame ends at dsp_vact_end
> + * (3) we should program this same number (dsp_vact_end) into dsp_line_frag_num,
> + *      to get "LINE_FLAG" interrupt at the end of the active on screen data.
> + *
> + * VOP_INTR_CTRL0.dsp_line_frag_num = VOP_DSP_VACT_ST_END.dsp_vact_end
> + * Interrupts
> + * LINE_FLAG -------------------------------+
> + * FRAME_SYNC ----+                         |
> + *                |                         |
> + *                v                         v
> + *                | Vsync | Vbp |  Vactive  | Vfp |
> + *                        ^     ^           ^     ^
> + *                        |     |           |     |
> + *                        |     |           |     |
> + * dsp_vs_end ------------+     |           |     |   VOP_DSP_VTOTAL_VS_END
> + * dsp_vact_start --------------+           |     |   VOP_DSP_VACT_ST_END
> + * dsp_vact_end ----------------------------+     |   VOP_DSP_VACT_ST_END
> + * dsp_total -------------------------------------+   VOP_DSP_VTOTAL_VS_END
> + */
> +
> +static void vop_line_flag_irq_enable(struct vop *vop, int line_num)
> +{
> +       unsigned long flags;
> +
> +       if (WARN_ON(!vop->is_enabled))
> +               return;
> +
> +       spin_lock_irqsave(&vop->irq_lock, flags);
> +
> +       VOP_CTRL_SET(vop, line_flag_num_0, line_num);
> +       VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1);
> +       vop_cfg_done(vop);
> +
> +       spin_unlock_irqrestore(&vop->irq_lock, flags);
> +}
> +
> +static void vop_line_flag_irq_disable(struct vop *vop)
> +{
> +       unsigned long flags;
> +
> +       if (WARN_ON(!vop->is_enabled))
> +               return;
> +
> +       spin_lock_irqsave(&vop->irq_lock, flags);
> +
> +       VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 0);
> +
> +       spin_unlock_irqrestore(&vop->irq_lock, flags);
> +}
> +
>  static void vop_enable(struct drm_crtc *crtc)
>  {
>         struct vop *vop = to_vop(crtc);
> @@ -1157,6 +1212,13 @@ static irqreturn_t vop_isr(int irq, void *data)
>                 ret = IRQ_HANDLED;
>         }
>
> +       if (active_irqs & LINE_FLAG_INTR) {
> +               if (!completion_done(&vop->line_flag_completion))
> +                       complete(&vop->line_flag_completion);

I think there's potential to miss flags here if the timing is just
right because the completion_done and complete are not atomic wrt the
wait_line_flag function. I think you need some locking around the
completion operations to ensure they're properly sequenced.

FWIW, if you're reinitializing the completion every time in
drm_wait_line_flag, you should be able to just call complete (or
complete_all) without checking if there are any waiters.


> +               active_irqs &= ~LINE_FLAG_INTR;
> +               ret = IRQ_HANDLED;
> +       }
> +
>         if (active_irqs & FS_INTR) {
>                 drm_crtc_handle_vblank(crtc);
>                 vop_handle_vblank(vop);
> @@ -1255,6 +1317,7 @@ static int vop_create_crtc(struct vop *vop)
>
>         init_completion(&vop->dsp_hold_completion);
>         init_completion(&vop->wait_update_complete);
> +       init_completion(&vop->line_flag_completion);
>         crtc->port = port;
>         rockchip_register_crtc_funcs(crtc, &private_crtc_funcs);
>
> @@ -1411,6 +1474,46 @@ static void vop_win_init(struct vop *vop)
>         }
>  }
>
> +/**
> + * rockchip_drm_wait_line_flag - acqiure the give line flag event
> + * @crtc: CRTC to enable line flag
> + * @line_num: interested line number
> + * @mstimeout: millisecond for timeout
> + *
> + * Driver would hold here until the interested line flag interrupt have
> + * happened or timeout to wait.
> + *
> + * Returns:
> + * Zero on success, negative errno on failure.
> + */
> +int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
> +                               unsigned int mstimeout)
> +{
> +       struct vop *vop = to_vop(crtc);
> +       unsigned long jiffies_left;
> +
> +       if (!crtc || !vop->is_enabled)
> +               return -ENODEV;
> +
> +       if (line_num > crtc->mode.vtotal || mstimeout <= 0)
> +               return -EINVAL;
> +
> +       reinit_completion(&vop->line_flag_completion);
> +       vop_line_flag_irq_enable(vop, line_num);
> +

This will only work for one waiter per vop. Is it worth enforcing this
explicitly to avoid weird behavior when there are more than one?

> +       jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion,
> +                                                  msecs_to_jiffies(mstimeout));
> +       vop_line_flag_irq_disable(vop);
> +
> +       if (jiffies_left == 0) {
> +               dev_err(vop->dev, "Timeout waiting for IRQ\n");
> +               return -ETIMEDOUT;
> +       }
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL(rockchip_drm_wait_line_flag);
> +
>  static int vop_bind(struct device *dev, struct device *master, void *data)
>  {
>         struct platform_device *pdev = to_platform_device(dev);
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> index ff4f52e..34fcd03 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> @@ -61,6 +61,9 @@ struct vop_ctrl {
>         struct vop_reg hpost_st_end;
>         struct vop_reg vpost_st_end;
>
> +       struct vop_reg line_flag_num_0;
> +       struct vop_reg line_flag_num_1;
> +

nit: you could make this an array:

struct vop_reg line_flag_num[2];


>         struct vop_reg cfg_done;
>  };
>
> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> index 6f42e56..e9211c9 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> @@ -122,6 +122,7 @@ static const struct vop_ctrl rk3036_ctrl_data = {
>         .hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0),
>         .vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
>         .vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0),
> +       .line_flag_num_0 = VOP_REG(RK3036_INT_STATUS, 0xfff, 12),
>         .cfg_done = VOP_REG(RK3036_REG_CFG_DONE, 0x1, 0),
>  };
>
> @@ -221,6 +222,7 @@ static const struct vop_ctrl rk3288_ctrl_data = {
>         .vact_st_end = VOP_REG(RK3288_DSP_VACT_ST_END, 0x1fff1fff, 0),
>         .hpost_st_end = VOP_REG(RK3288_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
>         .vpost_st_end = VOP_REG(RK3288_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
> +       .line_flag_num_0 = VOP_REG(RK3288_INTR_CTRL0, 0x1fff, 12),
>         .cfg_done = VOP_REG(RK3288_REG_CFG_DONE, 0x1, 0),
>  };
>
> @@ -299,6 +301,8 @@ static const struct vop_ctrl rk3399_ctrl_data = {
>         .vact_st_end = VOP_REG(RK3399_DSP_VACT_ST_END, 0x1fff1fff, 0),
>         .hpost_st_end = VOP_REG(RK3399_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
>         .vpost_st_end = VOP_REG(RK3399_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
> +       .line_flag_num_0 = VOP_REG(RK3399_LINE_FLAG, 0xffff, 0),
> +       .line_flag_num_1 = VOP_REG(RK3399_LINE_FLAG, 0xffff, 16),
>         .cfg_done = VOP_REG_MASK(RK3399_REG_CFG_DONE, 0x1, 0),
>  };
>
> --
> 1.9.1
>
>


From seanpaul at chromium.org  Fri Jul  1 08:32:20 2016
From: seanpaul at chromium.org (Sean Paul)
Date: Fri, 1 Jul 2016 11:32:20 -0400
Subject: [PATCH v3 1/4] drm/rockchip: vop: export line flag function
In-Reply-To: <CAOw6vb+vS3O+Db1_1RSGup7Y2tNjRkxyz7ah817R6nXceSHeFQ@mail.gmail.com>
References: <1467364685-21390-1-git-send-email-ykk@rock-chips.com>
 <1467364745-21621-1-git-send-email-ykk@rock-chips.com>
 <CAOw6vb+vS3O+Db1_1RSGup7Y2tNjRkxyz7ah817R6nXceSHeFQ@mail.gmail.com>
Message-ID: <CAOw6vbKuy5wCb_J0ENP5OGf7B31ut-FDzBzE5NLiFzEY5sWqsg@mail.gmail.com>

On Fri, Jul 1, 2016 at 11:30 AM, Sean Paul <seanpaul at chromium.org> wrote:
> On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk at rock-chips.com> wrote:
>> VOP have integrated a hardware counter which indicate the exact display
>> line that vop is scanning. And if we're interested in a specific line,
>> we can set the line number to vop line_flag register, and then vop would
>> generate a line_flag interrupt for it.
>>
>> For example eDP PSR function is interested in the vertical blanking
>> period, then driver could set the line number to zero.
>>
>> This patch have exported a symbol that allow other driver to listen the
>> line flag event with given timeout limit:
>> -  rockchip_drm_wait_line_flag()
>>
>> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
>> ---
>> Changes in v3:
>> - Export the 'rockchip_drm_wait_line_flag' symbol, and document it.
>> - Add 'line_flag_num_0' for RK3288/RK3036
>> - Remove the notify for waiting line_flag event (Daniel)
>>
>> Changes in v2:
>> - Introduce in v2, split VOP line flag changes out
>>
>>  drivers/gpu/drm/rockchip/rockchip_drm_drv.h |   3 +
>>  drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 103 ++++++++++++++++++++++++++++
>>  drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   3 +
>>  drivers/gpu/drm/rockchip/rockchip_vop_reg.c |   4 ++
>>  4 files changed, 113 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>> index ea39329..239b830 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>> @@ -70,4 +70,7 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
>>                                    struct device *dev);
>>  void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
>>                                     struct device *dev);
>> +int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
>> +                               unsigned int mstimeout);
>> +
>>  #endif /* _ROCKCHIP_DRM_DRV_H_ */
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> index c8a62a8..cd3cac5 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> @@ -121,6 +121,8 @@ struct vop {
>>         /* protected by dev->event_lock */
>>         struct drm_pending_vblank_event *event;
>>
>> +       struct completion line_flag_completion;
>> +
>>         const struct vop_data *data;
>>
>>         uint32_t *regsbak;
>> @@ -431,6 +433,59 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop)
>>         spin_unlock_irqrestore(&vop->irq_lock, flags);
>>  }
>>
>> +/*
>> + * (1) each frame starts at the start of the Vsync pulse which is signaled by
>> + *     the "FRAME_SYNC" interrupt.
>> + * (2) the active data region of each frame ends at dsp_vact_end
>> + * (3) we should program this same number (dsp_vact_end) into dsp_line_frag_num,
>> + *      to get "LINE_FLAG" interrupt at the end of the active on screen data.
>> + *
>> + * VOP_INTR_CTRL0.dsp_line_frag_num = VOP_DSP_VACT_ST_END.dsp_vact_end
>> + * Interrupts
>> + * LINE_FLAG -------------------------------+
>> + * FRAME_SYNC ----+                         |
>> + *                |                         |
>> + *                v                         v
>> + *                | Vsync | Vbp |  Vactive  | Vfp |
>> + *                        ^     ^           ^     ^
>> + *                        |     |           |     |
>> + *                        |     |           |     |
>> + * dsp_vs_end ------------+     |           |     |   VOP_DSP_VTOTAL_VS_END
>> + * dsp_vact_start --------------+           |     |   VOP_DSP_VACT_ST_END
>> + * dsp_vact_end ----------------------------+     |   VOP_DSP_VACT_ST_END
>> + * dsp_total -------------------------------------+   VOP_DSP_VTOTAL_VS_END
>> + */
>> +
>> +static void vop_line_flag_irq_enable(struct vop *vop, int line_num)
>> +{
>> +       unsigned long flags;
>> +
>> +       if (WARN_ON(!vop->is_enabled))
>> +               return;
>> +
>> +       spin_lock_irqsave(&vop->irq_lock, flags);
>> +
>> +       VOP_CTRL_SET(vop, line_flag_num_0, line_num);
>> +       VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1);
>> +       vop_cfg_done(vop);
>> +
>> +       spin_unlock_irqrestore(&vop->irq_lock, flags);
>> +}
>> +
>> +static void vop_line_flag_irq_disable(struct vop *vop)
>> +{
>> +       unsigned long flags;
>> +
>> +       if (WARN_ON(!vop->is_enabled))
>> +               return;
>> +
>> +       spin_lock_irqsave(&vop->irq_lock, flags);
>> +
>> +       VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 0);
>> +
>> +       spin_unlock_irqrestore(&vop->irq_lock, flags);
>> +}
>> +
>>  static void vop_enable(struct drm_crtc *crtc)
>>  {
>>         struct vop *vop = to_vop(crtc);
>> @@ -1157,6 +1212,13 @@ static irqreturn_t vop_isr(int irq, void *data)
>>                 ret = IRQ_HANDLED;
>>         }
>>
>> +       if (active_irqs & LINE_FLAG_INTR) {
>> +               if (!completion_done(&vop->line_flag_completion))
>> +                       complete(&vop->line_flag_completion);
>
> I think there's potential to miss flags here if the timing is just
> right because the completion_done and complete are not atomic wrt the
> wait_line_flag function. I think you need some locking around the
> completion operations to ensure they're properly sequenced.
>
> FWIW, if you're reinitializing the completion every time in
> drm_wait_line_flag, you should be able to just call complete (or
> complete_all) without checking if there are any waiters.
>

I should add, if you just call complete/complete_all() here, you
shouldn't need to add locking (assuming only one waiter).

>
>> +               active_irqs &= ~LINE_FLAG_INTR;
>> +               ret = IRQ_HANDLED;
>> +       }
>> +
>>         if (active_irqs & FS_INTR) {
>>                 drm_crtc_handle_vblank(crtc);
>>                 vop_handle_vblank(vop);
>> @@ -1255,6 +1317,7 @@ static int vop_create_crtc(struct vop *vop)
>>
>>         init_completion(&vop->dsp_hold_completion);
>>         init_completion(&vop->wait_update_complete);
>> +       init_completion(&vop->line_flag_completion);
>>         crtc->port = port;
>>         rockchip_register_crtc_funcs(crtc, &private_crtc_funcs);
>>
>> @@ -1411,6 +1474,46 @@ static void vop_win_init(struct vop *vop)
>>         }
>>  }
>>
>> +/**
>> + * rockchip_drm_wait_line_flag - acqiure the give line flag event
>> + * @crtc: CRTC to enable line flag
>> + * @line_num: interested line number
>> + * @mstimeout: millisecond for timeout
>> + *
>> + * Driver would hold here until the interested line flag interrupt have
>> + * happened or timeout to wait.
>> + *
>> + * Returns:
>> + * Zero on success, negative errno on failure.
>> + */
>> +int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
>> +                               unsigned int mstimeout)
>> +{
>> +       struct vop *vop = to_vop(crtc);
>> +       unsigned long jiffies_left;
>> +
>> +       if (!crtc || !vop->is_enabled)
>> +               return -ENODEV;
>> +
>> +       if (line_num > crtc->mode.vtotal || mstimeout <= 0)
>> +               return -EINVAL;
>> +
>> +       reinit_completion(&vop->line_flag_completion);
>> +       vop_line_flag_irq_enable(vop, line_num);
>> +
>
> This will only work for one waiter per vop. Is it worth enforcing this
> explicitly to avoid weird behavior when there are more than one?
>
>> +       jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion,
>> +                                                  msecs_to_jiffies(mstimeout));
>> +       vop_line_flag_irq_disable(vop);
>> +
>> +       if (jiffies_left == 0) {
>> +               dev_err(vop->dev, "Timeout waiting for IRQ\n");
>> +               return -ETIMEDOUT;
>> +       }
>> +
>> +       return 0;
>> +}
>> +EXPORT_SYMBOL(rockchip_drm_wait_line_flag);
>> +
>>  static int vop_bind(struct device *dev, struct device *master, void *data)
>>  {
>>         struct platform_device *pdev = to_platform_device(dev);
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>> index ff4f52e..34fcd03 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>> @@ -61,6 +61,9 @@ struct vop_ctrl {
>>         struct vop_reg hpost_st_end;
>>         struct vop_reg vpost_st_end;
>>
>> +       struct vop_reg line_flag_num_0;
>> +       struct vop_reg line_flag_num_1;
>> +
>
> nit: you could make this an array:
>
> struct vop_reg line_flag_num[2];
>
>
>>         struct vop_reg cfg_done;
>>  };
>>
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>> index 6f42e56..e9211c9 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>> +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>> @@ -122,6 +122,7 @@ static const struct vop_ctrl rk3036_ctrl_data = {
>>         .hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0),
>>         .vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
>>         .vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0),
>> +       .line_flag_num_0 = VOP_REG(RK3036_INT_STATUS, 0xfff, 12),
>>         .cfg_done = VOP_REG(RK3036_REG_CFG_DONE, 0x1, 0),
>>  };
>>
>> @@ -221,6 +222,7 @@ static const struct vop_ctrl rk3288_ctrl_data = {
>>         .vact_st_end = VOP_REG(RK3288_DSP_VACT_ST_END, 0x1fff1fff, 0),
>>         .hpost_st_end = VOP_REG(RK3288_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
>>         .vpost_st_end = VOP_REG(RK3288_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
>> +       .line_flag_num_0 = VOP_REG(RK3288_INTR_CTRL0, 0x1fff, 12),
>>         .cfg_done = VOP_REG(RK3288_REG_CFG_DONE, 0x1, 0),
>>  };
>>
>> @@ -299,6 +301,8 @@ static const struct vop_ctrl rk3399_ctrl_data = {
>>         .vact_st_end = VOP_REG(RK3399_DSP_VACT_ST_END, 0x1fff1fff, 0),
>>         .hpost_st_end = VOP_REG(RK3399_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
>>         .vpost_st_end = VOP_REG(RK3399_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
>> +       .line_flag_num_0 = VOP_REG(RK3399_LINE_FLAG, 0xffff, 0),
>> +       .line_flag_num_1 = VOP_REG(RK3399_LINE_FLAG, 0xffff, 16),
>>         .cfg_done = VOP_REG_MASK(RK3399_REG_CFG_DONE, 0x1, 0),
>>  };
>>
>> --
>> 1.9.1
>>
>>


From dianders at chromium.org  Fri Jul  1 09:56:31 2016
From: dianders at chromium.org (Doug Anderson)
Date: Fri, 1 Jul 2016 09:56:31 -0700
Subject: [PATCH v2] arm64: dts: rockchip: add the power domain node for
 rk3399
In-Reply-To: <1467347575-11806-1-git-send-email-wxt@rock-chips.com>
References: <1467347575-11806-1-git-send-email-wxt@rock-chips.com>
Message-ID: <CAD=FV=U0FU5P+seYYmJNZmcV1r-N6_UHtMW5LuVGM-NyPER8BQ@mail.gmail.com>

Caesar

On Thu, Jun 30, 2016 at 9:32 PM, Caesar Wang <wxt at rock-chips.com> wrote:
> From: Elaine Zhang <zhangqing at rock-chips.com>
>
> In order to meet low power requirements, a power management unit (PMU) is
> designed for controlling power resources in RK3399. The RK3399 PMU is
> dedicated for managing the power of the whole chip.
>
> 1. add pd node for RK3399 Soc
> 2. create power domain tree
> 3. add qos node for domain
>
> From the DT/binds and driver can get more detail information:
> The driver:
>         drivers/soc/rockchip/pm_domains.c
> The document:
>         Documentation/devicetree/bindings/soc/rockchip/power_domain.txt
> ---
>
> Changes in v2:
> - As Doug/Heiko commnets on https://patchwork.kernel.org/patch/9206415/.
>   drop the debugfs-dump and Add the comments for alphabetical order.
>
> Note: As the TRM lists many voltage domains and power domains, then
> in actual we just need control some domains for driver.
> Due to some domains (e.g. emmc, usb, core)...We can't turn off it on
> bootup.

I'm curious: why can't you turn off USB power domains if a board
doesn't usb USB?  ...or GMAC on boards that don't use Ethernet?  ...or
eDP on boards that don't use EDP?

Maybe the driver for these things isn't ready to handle power domains
yet so that's why they are left out for now?

It seems like GMAC could at least be added because we don't even have
the GMAC listed in the current device tree so therefore there can't be
any users of it.  When it's added we can make sure that the power
domains are added.

I think in the current upstream dtsi file there is also no xhci node,
so does that mean you could add the USB3 power domain?


> Signed-off-by: Elaine Zhang <zhangqing at rock-chips.com>
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
> Cc: linux-arm-kernel at lists.infradead.org
> Cc: linux-rockchip at lists.infradead.org
> Cc: Heiko Stuebner <heiko at sntech.de>
>
> ---
>  arch/arm64/boot/dts/rockchip/rk3399.dtsi | 179 +++++++++++++++++++++++++++++++
>  1 file changed, 179 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> index a6dd623..103e185 100644
> --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> @@ -45,6 +45,7 @@
>  #include <dt-bindings/interrupt-controller/arm-gic.h>
>  #include <dt-bindings/interrupt-controller/irq.h>
>  #include <dt-bindings/pinctrl/rockchip.h>
> +#include <dt-bindings/power/rk3399-power.h>
>  #include <dt-bindings/thermal/thermal.h>
>
>  / {
> @@ -594,6 +595,184 @@
>                 status = "disabled";
>         };
>
> +       qos_hdcp: qos_hdcp at ffa90000 {
> +               compatible = "syscon";
> +               reg = <0x0 0xffa90000 0x0 0x20>;
> +       };
> +
> +       qos_iep: qos_iep at ffa98000 {
> +               compatible = "syscon";
> +               reg = <0x0 0xffa98000 0x0 0x20>;
> +       };
> +
> +       qos_isp0_m0: qos_isp0_m0 at ffaa0000 {
> +               compatible = "syscon";
> +               reg = <0x0 0xffaa0000 0x0 0x20>;
> +       };
> +
> +       qos_isp0_m1: qos_isp0_m1 at ffaa0080 {
> +               compatible = "syscon";
> +               reg = <0x0 0xffaa0080 0x0 0x20>;
> +       };
> +
> +       qos_isp1_m0: qos_isp1_m0 at ffaa8000 {
> +               compatible = "syscon";
> +               reg = <0x0 0xffaa8000 0x0 0x20>;
> +       };
> +
> +       qos_isp1_m1: qos_isp1_m1 at ffaa8080 {
> +               compatible = "syscon";
> +               reg = <0x0 0xffaa8080 0x0 0x20>;
> +       };
> +
> +       qos_rga_r: qos_rga_r at ffab0000 {
> +               compatible = "syscon";
> +               reg = <0x0 0xffab0000 0x0 0x20>;
> +       };
> +
> +       qos_rga_w: qos_rga_w at ffab0080 {
> +               compatible = "syscon";
> +               reg = <0x0 0xffab0080 0x0 0x20>;
> +       };
> +
> +       qos_video_m0: qos_video_m0 at ffab8000 {
> +               compatible = "syscon";
> +               reg = <0x0 0xffab8000 0x0 0x20>;
> +       };
> +
> +       qos_video_m1_r: qos_video_m1_r at ffac0000 {
> +               compatible = "syscon";
> +               reg = <0x0 0xffac0000 0x0 0x20>;
> +       };
> +
> +       qos_video_m1_w: qos_video_m1_w at ffac0080 {
> +               compatible = "syscon";
> +               reg = <0x0 0xffac0080 0x0 0x20>;
> +       };
> +
> +       qos_vop_big_r: qos_vop_big_r at ffac8000 {
> +               compatible = "syscon";
> +               reg = <0x0 0xffac8000 0x0 0x20>;
> +       };
> +
> +       qos_vop_big_w: qos_vop_big_w at ffac8080 {
> +               compatible = "syscon";
> +               reg = <0x0 0xffac8080 0x0 0x20>;
> +       };
> +
> +       qos_vop_little: qos_vop_little at ffad0000 {
> +               compatible = "syscon";
> +               reg = <0x0 0xffad0000 0x0 0x20>;
> +       };
> +
> +       qos_gpu: qos_gpu at ffae0000 {
> +               compatible = "syscon";
> +               reg = <0x0 0xffae0000 0x0 0x20>;
> +       };
> +
> +       pmu: power-management at ff310000 {
> +               compatible = "rockchip,rk3399-pmu", "syscon", "simple-mfd";
> +               reg = <0x0 0xff310000 0x0 0x1000>;
> +
> +               /*
> +                * Note: RK3399 supports 6 voltage domains including VD_CORE_L,
> +                * VD_CORE_B, VD_CENTER, VD_GPU, VD_LOGIC and VD_PMU.
> +                * Some of the power domains are grouped together for every
> +                * voltage domain.
> +                * The detail contents as below.
> +                */
> +               power: power-controller {
> +                       status = "okay";
> +                       compatible = "rockchip,rk3399-power-controller";
> +                       #power-domain-cells = <1>;
> +                       #address-cells = <1>;
> +                       #size-cells = <0>;
> +
> +                       /* These power domains are grouped by VD_LOGIC */
> +                       pd_vio at RK3399_PD_VIO {
> +                               reg = <RK3399_PD_VIO>;
> +                               #address-cells = <1>;
> +                               #size-cells = <0>;
> +
> +                               pd_isp0 at RK3399_PD_ISP0 {
> +                                       reg = <RK3399_PD_ISP0>;
> +                                       clocks = <&cru ACLK_ISP0>,
> +                                                <&cru HCLK_ISP0>;
> +                                       pm_qos = <&qos_isp0_m0>,
> +                                                <&qos_isp0_m1>;
> +                               };
> +                               pd_isp1 at RK3399_PD_ISP1 {
> +                                       reg = <RK3399_PD_ISP1>;
> +                                       clocks = <&cru ACLK_ISP1>,
> +                                                <&cru HCLK_ISP1>;
> +                                       pm_qos = <&qos_isp1_m0>,
> +                                                <&qos_isp1_m1>;
> +                               };
> +                               pd_vo at RK3399_PD_VO {
> +                                       reg = <RK3399_PD_VO>;
> +                                       #address-cells = <1>;
> +                                       #size-cells = <0>;
> +
> +                                       pd_vopb at RK3399_PD_VOPB {
> +                                               reg = <RK3399_PD_VOPB>;
> +                                               clocks = <&cru ACLK_VOP0>,
> +                                                        <&cru HCLK_VOP0>;
> +                                               pm_qos = <&qos_vop_big_r>,
> +                                                        <&qos_vop_big_w>;
> +                                       };
> +                                       pd_vopl at RK3399_PD_VOP {
> +                                               reg = <RK3399_PD_VOPL>;
> +                                               clocks = <&cru ACLK_VOP1>,
> +                                                        <&cru HCLK_VOP1>;
> +                                               pm_qos = <&qos_vop_little>;
> +                                       };
> +                               };
> +                               pd_hdcp at RK3399_PD_HDCP {
> +                                       reg = <RK3399_PD_HDCP>;
> +                                       clocks = <&cru ACLK_HDCP>,
> +                                                <&cru HCLK_HDCP>,
> +                                                <&cru PCLK_HDCP>;
> +                                       pm_qos = <&qos_hdcp>;
> +                               };

HDCP sorts earlier than ISP0, so should be above that.  Basically:
anything in the same area should be sorted alphabetically.

> +                       };
> +
> +                       /* These power domains are grouped by VD_CENTER */
> +                       pd_vcodec at RK3399_PD_VCODEC {
> +                               reg = <RK3399_PD_VCODEC>;
> +                               clocks = <&cru ACLK_VCODEC>,
> +                                        <&cru HCLK_VCODEC>;
> +                               pm_qos = <&qos_video_m0>;
> +                       };
> +                       pd_vdu at RK3399_PD_VDU {
> +                               reg = <RK3399_PD_VDU>;
> +                               clocks = <&cru ACLK_VDU>,
> +                                        <&cru HCLK_VDU>;
> +                               pm_qos = <&qos_video_m1_r>,
> +                                        <&qos_video_m1_w>;
> +                       };
> +                       pd_rga at RK3399_PD_RGA {
> +                               reg = <RK3399_PD_RGA>;
> +                               clocks = <&cru ACLK_RGA>,
> +                                        <&cru HCLK_RGA>;
> +                               pm_qos = <&qos_rga_r>,
> +                                        <&qos_rga_w>;
> +                       };
> +                       pd_iep at RK3399_PD_IE {
> +                               reg = <RK3399_PD_IEP>;
> +                               clocks = <&cru ACLK_IEP>,
> +                                        <&cru HCLK_IEP>;
> +                               pm_qos = <&qos_iep>;
> +                       };

IE sorts before RGA.  ...and both come before VCODEC / VDU.

> +
> +                       /* These power domains are grouped by VD_GPU */
> +                       pd_gpu at RK3399_PD_GPU {
> +                               reg = <RK3399_PD_GPU>;
> +                               clocks = <&cru ACLK_GPU>;
> +                               pm_qos = <&qos_gpu>;
> +                       };
> +               };
> +       };
> +
>         pmugrf: syscon at ff320000 {
>                 compatible = "rockchip,rk3399-pmugrf", "syscon", "simple-mfd";
>                 reg = <0x0 0xff320000 0x0 0x1000>;
> --
> 1.9.1
>


From javier at dowhile0.org  Fri Jul  1 10:09:18 2016
From: javier at dowhile0.org (Javier Martinez Canillas)
Date: Fri, 1 Jul 2016 13:09:18 -0400
Subject: [PATCH v2] arm64: dts: rockchip: add the power domain node for
 rk3399
In-Reply-To: <1467347575-11806-1-git-send-email-wxt@rock-chips.com>
References: <1467347575-11806-1-git-send-email-wxt@rock-chips.com>
Message-ID: <CABxcv=nJ4jaeBwPOfcKTmBpSHSD1wX-kJ2oi-bFki4=Dae0tEw@mail.gmail.com>

Hello Caesar,

On Fri, Jul 1, 2016 at 12:32 AM, Caesar Wang <wxt at rock-chips.com> wrote:
> From: Elaine Zhang <zhangqing at rock-chips.com>
>
> In order to meet low power requirements, a power management unit (PMU) is
> designed for controlling power resources in RK3399. The RK3399 PMU is
> dedicated for managing the power of the whole chip.
>
> 1. add pd node for RK3399 Soc
> 2. create power domain tree
> 3. add qos node for domain
>
> From the DT/binds and driver can get more detail information:
> The driver:
>         drivers/soc/rockchip/pm_domains.c
> The document:
>         Documentation/devicetree/bindings/soc/rockchip/power_domain.txt
> ---

Everything from here to the actual diff will omitted by tools like git-am...

>
> Changes in v2:
> - As Doug/Heiko commnets on https://patchwork.kernel.org/patch/9206415/.
>   drop the debugfs-dump and Add the comments for alphabetical order.
>
> Note: As the TRM lists many voltage domains and power domains, then
> in actual we just need control some domains for driver.
> Due to some domains (e.g. emmc, usb, core)...We can't turn off it on
> bootup.
>
> Signed-off-by: Elaine Zhang <zhangqing at rock-chips.com>
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
> Cc: linux-arm-kernel at lists.infradead.org
> Cc: linux-rockchip at lists.infradead.org
> Cc: Heiko Stuebner <heiko at sntech.de>
>

...but you have your S-o-B and collected tags here so these are going
to be stripped. You should move those before the first '---'
separator.

Best regards,
Javier


From heiko at sntech.de  Fri Jul  1 10:19:13 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Fri, 01 Jul 2016 19:19:13 +0200
Subject: [PATCH v2] arm64: dts: rockchip: add the power domain node for
 rk3399
In-Reply-To: <CAD=FV=U0FU5P+seYYmJNZmcV1r-N6_UHtMW5LuVGM-NyPER8BQ@mail.gmail.com>
References: <1467347575-11806-1-git-send-email-wxt@rock-chips.com>
 <CAD=FV=U0FU5P+seYYmJNZmcV1r-N6_UHtMW5LuVGM-NyPER8BQ@mail.gmail.com>
Message-ID: <2235386.KcC75gPTXr@phil>

Am Freitag, 1. Juli 2016, 09:56:31 schrieb Doug Anderson:
> Caesar
> 
> On Thu, Jun 30, 2016 at 9:32 PM, Caesar Wang <wxt at rock-chips.com> wrote:
> > From: Elaine Zhang <zhangqing at rock-chips.com>
> > 
> > In order to meet low power requirements, a power management unit (PMU)
> > is
> > designed for controlling power resources in RK3399. The RK3399 PMU is
> > dedicated for managing the power of the whole chip.
> > 
> > 1. add pd node for RK3399 Soc
> > 2. create power domain tree
> > 3. add qos node for domain
> > 
> > From the DT/binds and driver can get more detail information:
> > 
> > The driver:
> >         drivers/soc/rockchip/pm_domains.c
> > 
> > The document:
> >         Documentation/devicetree/bindings/soc/rockchip/power_domain.txt
> > 
> > ---
> > 
> > Changes in v2:
> > - As Doug/Heiko commnets on https://patchwork.kernel.org/patch/9206415/.
> > 
> >   drop the debugfs-dump and Add the comments for alphabetical order.
> > 
> > Note: As the TRM lists many voltage domains and power domains, then
> > in actual we just need control some domains for driver.
> > Due to some domains (e.g. emmc, usb, core)...We can't turn off it on
> > bootup.
> 
> I'm curious: why can't you turn off USB power domains if a board
> doesn't usb USB?  ...or GMAC on boards that don't use Ethernet?  ...or
> eDP on boards that don't use EDP?
> 
> Maybe the driver for these things isn't ready to handle power domains
> yet so that's why they are left out for now?

I think core power-domain handling is actually transparent to drivers (aka 
turning on before probe and turning off after remove), see 
drivers/base/platform.c:platform_drv_probe() calling dev_pm_domain_attach() 
with the poweron flag set.

And all the unused domains getting turned off automatically also shouldn't 
hurt us I'd think. Just that someone need to collect all clocks needed for 
the synchronuous reset.




From seanpaul at chromium.org  Fri Jul  1 11:00:00 2016
From: seanpaul at chromium.org (Sean Paul)
Date: Fri, 1 Jul 2016 14:00:00 -0400
Subject: [PATCH v3 2/4] drm/rockchip: add an common abstracted PSR driver
In-Reply-To: <1467364754-21674-1-git-send-email-ykk@rock-chips.com>
References: <1467364685-21390-1-git-send-email-ykk@rock-chips.com>
 <1467364754-21674-1-git-send-email-ykk@rock-chips.com>
Message-ID: <CAOw6vbKMWEEDEqw0_ba_FGO-2mOBg3uanQzk5nbnCtowWdLu9w@mail.gmail.com>

On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk at rock-chips.com> wrote:
> The PSR driver have exported four symbols for specific device driver:
> - rockchip_drm_psr_register()
> - rockchip_drm_psr_unregister()
> - rockchip_drm_psr_enable()
> - rockchip_drm_psr_disable()
> - rockchip_drm_psr_flush()
>
> Encoder driver should call the register/unregister interfaces to hook
> itself into common PSR driver, encoder have implement the 'psr_set'
> callback which use the set PSR state in hardware side.
>
> Crtc driver would call the enable/disable interfaces when vblank is
> enable/disable, after that the common PSR driver would call the encoder
> registered callback to set the PSR state.
>

This feels overly complicated. It seems like you could cut out a bunch
of code by just coding the psr functions into vop and
analogix_dp-rockchip. I suppose the only reason to keep it abstracted
would be if you plan on supporting psr in a different encoder or crtc
in rockchip, or if you're planning on moving this into drm core.

Perhaps others will disagree with this sentiment and this is the right
thing to do.

> Fb driver would call the flush interface in 'fb->dirty' callback, this
> helper function would force all PSR enabled encoders to exit from PSR
> for 3 seconds.
>
> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
> ---
> Changes in v3:
> - split the psr flow into an common abstracted PSR driver
> - implement the 'fb->dirty' callback function (Daniel)
> - avoid to use notify to acqiure for vact event (Daniel)
> - remove psr_active() callback which introduce in v2
>
> Changes in v2: None
>
>  drivers/gpu/drm/rockchip/Makefile           |   2 +-
>  drivers/gpu/drm/rockchip/rockchip_drm_fb.c  |  12 ++
>  drivers/gpu/drm/rockchip/rockchip_drm_psr.c | 200 ++++++++++++++++++++++++++++
>  drivers/gpu/drm/rockchip/rockchip_drm_psr.h |  12 ++
>  drivers/gpu/drm/rockchip/rockchip_drm_vop.c |  24 ++++
>  5 files changed, 249 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.c
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.h
>
> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
> index 05d0713..9746365 100644
> --- a/drivers/gpu/drm/rockchip/Makefile
> +++ b/drivers/gpu/drm/rockchip/Makefile
> @@ -3,7 +3,7 @@
>  # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
>
>  rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
> -               rockchip_drm_gem.o rockchip_drm_vop.o
> +               rockchip_drm_gem.o rockchip_drm_psr.o rockchip_drm_vop.o
>  rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
>
>  obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> index 20f12bc..0fec18f 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> @@ -21,6 +21,7 @@
>
>  #include "rockchip_drm_drv.h"
>  #include "rockchip_drm_gem.h"
> +#include "rockchip_drm_psr.h"
>
>  #define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
>
> @@ -66,9 +67,20 @@ static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
>                                      rockchip_fb->obj[0], handle);
>  }
>
> +static int rockchip_drm_fb_dirty(struct drm_framebuffer *fb,
> +                                struct drm_file *file,
> +                                unsigned int flags, unsigned int color,
> +                                struct drm_clip_rect *clips,
> +                                unsigned int num_clips)
> +{
> +       rockchip_drm_psr_flush();
> +       return 0;
> +}
> +
>  static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
>         .destroy        = rockchip_drm_fb_destroy,
>         .create_handle  = rockchip_drm_fb_create_handle,
> +       .dirty          = rockchip_drm_fb_dirty,
>  };
>
>  static struct rockchip_drm_fb *
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> new file mode 100644
> index 0000000..c044443
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> @@ -0,0 +1,200 @@
> +#include <drm/drm_crtc_helper.h>
> +
> +#include "rockchip_drm_psr.h"
> +
> +#define PSR_FLUSH_TIMEOUT      msecs_to_jiffies(3000) /* 3 seconds */
> +
> +static LIST_HEAD(psr_list);
> +static DEFINE_MUTEX(psr_list_mutex);

I'm not crazy about these globals. Perhaps you can initialize them
with the rockchip driver and tuck them in a driver-level struct
(rockchip_drm_private or something).


> +
> +enum psr_state {
> +       PSR_FLUSH,
> +       PSR_ENABLE,
> +       PSR_DISABLE,
> +};
> +
> +struct psr_drv {
> +       struct list_head list;
> +       enum psr_state state;
> +       struct mutex state_mutex;
> +
> +       struct timer_list flush_timer;
> +
> +       struct drm_encoder *encoder;
> +       int (*set)(struct drm_encoder *encoder, bool enable);
> +};
> +
> +static struct psr_drv *find_psr_by_crtc(struct drm_crtc *crtc)
> +{
> +       struct psr_drv *psr;
> +
> +       mutex_lock(&psr_list_mutex);
> +       list_for_each_entry(psr, &psr_list, list) {
> +               if (psr->encoder->crtc == crtc) {
> +                       mutex_unlock(&psr_list_mutex);
> +                       return psr;
> +               }
> +       }
> +       mutex_unlock(&psr_list_mutex);
> +
> +       return ERR_PTR(-ENODEV);
> +}
> +
> +static void psr_enable(struct psr_drv *psr)
> +{
> +       if (psr->state == PSR_ENABLE)
> +               return;

Should you be worried about races by accessing this outside of the state mutex?

> +
> +       mutex_lock(&psr->state_mutex);
> +       psr->state = PSR_ENABLE;
> +       psr->set(psr->encoder, true);
> +       mutex_unlock(&psr->state_mutex);
> +}
> +
> +static void psr_disable(struct psr_drv *psr)
> +{
> +       if (psr->state == PSR_DISABLE)
> +               return;
> +
> +       mutex_lock(&psr->state_mutex);
> +       psr->state = PSR_DISABLE;
> +       psr->set(psr->encoder, false);
> +       mutex_unlock(&psr->state_mutex);
> +}
> +
> +static void psr_flush_handler(unsigned long data)
> +{
> +       struct psr_drv *psr = (struct psr_drv *)data;
> +
> +       if (!psr || psr->state != PSR_FLUSH)
> +               return;
> +
> +       psr_enable(psr);
> +}
> +
> +/**
> + * rockchip_drm_psr_enable - enable the encoder PSR which bind to given CRTC
> + * @crtc: CRTC to obtain the PSR encoder
> + *
> + * Returns:
> + * Zero on success, negative errno on failure.
> + */
> +int rockchip_drm_psr_enable(struct drm_crtc *crtc)
> +{
> +       struct psr_drv *psr = find_psr_by_crtc(crtc);
> +
> +       if (IS_ERR(psr))
> +               return PTR_ERR(psr);
> +
> +       psr_enable(psr);
> +       return 0;
> +}
> +EXPORT_SYMBOL(rockchip_drm_psr_enable);
> +
> +/**
> + * rockchip_drm_psr_disable - disable the encoder PSR which bind to given CRTC
> + * @crtc: CRTC to obtain the PSR encoder
> + *
> + * Returns:
> + * Zero on success, negative errno on failure.
> + */
> +int rockchip_drm_psr_disable(struct drm_crtc *crtc)
> +{
> +       struct psr_drv *psr = find_psr_by_crtc(crtc);
> +
> +       if (IS_ERR(psr))
> +               return PTR_ERR(psr);
> +
> +       psr_disable(psr);
> +       return 0;
> +}
> +EXPORT_SYMBOL(rockchip_drm_psr_disable);
> +
> +/**
> + * rockchip_drm_psr_flush - force to flush all registered PSR encoders
> + *
> + * Disable the PSR function for all registered encoders, and then enable the
> + * PSR function back after 5 second. If encoder PSR state have been changed

s/5 second/PSR_FLUSH_TIMEOUT/

> + * during flush time, then keep the state no change after flush timeout.
> + *
> + * Returns:
> + * Zero on success, negative errno on failure.
> + */
> +void rockchip_drm_psr_flush(void)
> +{
> +       struct psr_drv *psr;
> +
> +       mutex_lock(&psr_list_mutex);
> +       list_for_each_entry(psr, &psr_list, list) {
> +               if (psr->state == PSR_DISABLE)
> +                       continue;
> +
> +               mod_timer(&psr->flush_timer,
> +                         round_jiffies_up(jiffies + PSR_FLUSH_TIMEOUT));
> +
> +               psr_disable(psr);
> +               psr->state = PSR_FLUSH;

This is set outside of state_mutex, is that intentional?

> +       }
> +       mutex_unlock(&psr_list_mutex);
> +}
> +EXPORT_SYMBOL(rockchip_drm_psr_flush);
> +
> +/**
> + * rockchip_drm_psr_register - register encoder to psr driver
> + * @encoder: encoder that obtain the PSR function
> + * @psr_set: call back to set PSR state
> + *
> + * Returns:
> + * Zero on success, negative errno on failure.
> + */
> +int rockchip_drm_psr_register(struct drm_encoder *encoder,
> +                             int (*psr_set)(struct drm_encoder *, bool enable))
> +{
> +       struct psr_drv *psr;
> +
> +       if (!encoder || !psr_set)
> +               return -EINVAL;
> +
> +       psr = kzalloc(sizeof(struct psr_drv), GFP_KERNEL);
> +       if (!psr)
> +               return -ENOMEM;
> +
> +       setup_timer(&psr->flush_timer, psr_flush_handler, (unsigned long)psr);
> +
> +       mutex_init(&psr->state_mutex);
> +
> +       psr->state = PSR_DISABLE;
> +       psr->encoder = encoder;
> +       psr->set = psr_set;
> +
> +       mutex_lock(&psr_list_mutex);
> +       list_add_tail(&psr->list, &psr_list);
> +       mutex_unlock(&psr_list_mutex);
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL(rockchip_drm_psr_register);
> +
> +/**
> + * rockchip_drm_psr_unregister - unregister encoder to psr driver
> + * @encoder: encoder that obtain the PSR function
> + * @psr_set: call back to set PSR state
> + *
> + * Returns:
> + * Zero on success, negative errno on failure.
> + */
> +void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
> +{
> +       struct psr_drv *psr;
> +
> +       mutex_lock(&psr_list_mutex);
> +       list_for_each_entry(psr, &psr_list, list) {
> +               if (psr->encoder == encoder) {
> +                       del_timer(&psr->flush_timer);
> +                       list_del(&psr->list);
> +                       kfree(psr);
> +               }
> +       }
> +       mutex_unlock(&psr_list_mutex);
> +}
> +EXPORT_SYMBOL(rockchip_drm_psr_unregister);
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.h b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
> new file mode 100644
> index 0000000..622f605
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
> @@ -0,0 +1,12 @@
> +#ifndef __ROCKCHIP_DRM_PSR___
> +#define __ROCKCHIP_DRM_PSR___
> +
> +void rockchip_drm_psr_flush(void);
> +int rockchip_drm_psr_enable(struct drm_crtc *crtc);
> +int rockchip_drm_psr_disable(struct drm_crtc *crtc);
> +
> +int rockchip_drm_psr_register(struct drm_encoder *encoder,
> +                           int (*psr_set)(struct drm_encoder *, bool enable));
> +void rockchip_drm_psr_unregister(struct drm_encoder *encoder);
> +
> +#endif /* __ROCKCHIP_DRM_PSR__ */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> index cd3cac5..3c6dfc5 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> @@ -34,6 +34,7 @@
>  #include "rockchip_drm_drv.h"
>  #include "rockchip_drm_gem.h"
>  #include "rockchip_drm_fb.h"
> +#include "rockchip_drm_psr.h"
>  #include "rockchip_drm_vop.h"
>
>  #define __REG_SET_RELAXED(x, off, mask, shift, v, write_mask) \
> @@ -121,6 +122,9 @@ struct vop {
>         /* protected by dev->event_lock */
>         struct drm_pending_vblank_event *event;
>
> +       bool psr_enabled;
> +       struct delayed_work psr_work;
> +
>         struct completion line_flag_completion;
>
>         const struct vop_data *data;
> @@ -911,6 +915,16 @@ static const struct drm_plane_funcs vop_plane_funcs = {
>         .atomic_destroy_state = vop_atomic_plane_destroy_state,
>  };
>
> +static void vop_psr_work(struct work_struct *work)
> +{
> +       struct vop *vop = container_of(work, typeof(*vop), psr_work.work);
> +
> +       if (vop->psr_enabled)
> +               rockchip_drm_psr_enable(&vop->crtc);
> +       else
> +               rockchip_drm_psr_disable(&vop->crtc);
> +}
> +
>  static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
>  {
>         struct vop *vop = to_vop(crtc);
> @@ -925,6 +939,9 @@ static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
>
>         spin_unlock_irqrestore(&vop->irq_lock, flags);
>
> +       vop->psr_enabled = false;
> +       schedule_delayed_work(&vop->psr_work, msecs_to_jiffies(10));
> +
>         return 0;
>  }
>
> @@ -941,6 +958,9 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc)
>         VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0);
>
>         spin_unlock_irqrestore(&vop->irq_lock, flags);
> +
> +       vop->psr_enabled = true;
> +       schedule_delayed_work(&vop->psr_work, msecs_to_jiffies(10));
>  }
>
>  static void vop_crtc_wait_for_update(struct drm_crtc *crtc)
> @@ -1582,6 +1602,10 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
>                 return ret;
>
>         pm_runtime_enable(&pdev->dev);
> +
> +       vop->psr_enabled = false;
> +       INIT_DELAYED_WORK(&vop->psr_work, vop_psr_work);
> +
>         return 0;
>  }
>
> --
> 1.9.1
>
>


From seanpaul at chromium.org  Fri Jul  1 12:46:09 2016
From: seanpaul at chromium.org (Sean Paul)
Date: Fri, 1 Jul 2016 15:46:09 -0400
Subject: [PATCH v3 3/4] drm/bridge: analogix_dp: add the PSR function
 support
In-Reply-To: <1467364768-21741-1-git-send-email-ykk@rock-chips.com>
References: <1467364685-21390-1-git-send-email-ykk@rock-chips.com>
 <1467364768-21741-1-git-send-email-ykk@rock-chips.com>
Message-ID: <CAOw6vbJ4H9xzeaA-2OKsG+pGwdvehS91TZDPfUyq8R-=e70bPQ@mail.gmail.com>

On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk at rock-chips.com> wrote:
> The full name of PSR is Panel Self Refresh, panel device could refresh
> itself with the hardware framebuffer in panel, this would make lots of
> sense to save the power consumption.
>
> This patch have exported two symbols for platform driver to implement
> the PSR function in hardware side:
> - analogix_dp_active_psr()
> - analogix_dp_inactive_psr()
>
> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
> ---
> Changes in v3:
> - split analogix_dp_enable_psr(), make it more clearly
>     analogix_dp_detect_sink_psr()
>     analogix_dp_enable_sink_psr()
> - remove some nosie register setting comments
>
> Changes in v2:
> - introduce in v2, splite the common Analogix DP changes out
>
>  drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 64 ++++++++++++++++++++++
>  drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |  4 ++
>  drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  | 54 ++++++++++++++++++
>  drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h  | 28 ++++++++++
>  include/drm/bridge/analogix_dp.h                   |  3 +
>  5 files changed, 153 insertions(+)
>
> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> index 32715da..b557097 100644
> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> @@ -97,6 +97,66 @@ static int analogix_dp_detect_hpd(struct analogix_dp_device *dp)
>         return 0;
>  }
>
> +int analogix_dp_active_psr(struct device *dev)
> +{
> +       struct analogix_dp_device *dp = dev_get_drvdata(dev);
> +
> +       if (!dp->psr_support)
> +               return -EINVAL;
> +
> +       analogix_dp_send_psr_spd(dp, EDP_VSC_PSR_STATE_ACTIVE |
> +                                EDP_VSC_PSR_CRC_VALUES_VALID);
> +       return 0;
> +}
> +EXPORT_SYMBOL_GPL(analogix_dp_active_psr);
> +
> +int analogix_dp_inactive_psr(struct device *dev)
> +{
> +       struct analogix_dp_device *dp = dev_get_drvdata(dev);
> +
> +       if (!dp->psr_support)
> +               return -EINVAL;
> +
> +       analogix_dp_send_psr_spd(dp, 0);
> +       return 0;
> +}
> +EXPORT_SYMBOL_GPL(analogix_dp_inactive_psr);
> +
> +static bool analogix_dp_detect_sink_psr(struct analogix_dp_device *dp)
> +{
> +       unsigned char psr_version;
> +
> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_SUPPORT, &psr_version);
> +       dev_info(dp->dev, "Panel PSR version : %x\n", psr_version);
> +

This info message is likely to be spammy since it's printed everytime
the panel toggle on. Perhaps downgrade to debug level.

> +       return (psr_version & DP_PSR_IS_SUPPORTED) ? true : false;
> +}
> +
> +static int analogix_dp_enable_sink_psr(struct analogix_dp_device *dp)

Return type is int, but the function never fails and you don't check
the return value when calling it. Seems like this should be void.

> +{
> +       unsigned char psr_en;
> +
> +       /* Disable psr function */
> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
> +       psr_en &= ~DP_PSR_ENABLE;
> +       analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
> +
> +       /* Main-Link transmitter remains active during PSR active states */
> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
> +       psr_en = DP_PSR_MAIN_LINK_ACTIVE | DP_PSR_CRC_VERIFICATION;

Why read psr_en if you're just going to overwrite it? Perhaps you meant |= here.

> +       analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
> +
> +       /* Enable psr function */
> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
> +       psr_en = DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE |
> +                DP_PSR_CRC_VERIFICATION;

Again, no need to read if you're just overwriting.

> +       analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
> +
> +       analogix_dp_enable_psr_crc(dp);
> +
> +       return 0;
> +}
> +
>  static unsigned char analogix_dp_calc_edid_check_sum(unsigned char *edid_data)
>  {
>         int i;
> @@ -921,6 +981,10 @@ static void analogix_dp_commit(struct analogix_dp_device *dp)
>
>         /* Enable video */
>         analogix_dp_start_video(dp);
> +
> +       dp->psr_support = analogix_dp_detect_sink_psr(dp);
> +       if (dp->psr_support)
> +               analogix_dp_enable_sink_psr(dp);
>  }
>
>  int analogix_dp_get_modes(struct drm_connector *connector)
> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
> index b456380..6ca5dde 100644
> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
> @@ -177,6 +177,7 @@ struct analogix_dp_device {
>         int                     hpd_gpio;
>         bool                    force_hpd;
>         unsigned char           edid[EDID_BLOCK_LENGTH * 2];
> +       bool                    psr_support;
>
>         struct analogix_dp_plat_data *plat_data;
>  };
> @@ -278,4 +279,7 @@ int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp);
>  void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp);
>  void analogix_dp_enable_scrambling(struct analogix_dp_device *dp);
>  void analogix_dp_disable_scrambling(struct analogix_dp_device *dp);
> +void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp);
> +void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, int db1);
> +
>  #endif /* _ANALOGIX_DP_CORE_H */
> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
> index 48030f0..e8372c7 100644
> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
> @@ -1322,3 +1322,57 @@ void analogix_dp_disable_scrambling(struct analogix_dp_device *dp)
>         reg |= SCRAMBLING_DISABLE;
>         writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
>  }
> +
> +void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp)
> +{
> +       writel(PSR_VID_CRC_FLUSH | PSR_VID_CRC_ENABLE,
> +              dp->reg_base + ANALOGIX_DP_CRC_CON);
> +
> +       usleep_range(10, 20);

Is this sleep arbitrary, or documented somewhere? Could you add a
comment explaining how this was arrived at?

> +
> +       writel(PSR_VID_CRC_ENABLE, dp->reg_base + ANALOGIX_DP_CRC_CON);
> +}
> +
> +void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, int db1)
> +{
> +       unsigned int val;
> +
> +       /* don't send info frame */
> +       val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
> +       val &= ~IF_EN;
> +       writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
> +
> +       /* configure single frame update mode */
> +       writel(0x3, dp->reg_base + ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL);
> +
> +       /* configure VSC HB0 ~ HB3 */
> +       writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_HB0);
> +       writel(0x07, dp->reg_base + ANALOGIX_DP_SPD_HB1);
> +       writel(0x02, dp->reg_base + ANALOGIX_DP_SPD_HB2);
> +       writel(0x08, dp->reg_base + ANALOGIX_DP_SPD_HB3);
> +
> +       /* configure VSC HB0 ~ HB3 */
> +       writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_PB0);
> +       writel(0x16, dp->reg_base + ANALOGIX_DP_SPD_PB1);
> +       writel(0xCE, dp->reg_base + ANALOGIX_DP_SPD_PB2);
> +       writel(0x5D, dp->reg_base + ANALOGIX_DP_SPD_PB3);
> +
> +       /* configure DB0 / DB1 values */
> +       writel(0x00, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB0);

Lots of hardcoded values here. I think this could be cleaned up.

> +       writel(db1, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB1);
> +
> +       /* set reuse spd inforframe */
> +       val = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
> +       val |= REUSE_SPD_EN;
> +       writel(val, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
> +
> +       /* mark info frame update */
> +       val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
> +       val = (val | IF_UP) & ~IF_EN;
> +       writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
> +
> +       /* send info frame */
> +       val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
> +       val |= IF_EN;
> +       writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
> +}
> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
> index cdcc6c5..a2698e4 100644
> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
> @@ -22,6 +22,8 @@
>  #define ANALOGIX_DP_VIDEO_CTL_8                        0x3C
>  #define ANALOGIX_DP_VIDEO_CTL_10               0x44
>
> +#define ANALOGIX_DP_SPDIF_AUDIO_CTL_0          0xD8
> +
>  #define ANALOGIX_DP_PLL_REG_1                  0xfc
>  #define ANALOGIX_DP_PLL_REG_2                  0x9e4
>  #define ANALOGIX_DP_PLL_REG_3                  0x9e8
> @@ -30,6 +32,21 @@
>
>  #define ANALOGIX_DP_PD                         0x12c
>
> +#define ANALOGIX_DP_IF_TYPE                    0x244
> +#define ANALOGIX_DP_IF_PKT_DB1                 0x254
> +#define ANALOGIX_DP_IF_PKT_DB2                 0x258
> +#define ANALOGIX_DP_SPD_HB0                    0x2F8
> +#define ANALOGIX_DP_SPD_HB1                    0x2FC
> +#define ANALOGIX_DP_SPD_HB2                    0x300
> +#define ANALOGIX_DP_SPD_HB3                    0x304
> +#define ANALOGIX_DP_SPD_PB0                    0x308
> +#define ANALOGIX_DP_SPD_PB1                    0x30C
> +#define ANALOGIX_DP_SPD_PB2                    0x310
> +#define ANALOGIX_DP_SPD_PB3                    0x314
> +#define ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL      0x318
> +#define ANALOGIX_DP_VSC_SHADOW_DB0             0x31C
> +#define ANALOGIX_DP_VSC_SHADOW_DB1             0x320
> +
>  #define ANALOGIX_DP_LANE_MAP                   0x35C
>
>  #define ANALOGIX_DP_ANALOG_CTL_1               0x370
> @@ -103,6 +120,8 @@
>
>  #define ANALOGIX_DP_SOC_GENERAL_CTL            0x800
>
> +#define ANALOGIX_DP_CRC_CON                    0x890
> +
>  /* ANALOGIX_DP_TX_SW_RESET */
>  #define RESET_DP_TX                            (0x1 << 0)
>
> @@ -151,6 +170,7 @@
>  #define VID_CHK_UPDATE_TYPE_SHIFT              (4)
>  #define VID_CHK_UPDATE_TYPE_1                  (0x1 << 4)
>  #define VID_CHK_UPDATE_TYPE_0                  (0x0 << 4)
> +#define REUSE_SPD_EN                           (0x1 << 3)
>
>  /* ANALOGIX_DP_VIDEO_CTL_8 */
>  #define VID_HRES_TH(x)                         (((x) & 0xf) << 4)
> @@ -376,4 +396,12 @@
>  #define VIDEO_MODE_SLAVE_MODE                  (0x1 << 0)
>  #define VIDEO_MODE_MASTER_MODE                 (0x0 << 0)
>
> +/* ANALOGIX_DP_PKT_SEND_CTL */
> +#define IF_UP                                  (0x1 << 4)
> +#define IF_EN                                  (0x1 << 0)
> +
> +/* ANALOGIX_DP_CRC_CON */
> +#define PSR_VID_CRC_FLUSH                      (0x1 << 2)
> +#define PSR_VID_CRC_ENABLE                     (0x1 << 0)
> +
>  #endif /* _ANALOGIX_DP_REG_H */
> diff --git a/include/drm/bridge/analogix_dp.h b/include/drm/bridge/analogix_dp.h
> index 261b86d..183a336 100644
> --- a/include/drm/bridge/analogix_dp.h
> +++ b/include/drm/bridge/analogix_dp.h
> @@ -38,6 +38,9 @@ struct analogix_dp_plat_data {
>                          struct drm_connector *);
>  };
>
> +int analogix_dp_active_psr(struct device *dev);
> +int analogix_dp_inactive_psr(struct device *dev);

Why active/inactive instead of enable/disable, which is used everywhere else?

> +
>  int analogix_dp_resume(struct device *dev);
>  int analogix_dp_suspend(struct device *dev);
>
> --
> 1.9.1
>
>


From seanpaul at chromium.org  Fri Jul  1 13:05:52 2016
From: seanpaul at chromium.org (Sean Paul)
Date: Fri, 1 Jul 2016 16:05:52 -0400
Subject: [PATCH v3 4/4] drm/rockchip: analogix_dp: implement PSR function
In-Reply-To: <1467364776-21794-1-git-send-email-ykk@rock-chips.com>
References: <1467364685-21390-1-git-send-email-ykk@rock-chips.com>
 <1467364776-21794-1-git-send-email-ykk@rock-chips.com>
Message-ID: <CAOw6vbL=GsAL+XDive6g4ettZrdrW6BZ-OkVbPp54E5gxn_HsQ@mail.gmail.com>

On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk at rock-chips.com> wrote:
> Alway enable the PSR function for Rockchip analogix_dp driver. If panel
> don't support PSR, then the core analogix_dp would ignore this setting.
>
> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
> ---
> Changes in v3:
> - split the common psr logic into a seperate driver, make this to a
>   simple sub-psr device driver.
>
> Changes in v2:
> - remove vblank notify out (Daniel)
> - create a psr_active() callback in vop data struct.
>
>  drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 52 +++++++++++++++++++++++++
>  1 file changed, 52 insertions(+)
>
> diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
> index e81e19a..80a60a6 100644
> --- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
> +++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
> @@ -32,6 +32,7 @@
>  #include <drm/bridge/analogix_dp.h>
>
>  #include "rockchip_drm_drv.h"
> +#include "rockchip_drm_psr.h"
>  #include "rockchip_drm_vop.h"
>
>  #define RK3288_GRF_SOC_CON6            0x25c
> @@ -68,11 +69,53 @@ struct rockchip_dp_device {
>         struct regmap            *grf;
>         struct reset_control     *rst;
>
> +       struct delayed_work      psr_work;
> +       unsigned int             psr_state;
> +
>         const struct rockchip_dp_chip_data *data;
>
>         struct analogix_dp_plat_data plat_data;
>  };
>
> +static int analogix_dp_psr_set(struct drm_encoder *encoder, bool enabled)

Again, this function doesn't fail, but its return type is int.
Fortunately you don't check the return in rockchip_drm_psr.c, so this
also seems like a good void candidate.

> +{
> +       struct rockchip_dp_device *dp = to_dp(encoder);
> +
> +       dev_dbg(dp->dev, "%s PSR...\n", enabled ? "Entry" : "Exit");
> +
> +       if (enabled)
> +               dp->psr_state = EDP_VSC_PSR_STATE_ACTIVE;
> +       else
> +               dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE;
> +
> +       schedule_delayed_work(&dp->psr_work, msecs_to_jiffies(10));

Pull 10 out into a #define

> +
> +       return 0;
> +}
> +
> +static void analogix_dp_psr_work(struct work_struct *work)
> +{
> +       struct rockchip_dp_device *dp =
> +                               container_of(work, typeof(*dp), psr_work.work);
> +       struct drm_crtc *crtc = dp->encoder.crtc;
> +       int psr_state = dp->psr_state;
> +       int vact_end;
> +       int ret;
> +
> +       if (!crtc)
> +               return;
> +
> +       vact_end = crtc->mode.vtotal - crtc->mode.vsync_start + crtc->mode.vdisplay;
> +
> +       ret = rockchip_drm_wait_line_flag(dp->encoder.crtc, vact_end, 100);


Pull 100 out into a #define

> +       if (ret == 0) {

if (ret) {
    dev_err(... "line flag interrupt did not arrive");
    return;
}

> +               if (psr_state == EDP_VSC_PSR_STATE_ACTIVE)
> +                       analogix_dp_active_psr(dp->dev);
> +               else
> +                       analogix_dp_inactive_psr(dp->dev);
> +       }
> +}
> +
>  static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
>  {
>         reset_control_assert(dp->rst);
> @@ -340,12 +383,21 @@ static int rockchip_dp_bind(struct device *dev, struct device *master,
>         dp->plat_data.power_off = rockchip_dp_powerdown;
>         dp->plat_data.get_modes = rockchip_dp_get_modes;
>
> +       dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE;
> +       INIT_DELAYED_WORK(&dp->psr_work, analogix_dp_psr_work);
> +
> +       rockchip_drm_psr_register(&dp->encoder, analogix_dp_psr_set);
> +
>         return analogix_dp_bind(dev, dp->drm_dev, &dp->plat_data);
>  }
>
>  static void rockchip_dp_unbind(struct device *dev, struct device *master,
>                                void *data)
>  {
> +       struct rockchip_dp_device *dp = dev_get_drvdata(dev);
> +
> +       rockchip_drm_psr_unregister(&dp->encoder);
> +
>         return analogix_dp_unbind(dev, master, data);
>  }
>
> --
> 1.9.1
>
>


From dianders at chromium.org  Fri Jul  1 13:17:48 2016
From: dianders at chromium.org (Doug Anderson)
Date: Fri, 1 Jul 2016 13:17:48 -0700
Subject: [PATCH] regulator: pwm: Fix regulator ramp delay for continuous
 mode
In-Reply-To: <20160701080614.GA9196@sirena.org.uk>
References: <1467089591-7631-1-git-send-email-dianders@chromium.org>
 <20160701080614.GA9196@sirena.org.uk>
Message-ID: <CAD=FV=WKR_Fq6o34886RCY-T-6AKdRqFz9H1aTiRq7Zu=o8TWA@mail.gmail.com>

Mark,

On Fri, Jul 1, 2016 at 1:06 AM, Mark Brown <broonie at kernel.org> wrote:
> On Mon, Jun 27, 2016 at 09:53:11PM -0700, Douglas Anderson wrote:
>
>> Note that this patch is atop Boris's recent PWM regulator fixes.  If
>> desired it wouldn't be too hard to write it atop the old code, though
>> quite honestly anyone using a PWM regulator should probably be using his
>> new code.
>
> Given that those don't seem likely to go in any time soon and aren't
> going to go in as a fix can you please respin against current code?

I went to write this patch and it was pretty easy to write.  ...but
then I realized that I had no real way to test it.  I can compile test
it, but that's it.  Do you still want it?

Specifically without Boris's patches then I don't believe the devices
I have access to are bootable when I tell Linux about the PWM
regulator, especially when I configure the PWM regulator to use
continuous mode (which is where the bug is).  As I understand from
<https://patchwork.kernel.org/patch/9175279/> the current PWM
regulator assumes that 0% duty cycle is the lowest voltage and 100% is
the highest voltage.  My system is backwards (0% is highest, 100%
lowest).  Also: last I tried even the non-continuous mode without
Boris's patches the regulator would glitch enough at bootup that my
system would crash (the PWM regulator affects the memory controller).

Anyway, let me know if you want me to post the untested, rebased
patch...  Personally I'd prefer to wait for Boris's patches and the
land the tested version.

Boris: any idea for what the next steps on your series are?


-Doug


From sboyd at codeaurora.org  Fri Jul  1 17:34:57 2016
From: sboyd at codeaurora.org (Stephen Boyd)
Date: Fri, 1 Jul 2016 17:34:57 -0700
Subject: [GIT PULL] Rockchip clk changes for 4.8
In-Reply-To: <112903792.EQ89smGoay@phil>
References: <112903792.EQ89smGoay@phil>
Message-ID: <20160702003457.GI27880@codeaurora.org>

On 07/01, Heiko Stuebner wrote:
> Hi Mike, Stephen,
> 
> please find below clock changes for 4.8.
> 
> Apart from some new clock ids there is the addition of a placeholder clock
> for the rk3399 watchdog pclk - as explained in the code, this gate is not
> controlled through the clock controller but through the general register
> files, yet the watchdog is unhappy if can't see the clock. And finally some
> small fixes for clock mistakes that do not affect any currently support
> board ip blocks like spdif-DPTX clock on the rk3399.
> 
> So if this looks ok, please pull
> 

Pulled into clk-next.

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project


From wxt at rock-chips.com  Fri Jul  1 17:57:21 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Sat, 02 Jul 2016 08:57:21 +0800
Subject: [PATCH v2] arm64: dts: rockchip: add the power domain node for
 rk3399
In-Reply-To: <CABxcv=nJ4jaeBwPOfcKTmBpSHSD1wX-kJ2oi-bFki4=Dae0tEw@mail.gmail.com>
References: <1467347575-11806-1-git-send-email-wxt@rock-chips.com>
 <CABxcv=nJ4jaeBwPOfcKTmBpSHSD1wX-kJ2oi-bFki4=Dae0tEw@mail.gmail.com>
Message-ID: <57771171.2030701@rock-chips.com>


On 2016?07?02? 01:09, Javier Martinez Canillas wrote:
> Hello Caesar,
>
> On Fri, Jul 1, 2016 at 12:32 AM, Caesar Wang <wxt at rock-chips.com> wrote:
>> From: Elaine Zhang <zhangqing at rock-chips.com>
>>
>> In order to meet low power requirements, a power management unit (PMU) is
>> designed for controlling power resources in RK3399. The RK3399 PMU is
>> dedicated for managing the power of the whole chip.
>>
>> 1. add pd node for RK3399 Soc
>> 2. create power domain tree
>> 3. add qos node for domain
>>
>>  From the DT/binds and driver can get more detail information:
>> The driver:
>>          drivers/soc/rockchip/pm_domains.c
>> The document:
>>          Documentation/devicetree/bindings/soc/rockchip/power_domain.txt
>> ---
> Everything from here to the actual diff will omitted by tools like git-am...

Hmm....

I think the '---' cause the broken with patman tool.

Thanks,
Caesar
>
>> Changes in v2:
>> - As Doug/Heiko commnets on https://patchwork.kernel.org/patch/9206415/.
>>    drop the debugfs-dump and Add the comments for alphabetical order.
>>
>> Note: As the TRM lists many voltage domains and power domains, then
>> in actual we just need control some domains for driver.
>> Due to some domains (e.g. emmc, usb, core)...We can't turn off it on
>> bootup.
>>
>> Signed-off-by: Elaine Zhang <zhangqing at rock-chips.com>
>> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
>> Cc: linux-arm-kernel at lists.infradead.org
>> Cc: linux-rockchip at lists.infradead.org
>> Cc: Heiko Stuebner <heiko at sntech.de>
>>
> ...but you have your S-o-B and collected tags here so these are going
> to be stripped. You should move those before the first '---'
> separator.
> Best regards,
> Javier
>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip


-- 
caesar wang | software engineer | wxt at rock-chip.com




From wxt at rock-chips.com  Fri Jul  1 18:34:49 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Sat, 02 Jul 2016 09:34:49 +0800
Subject: [PATCH v2] arm64: dts: rockchip: add the power domain node for
 rk3399
In-Reply-To: <CAD=FV=U0FU5P+seYYmJNZmcV1r-N6_UHtMW5LuVGM-NyPER8BQ@mail.gmail.com>
References: <1467347575-11806-1-git-send-email-wxt@rock-chips.com>
 <CAD=FV=U0FU5P+seYYmJNZmcV1r-N6_UHtMW5LuVGM-NyPER8BQ@mail.gmail.com>
Message-ID: <57771A39.8060606@rock-chips.com>


On 2016?07?02? 00:56, Doug Anderson wrote:
> Caesar
>
> On Thu, Jun 30, 2016 at 9:32 PM, Caesar Wang <wxt at rock-chips.com> wrote:
>> From: Elaine Zhang <zhangqing at rock-chips.com>
>>
>> In order to meet low power requirements, a power management unit (PMU) is
>> designed for controlling power resources in RK3399. The RK3399 PMU is
>> dedicated for managing the power of the whole chip.
>>
>> 1. add pd node for RK3399 Soc
>> 2. create power domain tree
>> 3. add qos node for domain
>>
>>  From the DT/binds and driver can get more detail information:
>> The driver:
>>          drivers/soc/rockchip/pm_domains.c
>> The document:
>>          Documentation/devicetree/bindings/soc/rockchip/power_domain.txt
>> ---
>>
>> Changes in v2:
>> - As Doug/Heiko commnets on https://patchwork.kernel.org/patch/9206415/.
>>    drop the debugfs-dump and Add the comments for alphabetical order.
>>
>> Note: As the TRM lists many voltage domains and power domains, then
>> in actual we just need control some domains for driver.
>> Due to some domains (e.g. emmc, usb, core)...We can't turn off it on
>> bootup.
> I'm curious: why can't you turn off USB power domains if a board
> doesn't usb USB?  ...or GMAC on boards that don't use Ethernet?  ...or
> eDP on boards that don't use EDP?
>
> Maybe the driver for these things isn't ready to handle power domains
> yet so that's why they are left out for now?
>
> It seems like GMAC could at least be added because we don't even have
> the GMAC listed in the current device tree so therefore there can't be
> any users of it.  When it's added we can make sure that the power
> domains are added.

At least, the gmac had been supported in rockchip inside.
That's seem the gmac driver can't handle the power domain enough.
--

Frankly, I'm no test the GMAC?then this patch should support most of 
devices.
We can send another patch to support it if someone?usb/gmac) can handle 
the power domain.

>
> I think in the current upstream dtsi file there is also no xhci node,
> so does that mean you could add the USB3 power domain?
>
>

willam at RK, what's think of it?

>> Signed-off-by: Elaine Zhang <zhangqing at rock-chips.com>
>> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
>> Cc: linux-arm-kernel at lists.infradead.org
>> Cc: linux-rockchip at lists.infradead.org
>> Cc: Heiko Stuebner <heiko at sntech.de>
>>
>> ---
>>   arch/arm64/boot/dts/rockchip/rk3399.dtsi | 179 +++++++++++++++++++++++++++++++
>>   1 file changed, 179 insertions(+)
>>
>> diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
>> index a6dd623..103e185 100644
>> --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
>> +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
>> @@ -45,6 +45,7 @@
>>   #include <dt-bindings/interrupt-controller/arm-gic.h>
>>   #include <dt-bindings/interrupt-controller/irq.h>
>>   #include <dt-bindings/pinctrl/rockchip.h>
>> +#include <dt-bindings/power/rk3399-power.h>
>>   #include <dt-bindings/thermal/thermal.h>
>>
>>   / {
>> @@ -594,6 +595,184 @@
>>                  status = "disabled";
>>          };
>>
>> +       qos_hdcp: qos_hdcp at ffa90000 {
>> +               compatible = "syscon";
>> +               reg = <0x0 0xffa90000 0x0 0x20>;
>> +       };
>> +
>> +       qos_iep: qos_iep at ffa98000 {
>> +               compatible = "syscon";
>> +               reg = <0x0 0xffa98000 0x0 0x20>;
>> +       };
>> +
>> +       qos_isp0_m0: qos_isp0_m0 at ffaa0000 {
>> +               compatible = "syscon";
>> +               reg = <0x0 0xffaa0000 0x0 0x20>;
>> +       };
>> +
>> +       qos_isp0_m1: qos_isp0_m1 at ffaa0080 {
>> +               compatible = "syscon";
>> +               reg = <0x0 0xffaa0080 0x0 0x20>;
>> +       };
>> +
>> +       qos_isp1_m0: qos_isp1_m0 at ffaa8000 {
>> +               compatible = "syscon";
>> +               reg = <0x0 0xffaa8000 0x0 0x20>;
>> +       };
>> +
>> +       qos_isp1_m1: qos_isp1_m1 at ffaa8080 {
>> +               compatible = "syscon";
>> +               reg = <0x0 0xffaa8080 0x0 0x20>;
>> +       };
>> +
>> +       qos_rga_r: qos_rga_r at ffab0000 {
>> +               compatible = "syscon";
>> +               reg = <0x0 0xffab0000 0x0 0x20>;
>> +       };
>> +
>> +       qos_rga_w: qos_rga_w at ffab0080 {
>> +               compatible = "syscon";
>> +               reg = <0x0 0xffab0080 0x0 0x20>;
>> +       };
>> +
>> +       qos_video_m0: qos_video_m0 at ffab8000 {
>> +               compatible = "syscon";
>> +               reg = <0x0 0xffab8000 0x0 0x20>;
>> +       };
>> +
>> +       qos_video_m1_r: qos_video_m1_r at ffac0000 {
>> +               compatible = "syscon";
>> +               reg = <0x0 0xffac0000 0x0 0x20>;
>> +       };
>> +
>> +       qos_video_m1_w: qos_video_m1_w at ffac0080 {
>> +               compatible = "syscon";
>> +               reg = <0x0 0xffac0080 0x0 0x20>;
>> +       };
>> +
>> +       qos_vop_big_r: qos_vop_big_r at ffac8000 {
>> +               compatible = "syscon";
>> +               reg = <0x0 0xffac8000 0x0 0x20>;
>> +       };
>> +
>> +       qos_vop_big_w: qos_vop_big_w at ffac8080 {
>> +               compatible = "syscon";
>> +               reg = <0x0 0xffac8080 0x0 0x20>;
>> +       };
>> +
>> +       qos_vop_little: qos_vop_little at ffad0000 {
>> +               compatible = "syscon";
>> +               reg = <0x0 0xffad0000 0x0 0x20>;
>> +       };
>> +
>> +       qos_gpu: qos_gpu at ffae0000 {
>> +               compatible = "syscon";
>> +               reg = <0x0 0xffae0000 0x0 0x20>;
>> +       };
>> +
>> +       pmu: power-management at ff310000 {
>> +               compatible = "rockchip,rk3399-pmu", "syscon", "simple-mfd";
>> +               reg = <0x0 0xff310000 0x0 0x1000>;
>> +
>> +               /*
>> +                * Note: RK3399 supports 6 voltage domains including VD_CORE_L,
>> +                * VD_CORE_B, VD_CENTER, VD_GPU, VD_LOGIC and VD_PMU.
>> +                * Some of the power domains are grouped together for every
>> +                * voltage domain.
>> +                * The detail contents as below.
>> +                */
>> +               power: power-controller {
>> +                       status = "okay";
>> +                       compatible = "rockchip,rk3399-power-controller";
>> +                       #power-domain-cells = <1>;
>> +                       #address-cells = <1>;
>> +                       #size-cells = <0>;
>> +
>> +                       /* These power domains are grouped by VD_LOGIC */
>> +                       pd_vio at RK3399_PD_VIO {
>> +                               reg = <RK3399_PD_VIO>;
>> +                               #address-cells = <1>;
>> +                               #size-cells = <0>;
>> +
>> +                               pd_isp0 at RK3399_PD_ISP0 {
>> +                                       reg = <RK3399_PD_ISP0>;
>> +                                       clocks = <&cru ACLK_ISP0>,
>> +                                                <&cru HCLK_ISP0>;
>> +                                       pm_qos = <&qos_isp0_m0>,
>> +                                                <&qos_isp0_m1>;
>> +                               };
>> +                               pd_isp1 at RK3399_PD_ISP1 {
>> +                                       reg = <RK3399_PD_ISP1>;
>> +                                       clocks = <&cru ACLK_ISP1>,
>> +                                                <&cru HCLK_ISP1>;
>> +                                       pm_qos = <&qos_isp1_m0>,
>> +                                                <&qos_isp1_m1>;
>> +                               };
>> +                               pd_vo at RK3399_PD_VO {
>> +                                       reg = <RK3399_PD_VO>;
>> +                                       #address-cells = <1>;
>> +                                       #size-cells = <0>;
>> +
>> +                                       pd_vopb at RK3399_PD_VOPB {
>> +                                               reg = <RK3399_PD_VOPB>;
>> +                                               clocks = <&cru ACLK_VOP0>,
>> +                                                        <&cru HCLK_VOP0>;
>> +                                               pm_qos = <&qos_vop_big_r>,
>> +                                                        <&qos_vop_big_w>;
>> +                                       };
>> +                                       pd_vopl at RK3399_PD_VOP {
>> +                                               reg = <RK3399_PD_VOPL>;
>> +                                               clocks = <&cru ACLK_VOP1>,
>> +                                                        <&cru HCLK_VOP1>;
>> +                                               pm_qos = <&qos_vop_little>;
>> +                                       };
>> +                               };
>> +                               pd_hdcp at RK3399_PD_HDCP {
>> +                                       reg = <RK3399_PD_HDCP>;
>> +                                       clocks = <&cru ACLK_HDCP>,
>> +                                                <&cru HCLK_HDCP>,
>> +                                                <&cru PCLK_HDCP>;
>> +                                       pm_qos = <&qos_hdcp>;
>> +                               };
> HDCP sorts earlier than ISP0, so should be above that.  Basically:
> anything in the same area should be sorted alphabetically.

The sort is follow up the TRM.  At least I'm according to every voltage 
domain.

>
>> +                       };
>> +
>> +                       /* These power domains are grouped by VD_CENTER */
>> +                       pd_vcodec at RK3399_PD_VCODEC {
>> +                               reg = <RK3399_PD_VCODEC>;
>> +                               clocks = <&cru ACLK_VCODEC>,
>> +                                        <&cru HCLK_VCODEC>;
>> +                               pm_qos = <&qos_video_m0>;
>> +                       };
>> +                       pd_vdu at RK3399_PD_VDU {
>> +                               reg = <RK3399_PD_VDU>;
>> +                               clocks = <&cru ACLK_VDU>,
>> +                                        <&cru HCLK_VDU>;
>> +                               pm_qos = <&qos_video_m1_r>,
>> +                                        <&qos_video_m1_w>;
>> +                       };
>> +                       pd_rga at RK3399_PD_RGA {
>> +                               reg = <RK3399_PD_RGA>;
>> +                               clocks = <&cru ACLK_RGA>,
>> +                                        <&cru HCLK_RGA>;
>> +                               pm_qos = <&qos_rga_r>,
>> +                                        <&qos_rga_w>;
>> +                       };
>> +                       pd_iep at RK3399_PD_IE {
>> +                               reg = <RK3399_PD_IEP>;
>> +                               clocks = <&cru ACLK_IEP>,
>> +                                        <&cru HCLK_IEP>;
>> +                               pm_qos = <&qos_iep>;
>> +                       };
> IE sorts before RGA.  ...and both come before VCODEC / VDU.

Ditto
>
>> +
>> +                       /* These power domains are grouped by VD_GPU */
>> +                       pd_gpu at RK3399_PD_GPU {
>> +                               reg = <RK3399_PD_GPU>;
>> +                               clocks = <&cru ACLK_GPU>;
>> +                               pm_qos = <&qos_gpu>;
>> +                       };
>> +               };
>> +       };
>> +
>>          pmugrf: syscon at ff320000 {
>>                  compatible = "rockchip,rk3399-pmugrf", "syscon", "simple-mfd";
>>                  reg = <0x0 0xff320000 0x0 0x1000>;
>> --
>> 1.9.1
>>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip


-- 
caesar wang | software engineer | wxt at rock-chip.com




From edubezval at gmail.com  Fri Jul  1 18:39:21 2016
From: edubezval at gmail.com (Eduardo Valentin)
Date: Fri, 1 Jul 2016 18:39:21 -0700
Subject: [PATCH v5 1/5] thermal: Add support for hardware-tracked trip
 points
In-Reply-To: <1466565319.2379.0.camel@intel.com>
References: <1465213489-14898-1-git-send-email-wxt@rock-chips.com>
 <1465213489-14898-2-git-send-email-wxt@rock-chips.com>
 <1466565319.2379.0.camel@intel.com>
Message-ID: <20160702013920.GA9796@localhost.localdomain>

On Wed, Jun 22, 2016 at 11:15:19AM +0800, Zhang Rui wrote:
> On ?, 2016-06-06 at 19:44 +0800, Caesar Wang wrote:
> > From: Sascha Hauer <s.hauer at pengutronix.de>
> > 
> > This adds support for hardware-tracked trip points to the device tree
> > thermal sensor framework.
> > 
> > The framework supports an arbitrary number of trip points. Whenever
> > the current temperature is updated, the trip points immediately
> > below and above the current temperature are found. A .set_trips
> > callback is then called with the temperatures. If there is no trip
> > point above or below the current temperature, the passed trip
> > temperature will be -INT_MAX or INT_MAX respectively. In this
> > callback,
> > the driver should program the hardware such that it is notified
> > when either of these trip points are triggered. When a trip point
> > is triggered, the driver should call `thermal_zone_device_update'
> > for the respective thermal zone. This will cause the trip points
> > to be updated again.
> > 
> > If .set_trips is not implemented, the framework behaves as before.
> > 
> > This patch is based on an earlier version from Mikko Perttunen
> > <mikko.perttunen at kapsi.fi>
> > 
> > Signed-off-by: Sascha Hauer <s.hauer at pengutronix.de>
> > Signed-off-by: Caesar Wang <wxt at rock-chips.com>
> > Cc: Zhang Rui <rui.zhang at intel.com>
> > Cc: Eduardo Valentin <edubezval at gmail.com>
> > Cc: linux-pm at vger.kernel.org
> > 
> > ---
> > 
> > Changes in v5:
> > - add the lock for thermal_zone_set_trips function.
> > - change based on next kernel.
> > 
> > Changes in v4:
> > - Missing the lock added in v3.
> > 
> > Changes in v3:
> > - as Javi comments on https://patchwork.kernel.org/patch/9001281/.
> > - add the lock for preventing the called from multi placce
> > - add the note for pre_low/high_trip.
> > 
> > Changes in v2:
> > - update the sysfs-api.txt for set_trips.
> > 
> > ?Documentation/thermal/sysfs-api.txt |??7 +++++
> > ?drivers/thermal/thermal_core.c??????| 54
> > +++++++++++++++++++++++++++++++++++++
> > ?drivers/thermal/thermal_sysfs.c?????|??3 +++
> > ?include/linux/thermal.h?????????????| 10 +++++++
> > ?4 files changed, 74 insertions(+)
> > 
> > diff --git a/Documentation/thermal/sysfs-api.txt
> > b/Documentation/thermal/sysfs-api.txt
> > index efc3f3d..75d8838 100644
> > --- a/Documentation/thermal/sysfs-api.txt
> > +++ b/Documentation/thermal/sysfs-api.txt
> > @@ -49,6 +49,9 @@ temperature) and throttle appropriate devices.
> > ?	.bind: bind the thermal zone device with a thermal cooling
> > device.
> > ?	.unbind: unbind the thermal zone device with a thermal
> > cooling device.
> > ?	.get_temp: get the current temperature of the thermal zone.
> > +	.set_trips: set the trip points window. Whenever the current
> > temperature
> > +		????is updated, the trip points immediately below
> > and above the
> > +		????current temperature are found.
> > ?	.get_mode: get the current mode (enabled/disabled) of the
> > thermal zone.
> > ?	????- "enabled" means the kernel thermal management is
> > enabled.
> > ?	????- "disabled" will prevent kernel thermal driver action
> > upon trip points
> > @@ -95,6 +98,10 @@ temperature) and throttle appropriate devices.
> > ?			get_temp:	a pointer to a function
> > that reads the
> > ?					sensor temperature. This is
> > mandatory
> > ?					callback provided by sensor
> > driver.
> > +			set_trips:??????a pointer to a function that
> > sets a
> > +					temperature window. When
> > this window is
> > +					left the driver must inform
> > the thermal
> > +					core via
> > thermal_zone_device_update.
> > ?			get_trend:?	a pointer to a function
> > that reads the
> > ?					sensor temperature trend.
> > ?			set_emul_temp:	a pointer to a
> > function that sets
> > diff --git a/drivers/thermal/thermal_core.c
> > b/drivers/thermal/thermal_core.c
> > index 792aab7..e69aee2 100644
> > --- a/drivers/thermal/thermal_core.c
> > +++ b/drivers/thermal/thermal_core.c
> > @@ -368,6 +368,56 @@ static void handle_thermal_trip(struct
> > thermal_zone_device *tz, int trip)
> > ?	monitor_thermal_zone(tz);
> > ?}
> > ?
> > +void thermal_zone_set_trips(struct thermal_zone_device *tz)
> > +{
> > +	int low = -INT_MAX;
> > +	int high = INT_MAX;
> > +	int trip_temp, hysteresis;
> > +	int i, ret;
> > +
> > +	mutex_lock(&tz->lock);
> > +
> > +	if (!tz->ops->set_trips)
> > +		goto exit;
> > +
> 	if (!tz->ops->get_trip_hyst)
> 		goto exit;
> 
> otherwise, the patch looks good to me.

Agreed here, you need to check if get_trip_hyst is present before
calling it. Not all drivers have it.

Besides, I would prefer you to lock after the checks:

	if (!tz->ops->set_trips)
		return;

	if (!tz->ops->get_trip_hyst)
		return;

	mutex_lock(&tz->lock);

> 
> thanks,
> rui
> > +	for (i = 0; i < tz->trips; i++) {
> > +		int trip_low;
> > +
> > +		tz->ops->get_trip_temp(tz, i, &trip_temp);
> > +		tz->ops->get_trip_hyst(tz, i, &hysteresis);
> > +
> > +		trip_low = trip_temp - hysteresis;
> > +
> > +		if (trip_low < tz->temperature && trip_low > low)
> > +			low = trip_low;
> > +
> > +		if (trip_temp > tz->temperature && trip_temp < high)
> > +			high = trip_temp;
> > +	}
> > +
> > +	/* No need to change trip points */
> > +	if (tz->prev_low_trip == low && tz->prev_high_trip == high)
> > +		goto exit;
> > +
> > +	tz->prev_low_trip = low;
> > +	tz->prev_high_trip = high;
> > +
> > +	dev_dbg(&tz->device, "new temperature boundaries: %d < x <
> > %d\n",
> > +			low, high);
> > +
> > +	/*
> > +	?* Set a temperature window. When this window is left the
> > driver
> > +	?* must inform the thermal core via
> > thermal_zone_device_update.
> > +	?*/
> > +	ret = tz->ops->set_trips(tz, low, high);
> > +	if (ret)
> > +		dev_err(&tz->device, "Failed to set trips: %d\n",
> > ret);
> > +
> > +exit:
> > +	mutex_unlock(&tz->lock);
> > +}
> > +EXPORT_SYMBOL_GPL(thermal_zone_set_trips);
> > +
> > ?static void update_temperature(struct thermal_zone_device *tz)
> > ?{
> > ?	int temp, ret;
> > @@ -417,6 +467,8 @@ void thermal_zone_device_update(struct
> > thermal_zone_device *tz)
> > ?
> > ?	update_temperature(tz);
> > ?
> > +	thermal_zone_set_trips(tz);
> > +
> > ?	for (count = 0; count < tz->trips; count++)
> > ?		handle_thermal_trip(tz, count);
> > ?}
> > @@ -1175,6 +1227,8 @@ thermal_zone_device_register(const char *type,
> > int trips, int mask,
> > ?	tz->trips = trips;
> > ?	tz->passive_delay = passive_delay;
> > ?	tz->polling_delay = polling_delay;
> > +	tz->prev_low_trip = INT_MAX;
> > +	tz->prev_high_trip = -INT_MAX;
> > ?
> > ?	/* sys I/F */
> > ?	/* Add nodes that are always present via .groups */
> > diff --git a/drivers/thermal/thermal_sysfs.c
> > b/drivers/thermal/thermal_sysfs.c
> > index 743df50..33bbe30 100644
> > --- a/drivers/thermal/thermal_sysfs.c
> > +++ b/drivers/thermal/thermal_sysfs.c
> > @@ -219,6 +219,9 @@ trip_point_hyst_store(struct device *dev, struct
> > device_attribute *attr,
> > ?	ret = tz->ops->set_trip_hyst(tz, trip, temperature);
> > ?	mutex_unlock(&tz->lock);
> > ?
> > +	if (!ret)
> > +		thermal_zone_set_trips(tz);
> > +
> > ?	return ret ? ret : count;
> > ?}
> > ?
> > diff --git a/include/linux/thermal.h b/include/linux/thermal.h
> > index ea667b6..780e69d 100644
> > --- a/include/linux/thermal.h
> > +++ b/include/linux/thermal.h
> > @@ -99,6 +99,7 @@ struct thermal_zone_device_ops {
> > ?	int (*unbind) (struct thermal_zone_device *,
> > ?		???????struct thermal_cooling_device *);
> > ?	int (*get_temp) (struct thermal_zone_device *, int *);
> > +	int (*set_trips) (struct thermal_zone_device *, int, int);
> > ?	int (*get_mode) (struct thermal_zone_device *,
> > ?			?enum thermal_device_mode *);
> > ?	int (*set_mode) (struct thermal_zone_device *,
> > @@ -169,6 +170,10 @@ struct thermal_attr {
> > ? * @last_temperature:	previous temperature read
> > ? * @emul_temperature:	emulated temperature when using
> > CONFIG_THERMAL_EMULATION
> > ? * @passive:		1 if you've crossed a passive trip
> > point, 0 otherwise.
> > + * @prev_low_trip:	the low current temperature if you've
> > crossed a passive
> > +			trip point.
> > + * @prev_high_trip:	the above current temperature if you've
> > crossed a
> > +			passive trip point.
> > ? * @forced_passive:	If > 0, temperature at which to switch on
> > all ACPI
> > ? *			processor cooling devices.??Currently only
> > used by the
> > ? *			step-wise governor.
> > @@ -201,6 +206,8 @@ struct thermal_zone_device {
> > ?	int last_temperature;
> > ?	int emul_temperature;
> > ?	int passive;
> > +	int prev_low_trip;
> > +	int prev_high_trip;
> > ?	unsigned int forced_passive;
> > ?	atomic_t need_update;
> > ?	struct thermal_zone_device_ops *ops;
> > @@ -428,6 +435,7 @@ int thermal_zone_bind_cooling_device(struct
> > thermal_zone_device *, int,
> > ?int thermal_zone_unbind_cooling_device(struct thermal_zone_device *,
> > int,
> > ?				???????struct thermal_cooling_device
> > *);
> > ?void thermal_zone_device_update(struct thermal_zone_device *);
> > +void thermal_zone_set_trips(struct thermal_zone_device *);
> > ?
> > ?struct thermal_cooling_device *thermal_cooling_device_register(char
> > *, void *,
> > ?		const struct thermal_cooling_device_ops *);
> > @@ -477,6 +485,8 @@ static inline int
> > thermal_zone_unbind_cooling_device(
> > ?{ return -ENODEV; }
> > ?static inline void thermal_zone_device_update(struct
> > thermal_zone_device *tz)
> > ?{ }
> > +static inline void thermal_zone_set_trips(struct thermal_zone_device
> > *tz)
> > +{ }
> > ?static inline struct thermal_cooling_device *
> > ?thermal_cooling_device_register(char *type, void *devdata,
> > ?	const struct thermal_cooling_device_ops *ops)


From briannorris at chromium.org  Fri Jul  1 19:25:38 2016
From: briannorris at chromium.org (Brian Norris)
Date: Fri, 1 Jul 2016 19:25:38 -0700
Subject: [PATCH v4 1/2] Documentation: bindings: add dt doc for Rockchip
 PCIe controller
In-Reply-To: <57766995.9070302@arm.com>
References: <1467336290-11282-1-git-send-email-shawn.lin@rock-chips.com>
 <57766995.9070302@arm.com>
Message-ID: <20160702022537.GA29641@google.com>

Hi,

On Fri, Jul 01, 2016 at 02:01:09PM +0100, Marc Zyngier wrote:
> On 01/07/16 02:24, Shawn Lin wrote:
> > This patch adds a binding that describes the Rockchip PCIe controller
> > found on Rockchip SoCs PCIe interface.
> > 
> > Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
> > 
> > ---
> > 
> > Changes in v4:
> > - fix example of adding intermediate interrupt controller for pcie
> >   legacy interrrupt
> > 
> > Changes in v3:
> > - fix example dts code suggested by Rob and Marc
> > - remove driver's behaviour of regulator
> > 
> > Changes in v2:
> > - fix lots clk/reset stuff suggested by Heiko
> > - remove msi-parent and add msi-map suggested by Marc
> > - drop phy related stuff
> > - some others minor fixes
> > 
> >  .../devicetree/bindings/pci/rockchip-pcie.txt      | 91 ++++++++++++++++++++++
> >  1 file changed, 91 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/pci/rockchip-pcie.txt
> > 
> > diff --git a/Documentation/devicetree/bindings/pci/rockchip-pcie.txt b/Documentation/devicetree/bindings/pci/rockchip-pcie.txt
> > new file mode 100644
> > index 0000000..8092fc5
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/pci/rockchip-pcie.txt
> > @@ -0,0 +1,91 @@
> > +* Rockchip AXI PCIe Root Port Bridge DT description
> > +
> > +Required properties:
> > +- #address-cells: Address representation for root ports, set to <3>
> > +- #size-cells: Size representation for root ports, set to <2>
> > +- #interrupt-cells: specifies the number of cells needed to encode an
> > +		interrupt source. The value must be 1.

I think this propoerty should be in a separate section, since it's going
under a sub-node (not the main node). Right?

> > +- compatible: Should contain "rockchip,rk3399-pcie"
> > +- reg: Two register ranges as listed in the reg-names property
> > +- reg-names: Must include the following names
> > +	- "axi-base"
> > +	- "apb-base"
> > +- clocks: Must contain an entry for each entry in clock-names.
> > +		See ../clocks/clock-bindings.txt for details.
> > +- clock-names: Must include the following entries:
> > +	- "aclk"
> > +	- "aclk-perf"
> > +	- "hclk"
> > +	- "pm"
> > +- msi-map: Maps a Requester ID to an MSI controller and associated.
> > +		See ./pci-msi.txt
> > +- phys: From PHY bindings: Phandle for the Generic PHY for PCIe.
> > +- phy-names:  MUST be "pcie-phy".
> > +- interrupts: Three interrupt entries must be specified.
> > +- interrupt-names: Must include the following names
> > +	- "sys"
> > +	- "legacy"
> > +	- "client"
> > +- resets: Must contain five entries for each entry in reset-names.
> > +	   See ../reset/reset.txt for details.
> > +- reset-names: Must include the following names
> > +	- "core"
> > +	- "mgmt"
> > +	- "mgmt-sticky"
> > +	- "pipe"
> > +- pinctrl-names : The pin control state names
> > +- pinctrl-0: The "default" pinctrl state
> > +- interrupt-map-mask and interrupt-map: standard PCI properties
> > +- interrupt-controller: identifies the node as an interrupt controller

Same with this property.

> > +
> > +Optional Property:
> > +- ep-gpios: contain the entry for pre-reset gpio
> > +- num-lanes: number of lanes to use
> > +- vpcie3v3-supply: The phandle to the 3.3v regulator to use for pcie.
> > +- vpcie1v8-supply: The phandle to the 1.8v regulator to use for pcie.
> > +- vpcie0v9-supply: The phandle to the 0.9v regulator to use for pcie.

e.g., you might start a new section here describing how this controller
acts as an intermediate IRQ controller, and that the
interrupt-controller-related properties should be placed under a subnode.
(Did you also need an interrupt-parent property? I'm a little fuzzy on
the details myself, actually...)

> > +
> > +Example:
> > +
> > +pcie0: pcie at f8000000 {
> > +	compatible = "rockchip,rk3399-pcie";
> > +	#address-cells = <3>;
> > +	#size-cells = <2>;
> > +	clocks = <&cru ACLK_PCIE>, <&cru ACLK_PERF_PCIE>,
> > +		 <&cru PCLK_PCIE>, <&cru SCLK_PCIE_PM>;
> > +	clock-names = "aclk", "aclk-perf",
> > +		      "hclk", "pm";
> > +	bus-range = <0x0 0x1>;
> > +	interrupts = <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 50 IRQ_TYPE_LEVEL_HIGH>,
> > +		     <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>;
> > +	interrupt-names = "sys", "legacy", "client";
> > +	assigned-clocks = <&cru SCLK_PCIEPHY_REF>;
> > +	assigned-clock-parents = <&cru SCLK_PCIEPHY_REF100M>;
> > +	assigned-clock-rates = <100000000>;
> > +	ep-gpios = <&gpio3 13 GPIO_ACTIVE_HIGH>;
> > +	ranges = <0x83000000 0x0 0xfa000000 0x0 0xfa000000 0x0 0x600000
> > +		  0x81000000 0x0 0xfa600000 0x0 0xfa600000 0x0 0x100000>;
> > +	num-lanes = <4>;
> > +	msi-map = <0x0 &its 0x0 0x1000>;
> > +	reg = < 0x0 0xf8000000 0x0 0x2000000 >, < 0x0 0xfd000000 0x0 0x1000000 >;
> > +	reg-names = "axi-base", "apb-base";
> > +	resets = <&cru SRST_PCIE_CORE>, <&cru SRST_PCIE_MGMT>,
> > +		 <&cru SRST_PCIE_MGMT_STICKY>, <&cru SRST_PCIE_PIPE>;
> > +	reset-names = "core", "mgmt", "mgmt-sticky", "pipe";
> > +	phys = <&pcie_phy>;
> > +	phy-names = "pcie-phy";
> > +	pinctrl-names = "default";
> > +	pinctrl-0 = <&pcie_clkreq>;
> > +	#interrupt-cells = <1>;
> > +	interrupt-controller;
> 
> So the pcie node itself is an interrupt controller...
> 
> > +	interrupt-map-mask = <0 0 0 7>;
> > +	interrupt-map = <0 0 0 1 &pcie0_intc 1>,
> > +			<0 0 0 2 &pcie0_intc 2>,
> > +			<0 0 0 3 &pcie0_intc 3>,
> > +			<0 0 0 4 &pcie0_intc 4>;
> > +	pcie0_intc: interrupt-controller {
> > +		interrupt-controller;
> > +		#address-cells = <0>;
> > +		#interrupt-cells = <1>;
> > +	};
> 
> But there's also another here. I just don't understand how it works.

I believe the repeat of #interrupt-cells and interrupt-controller in the
pcie@ node is a mistake. They should only be in the 'interrupt-controller'
subnode. And in case this is what you're asking about... the subnode was
present in v1 but was removed at your request:

>From Marc, in https://patchwork.kernel.org/patch/9129183/:
> > +static int rockchip_pcie_init_irq_domain(struct rockchip_pcie_port *pp)
> > +{
> > +	struct device *dev = pp->dev;
> > +	struct device_node *node = dev->of_node;
> > +	struct device_node *pcie_intc_node =  of_get_next_child(node, NULL);
> 
> That's really ugly, as it depends on the layout of your DT.
> 
> > +
> > +	if (!pcie_intc_node) {
> > +		dev_err(dev, "No PCIe Intc node found\n");
> > +		return PTR_ERR(pcie_intc_node);
> > +	}
> > +	pp->irq_domain = irq_domain_add_linear(pcie_intc_node, 4,
> > +					       &intx_domain_ops, pp);
> 
> Why can't you just register your host controller as the interrupt
> controller? You don't need an intermediate node for that.

But then in v3, Arnd concluded it actually *should* be used:

>From Arnd, in https://patchwork.kernel.org/patch/9179763/:
> On Thursday, June 16, 2016 4:01:12 PM CEST Wenrui Li wrote:
> > ? 2016/6/16 15:00, Arnd Bergmann ??:
> > > On Thursday, June 16, 2016 9:50:21 AM CEST Shawn Lin wrote:
> > >
> > >> +    reset-names = "core", "mgmt", "mgmt-sticky", "pipe";
> > >> +    phys = <&pcie_phy>;
> > >> +    phy-names = "pcie-phy";
> > >> +    pinctrl-names = "default";
> > >> +    pinctrl-0 = <&pcie_clkreq>;
> > >> +    #interrupt-cells = <1>;
> > >> +    interrupt-controller;
> > >> +    interrupt-map-mask = <0 0 0 7>;
> > >> +    interrupt-map = <0 0 0 1 &pcie0 1>,
> > >> +                    <0 0 0 2 &pcie0 2>,
> > >> +                    <0 0 0 3 &pcie0 3>,
> > >> +                    <0 0 0 4 &pcie0 4>;
> > >> +};
> > >>
> > >
> > > One thing that came up in the review of the new Marvell PCIe driver is that it's
> > > most likely invalid for a device node to have both "interrupt-controller"
> > > and "interrupt-map" properties. I originally thought this was a nice way to
> > > handle embedded irqchips within the PCIe host, but it only really works
> > > by coincidence with the current kernel, and only as long as the hwirq number
> > > of the irqchip matches the integer representation of the irq line in the root
> > > bridge (which it does in the example above).
> > >
> > > For that driver we concluded that it would be less of a hack to have the
> > > irqchip as a child node of the PCIe host after all (just not with
> > > device_type="pci" of course), and that makes the translation work as
> > > expected.
> > >
> > >       Arnd
> > >
> > 
> > Original driver have an irqchip as child node. But Marc suggested don't 
> > need an intermediate node here.
> > Now the conclusion is to retain the child node?
> 
> That is at least my view of the situation, sorry for the mixed messages
> you have been getting. Marc, Rob, do you agree with my finding?

Rob and others agreed with the subnode. So, I hope you don't disagree in
principle, and are only confused by the duplicate properties?

Regards,
Brian


From edubezval at gmail.com  Fri Jul  1 19:41:54 2016
From: edubezval at gmail.com (Eduardo Valentin)
Date: Fri, 1 Jul 2016 19:41:54 -0700
Subject: [PATCH v6 3/5] thermal: streamline get_trend callbacks
In-Reply-To: <1466584925-4829-4-git-send-email-wxt@rock-chips.com>
References: <1466584925-4829-1-git-send-email-wxt@rock-chips.com>
 <1466584925-4829-4-git-send-email-wxt@rock-chips.com>
Message-ID: <20160702024153.GA26067@localhost.localdomain>

On Wed, Jun 22, 2016 at 04:42:03PM +0800, Caesar Wang wrote:
> From: Sascha Hauer <s.hauer at pengutronix.de>
> 
> The .get_trend callback in struct thermal_zone_device_ops has
> the prototype:
>         int (*get_trend) (struct thermal_zone_device *, int,
>                           enum thermal_trend *);
> whereas the .get_trend callback in struct thermal_zone_of_device_ops
> has:
>         int (*get_trend)(void *, long *);
> 
> Streamline both prototypes and add the trip argument to the OF callback
> aswell and use enum thermal_trend * instead of an integer pointer.
> 
> While the OF prototype may be the better one, this should be decided at
> framework level and not on OF level.
> 
> Signed-off-by: Sascha Hauer <s.hauer at pengutronix.de>
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
> Cc: Zhang Rui <rui.zhang at intel.com>
> Cc: Eduardo Valentin <edubezval at gmail.com>
> Cc: linux-pm at vger.kernel.org
> Reviewed-by: Keerthy <j-keerthy at ti.com>

I had to append the following diff, because I merged a new qsens driver:


diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c
index b182272..446f70b 100644
--- a/drivers/thermal/qcom/tsens.c
+++ b/drivers/thermal/qcom/tsens.c
@@ -29,13 +29,13 @@ static int tsens_get_temp(void *data, int *temp)
        return tmdev->ops->get_temp(tmdev, s->id, temp);
 }
 
-static int tsens_get_trend(void *data, long *temp)
+static int tsens_get_trend(void *p, int trip, enum thermal_trend *trend)
 {
-       const struct tsens_sensor *s = data;
+       const struct tsens_sensor *s = p;
        struct tsens_device *tmdev = s->tmdev;
 
        if (tmdev->ops->get_trend)
-               return tmdev->ops->get_trend(tmdev, s->id, temp);
+               return  tmdev->ops->get_trend(tmdev, s->id, trend);
 
        return -ENOTSUPP;
 }

> 
> ---
> 
> Changes in v6:
> - as the Keerthy comments on https://patchwork.kernel.org/patch/9157913/,
>   change the get_trend for of_thermal.c.
> 
> Changes in v5: None
> Changes in v4: None
> Changes in v3: None
> Changes in v2: None
> 
>  drivers/thermal/of-thermal.c                       | 16 +-------------
>  drivers/thermal/ti-soc-thermal/ti-thermal-common.c | 25 +++++++---------------
>  include/linux/thermal.h                            |  2 +-
>  3 files changed, 10 insertions(+), 33 deletions(-)
> 
> diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c
> index 2d2a06f..20822ab 100644
> --- a/drivers/thermal/of-thermal.c
> +++ b/drivers/thermal/of-thermal.c
> @@ -202,25 +202,11 @@ static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip,
>  				enum thermal_trend *trend)
>  {
>  	struct __thermal_zone *data = tz->devdata;
> -	long dev_trend;
> -	int r;
>  
>  	if (!data->ops->get_trend)
>  		return -EINVAL;
>  
> -	r = data->ops->get_trend(data->sensor_data, &dev_trend);
> -	if (r)
> -		return r;
> -
> -	/* TODO: These intervals might have some thresholds, but in core code */
> -	if (dev_trend > 0)
> -		*trend = THERMAL_TREND_RAISING;
> -	else if (dev_trend < 0)
> -		*trend = THERMAL_TREND_DROPPING;
> -	else
> -		*trend = THERMAL_TREND_STABLE;
> -
> -	return 0;
> +	return data->ops->get_trend(data->sensor_data, trip, trend);
>  }
>  
>  static int of_thermal_bind(struct thermal_zone_device *thermal,
> diff --git a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c
> index 15c0a9a..4a6757c 100644
> --- a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c
> +++ b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c
> @@ -239,7 +239,7 @@ static int ti_thermal_get_trip_temp(struct thermal_zone_device *thermal,
>  	return 0;
>  }
>  
> -static int __ti_thermal_get_trend(void *p, long *trend)
> +static int __ti_thermal_get_trend(void *p, int trip, enum thermal_trend *trend)
>  {
>  	struct ti_thermal_data *data = p;
>  	struct ti_bandgap *bgp;
> @@ -252,22 +252,6 @@ static int __ti_thermal_get_trend(void *p, long *trend)
>  	if (ret)
>  		return ret;
>  
> -	*trend = tr;
> -
> -	return 0;
> -}
> -
> -/* Get the temperature trend callback functions for thermal zone */
> -static int ti_thermal_get_trend(struct thermal_zone_device *thermal,
> -				int trip, enum thermal_trend *trend)
> -{
> -	int ret;
> -	long tr;
> -
> -	ret = __ti_thermal_get_trend(thermal->devdata, &tr);
> -	if (ret)
> -		return ret;
> -
>  	if (tr > 0)
>  		*trend = THERMAL_TREND_RAISING;
>  	else if (tr < 0)
> @@ -278,6 +262,13 @@ static int ti_thermal_get_trend(struct thermal_zone_device *thermal,
>  	return 0;
>  }
>  
> +/* Get the temperature trend callback functions for thermal zone */
> +static int ti_thermal_get_trend(struct thermal_zone_device *thermal,
> +				int trip, enum thermal_trend *trend)
> +{
> +	return __ti_thermal_get_trend(thermal->devdata, trip, trend);
> +}
> +
>  /* Get critical temperature callback functions for thermal zone */
>  static int ti_thermal_get_crit_temp(struct thermal_zone_device *thermal,
>  				    int *temp)
> diff --git a/include/linux/thermal.h b/include/linux/thermal.h
> index 2440b8d..d3ce3a5 100644
> --- a/include/linux/thermal.h
> +++ b/include/linux/thermal.h
> @@ -352,7 +352,7 @@ struct thermal_genl_event {
>   */
>  struct thermal_zone_of_device_ops {
>  	int (*get_temp)(void *, int *);
> -	int (*get_trend)(void *, long *);
> +	int (*get_trend)(void *, int, enum thermal_trend *);
>  	int (*set_trips)(void *, int, int);
>  	int (*set_emul_temp)(void *, int);
>  	int (*set_trip_temp)(void *, int, int);
> -- 
> 1.9.1
> 


From william.wu at rock-chips.com  Fri Jul  1 20:41:42 2016
From: william.wu at rock-chips.com (William Wu)
Date: Sat, 02 Jul 2016 11:41:42 +0800
Subject: [PATCH v2] arm64: dts: rockchip: add the power domain node for
 rk3399
In-Reply-To: <57771A39.8060606@rock-chips.com>
References: <1467347575-11806-1-git-send-email-wxt@rock-chips.com>
 <CAD=FV=U0FU5P+seYYmJNZmcV1r-N6_UHtMW5LuVGM-NyPER8BQ@mail.gmail.com>
 <57771A39.8060606@rock-chips.com>
Message-ID: <577737F6.7000008@rock-chips.com>

Dear Caesar & Doug,


On 07/02/2016 09:34 AM, Caesar Wang wrote:
>
> On 2016?07?02? 00:56, Doug Anderson wrote:
>> Caesar
>>
>> On Thu, Jun 30, 2016 at 9:32 PM, Caesar Wang <wxt at rock-chips.com> wrote:
>>> From: Elaine Zhang <zhangqing at rock-chips.com>
>>>
>>> In order to meet low power requirements, a power management unit
>>> (PMU) is
>>> designed for controlling power resources in RK3399. The RK3399 PMU is
>>> dedicated for managing the power of the whole chip.
>>>
>>> 1. add pd node for RK3399 Soc
>>> 2. create power domain tree
>>> 3. add qos node for domain
>>>
>>> From the DT/binds and driver can get more detail information:
>>> The driver:
>>> drivers/soc/rockchip/pm_domains.c
>>> The document:
>>> Documentation/devicetree/bindings/soc/rockchip/power_domain.txt
>>> ---
>>>
>>> Changes in v2:
>>> - As Doug/Heiko commnets on
>>> https://patchwork.kernel.org/patch/9206415/.
>>> drop the debugfs-dump and Add the comments for alphabetical order.
>>>
>>> Note: As the TRM lists many voltage domains and power domains, then
>>> in actual we just need control some domains for driver.
>>> Due to some domains (e.g. emmc, usb, core)...We can't turn off it on
>>> bootup.
>> I'm curious: why can't you turn off USB power domains if a board
>> doesn't usb USB? ...or GMAC on boards that don't use Ethernet? ...or
>> eDP on boards that don't use EDP?
>>
>> Maybe the driver for these things isn't ready to handle power domains
>> yet so that's why they are left out for now?
>>
>> It seems like GMAC could at least be added because we don't even have
>> the GMAC listed in the current device tree so therefore there can't be
>> any users of it. When it's added we can make sure that the power
>> domains are added.
>
> At least, the gmac had been supported in rockchip inside.
> That's seem the gmac driver can't handle the power domain enough.
> -- 
>
> Frankly, I'm no test the GMAC?then this patch should support most of
> devices.
> We can send another patch to support it if someone?usb/gmac) can
> handle the power domain.
>
>>
>> I think in the current upstream dtsi file there is also no xhci node,
>> so does that mean you could add the USB3 power domain?
>>
>>
>
> willam at RK, what's think of it?
RK3399 support XHCI controller, but it's integrated in DWC3 IP core,
and we don't need to add xhci node separately, but just add dwc3
node like this:
usbdrd3_0: usb at fe800000 {
compatible = "rockchip,rk3399-dwc3";
......
ranges;
status = "disabled";
usbdrd_dwc3_0: dwc3 at fe800000 {
compatible = "snps,dwc3";
reg = <0x0 0xfe800000 0x0 0x100000>;
interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>;
dr_mode = "otg";
......
};
};

And I'm working on DWC3 upstream.
RK3399 have two usb power domain (pd_perihp usb2 and pd_usb3),
usb driver isn't ready to handle power domain for the time being,
but we're trying to support usb power domain control.

Best regards,
William Wu
>
>>> Signed-off-by: Elaine Zhang <zhangqing at rock-chips.com>
>>> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
>>> Cc: linux-arm-kernel at lists.infradead.org
>>> Cc: linux-rockchip at lists.infradead.org
>>> Cc: Heiko Stuebner <heiko at sntech.de>
>>>
>>> ---
>>> arch/arm64/boot/dts/rockchip/rk3399.dtsi | 179
>>> +++++++++++++++++++++++++++++++
>>> 1 file changed, 179 insertions(+)
>>>
>>> diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
>>> b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
>>> index a6dd623..103e185 100644
>>> --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
>>> +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
>>> @@ -45,6 +45,7 @@
>>> #include <dt-bindings/interrupt-controller/arm-gic.h>
>>> #include <dt-bindings/interrupt-controller/irq.h>
>>> #include <dt-bindings/pinctrl/rockchip.h>
>>> +#include <dt-bindings/power/rk3399-power.h>
>>> #include <dt-bindings/thermal/thermal.h>
>>>
>>> / {
>>> @@ -594,6 +595,184 @@
>>> status = "disabled";
>>> };
>>>
>>> + qos_hdcp: qos_hdcp at ffa90000 {
>>> + compatible = "syscon";
>>> + reg = <0x0 0xffa90000 0x0 0x20>;
>>> + };
>>> +
>>> + qos_iep: qos_iep at ffa98000 {
>>> + compatible = "syscon";
>>> + reg = <0x0 0xffa98000 0x0 0x20>;
>>> + };
>>> +
>>> + qos_isp0_m0: qos_isp0_m0 at ffaa0000 {
>>> + compatible = "syscon";
>>> + reg = <0x0 0xffaa0000 0x0 0x20>;
>>> + };
>>> +
>>> + qos_isp0_m1: qos_isp0_m1 at ffaa0080 {
>>> + compatible = "syscon";
>>> + reg = <0x0 0xffaa0080 0x0 0x20>;
>>> + };
>>> +
>>> + qos_isp1_m0: qos_isp1_m0 at ffaa8000 {
>>> + compatible = "syscon";
>>> + reg = <0x0 0xffaa8000 0x0 0x20>;
>>> + };
>>> +
>>> + qos_isp1_m1: qos_isp1_m1 at ffaa8080 {
>>> + compatible = "syscon";
>>> + reg = <0x0 0xffaa8080 0x0 0x20>;
>>> + };
>>> +
>>> + qos_rga_r: qos_rga_r at ffab0000 {
>>> + compatible = "syscon";
>>> + reg = <0x0 0xffab0000 0x0 0x20>;
>>> + };
>>> +
>>> + qos_rga_w: qos_rga_w at ffab0080 {
>>> + compatible = "syscon";
>>> + reg = <0x0 0xffab0080 0x0 0x20>;
>>> + };
>>> +
>>> + qos_video_m0: qos_video_m0 at ffab8000 {
>>> + compatible = "syscon";
>>> + reg = <0x0 0xffab8000 0x0 0x20>;
>>> + };
>>> +
>>> + qos_video_m1_r: qos_video_m1_r at ffac0000 {
>>> + compatible = "syscon";
>>> + reg = <0x0 0xffac0000 0x0 0x20>;
>>> + };
>>> +
>>> + qos_video_m1_w: qos_video_m1_w at ffac0080 {
>>> + compatible = "syscon";
>>> + reg = <0x0 0xffac0080 0x0 0x20>;
>>> + };
>>> +
>>> + qos_vop_big_r: qos_vop_big_r at ffac8000 {
>>> + compatible = "syscon";
>>> + reg = <0x0 0xffac8000 0x0 0x20>;
>>> + };
>>> +
>>> + qos_vop_big_w: qos_vop_big_w at ffac8080 {
>>> + compatible = "syscon";
>>> + reg = <0x0 0xffac8080 0x0 0x20>;
>>> + };
>>> +
>>> + qos_vop_little: qos_vop_little at ffad0000 {
>>> + compatible = "syscon";
>>> + reg = <0x0 0xffad0000 0x0 0x20>;
>>> + };
>>> +
>>> + qos_gpu: qos_gpu at ffae0000 {
>>> + compatible = "syscon";
>>> + reg = <0x0 0xffae0000 0x0 0x20>;
>>> + };
>>> +
>>> + pmu: power-management at ff310000 {
>>> + compatible = "rockchip,rk3399-pmu", "syscon", "simple-mfd";
>>> + reg = <0x0 0xff310000 0x0 0x1000>;
>>> +
>>> + /*
>>> + * Note: RK3399 supports 6 voltage domains including VD_CORE_L,
>>> + * VD_CORE_B, VD_CENTER, VD_GPU, VD_LOGIC and VD_PMU.
>>> + * Some of the power domains are grouped together for every
>>> + * voltage domain.
>>> + * The detail contents as below.
>>> + */
>>> + power: power-controller {
>>> + status = "okay";
>>> + compatible = "rockchip,rk3399-power-controller";
>>> + #power-domain-cells = <1>;
>>> + #address-cells = <1>;
>>> + #size-cells = <0>;
>>> +
>>> + /* These power domains are grouped by VD_LOGIC */
>>> + pd_vio at RK3399_PD_VIO {
>>> + reg = <RK3399_PD_VIO>;
>>> + #address-cells = <1>;
>>> + #size-cells = <0>;
>>> +
>>> + pd_isp0 at RK3399_PD_ISP0 {
>>> + reg = <RK3399_PD_ISP0>;
>>> + clocks = <&cru ACLK_ISP0>,
>>> + <&cru HCLK_ISP0>;
>>> + pm_qos = <&qos_isp0_m0>,
>>> + <&qos_isp0_m1>;
>>> + };
>>> + pd_isp1 at RK3399_PD_ISP1 {
>>> + reg = <RK3399_PD_ISP1>;
>>> + clocks = <&cru ACLK_ISP1>,
>>> + <&cru HCLK_ISP1>;
>>> + pm_qos = <&qos_isp1_m0>,
>>> + <&qos_isp1_m1>;
>>> + };
>>> + pd_vo at RK3399_PD_VO {
>>> + reg = <RK3399_PD_VO>;
>>> + #address-cells = <1>;
>>> + #size-cells = <0>;
>>> +
>>> + pd_vopb at RK3399_PD_VOPB {
>>> + reg = <RK3399_PD_VOPB>;
>>> + clocks = <&cru ACLK_VOP0>,
>>> + <&cru HCLK_VOP0>;
>>> + pm_qos = <&qos_vop_big_r>,
>>> + <&qos_vop_big_w>;
>>> + };
>>> + pd_vopl at RK3399_PD_VOP {
>>> + reg = <RK3399_PD_VOPL>;
>>> + clocks = <&cru ACLK_VOP1>,
>>> + <&cru HCLK_VOP1>;
>>> + pm_qos = <&qos_vop_little>;
>>> + };
>>> + };
>>> + pd_hdcp at RK3399_PD_HDCP {
>>> + reg = <RK3399_PD_HDCP>;
>>> + clocks = <&cru ACLK_HDCP>,
>>> + <&cru HCLK_HDCP>,
>>> + <&cru PCLK_HDCP>;
>>> + pm_qos = <&qos_hdcp>;
>>> + };
>> HDCP sorts earlier than ISP0, so should be above that. Basically:
>> anything in the same area should be sorted alphabetically.
>
> The sort is follow up the TRM. At least I'm according to every voltage
> domain.
>
>>
>>> + };
>>> +
>>> + /* These power domains are grouped by VD_CENTER */
>>> + pd_vcodec at RK3399_PD_VCODEC {
>>> + reg = <RK3399_PD_VCODEC>;
>>> + clocks = <&cru ACLK_VCODEC>,
>>> + <&cru HCLK_VCODEC>;
>>> + pm_qos = <&qos_video_m0>;
>>> + };
>>> + pd_vdu at RK3399_PD_VDU {
>>> + reg = <RK3399_PD_VDU>;
>>> + clocks = <&cru ACLK_VDU>,
>>> + <&cru HCLK_VDU>;
>>> + pm_qos = <&qos_video_m1_r>,
>>> + <&qos_video_m1_w>;
>>> + };
>>> + pd_rga at RK3399_PD_RGA {
>>> + reg = <RK3399_PD_RGA>;
>>> + clocks = <&cru ACLK_RGA>,
>>> + <&cru HCLK_RGA>;
>>> + pm_qos = <&qos_rga_r>,
>>> + <&qos_rga_w>;
>>> + };
>>> + pd_iep at RK3399_PD_IE {
>>> + reg = <RK3399_PD_IEP>;
>>> + clocks = <&cru ACLK_IEP>,
>>> + <&cru HCLK_IEP>;
>>> + pm_qos = <&qos_iep>;
>>> + };
>> IE sorts before RGA. ...and both come before VCODEC / VDU.
>
> Ditto
>>
>>> +
>>> + /* These power domains are grouped by VD_GPU */
>>> + pd_gpu at RK3399_PD_GPU {
>>> + reg = <RK3399_PD_GPU>;
>>> + clocks = <&cru ACLK_GPU>;
>>> + pm_qos = <&qos_gpu>;
>>> + };
>>> + };
>>> + };
>>> +
>>> pmugrf: syscon at ff320000 {
>>> compatible = "rockchip,rk3399-pmugrf", "syscon", "simple-mfd";
>>> reg = <0x0 0xff320000 0x0 0x1000>;
>>> -- 
>>> 1.9.1
>>>
>> _______________________________________________
>> Linux-rockchip mailing list
>> Linux-rockchip at lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-rockchip
>
>




From broonie at kernel.org  Sat Jul  2 00:59:39 2016
From: broonie at kernel.org (Mark Brown)
Date: Sat, 2 Jul 2016 09:59:39 +0200
Subject: [PATCH] regulator: pwm: Fix regulator ramp delay for continuous
 mode
In-Reply-To: <CAD=FV=WKR_Fq6o34886RCY-T-6AKdRqFz9H1aTiRq7Zu=o8TWA@mail.gmail.com>
References: <1467089591-7631-1-git-send-email-dianders@chromium.org>
 <20160701080614.GA9196@sirena.org.uk>
 <CAD=FV=WKR_Fq6o34886RCY-T-6AKdRqFz9H1aTiRq7Zu=o8TWA@mail.gmail.com>
Message-ID: <20160702075939.GD6247@sirena.org.uk>

On Fri, Jul 01, 2016 at 01:17:48PM -0700, Doug Anderson wrote:

> Anyway, let me know if you want me to post the untested, rebased
> patch...  Personally I'd prefer to wait for Boris's patches and the
> land the tested version.

We've got some boards in kernelci.org using PWM regulators I think, or
perhaps someone else can test.  It does seem like a real fix so...
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160702/14502d9e/attachment.sig>

From marc.zyngier at arm.com  Sat Jul  2 02:20:22 2016
From: marc.zyngier at arm.com (Marc Zyngier)
Date: Sat, 2 Jul 2016 10:20:22 +0100
Subject: [PATCH v4 1/2] Documentation: bindings: add dt doc for Rockchip
 PCIe controller
In-Reply-To: <20160702022537.GA29641@google.com>
References: <1467336290-11282-1-git-send-email-shawn.lin@rock-chips.com>
 <57766995.9070302@arm.com> <20160702022537.GA29641@google.com>
Message-ID: <20160702102022.144313fb@arm.com>

On Fri, 1 Jul 2016 19:25:38 -0700
Brian Norris <briannorris at chromium.org> wrote:

> Hi,
> 
> On Fri, Jul 01, 2016 at 02:01:09PM +0100, Marc Zyngier wrote:
> > On 01/07/16 02:24, Shawn Lin wrote:
> > > This patch adds a binding that describes the Rockchip PCIe controller
> > > found on Rockchip SoCs PCIe interface.
> > > 
> > > Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
> > > 
> > > ---
> > > 
> > > Changes in v4:
> > > - fix example of adding intermediate interrupt controller for pcie
> > >   legacy interrrupt
> > > 
> > > Changes in v3:
> > > - fix example dts code suggested by Rob and Marc
> > > - remove driver's behaviour of regulator
> > > 
> > > Changes in v2:
> > > - fix lots clk/reset stuff suggested by Heiko
> > > - remove msi-parent and add msi-map suggested by Marc
> > > - drop phy related stuff
> > > - some others minor fixes
> > > 
> > >  .../devicetree/bindings/pci/rockchip-pcie.txt      | 91 ++++++++++++++++++++++
> > >  1 file changed, 91 insertions(+)
> > >  create mode 100644 Documentation/devicetree/bindings/pci/rockchip-pcie.txt
> > > 
> > > diff --git a/Documentation/devicetree/bindings/pci/rockchip-pcie.txt b/Documentation/devicetree/bindings/pci/rockchip-pcie.txt
> > > new file mode 100644
> > > index 0000000..8092fc5
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/pci/rockchip-pcie.txt
> > > @@ -0,0 +1,91 @@
> > > +* Rockchip AXI PCIe Root Port Bridge DT description
> > > +
> > > +Required properties:
> > > +- #address-cells: Address representation for root ports, set to <3>
> > > +- #size-cells: Size representation for root ports, set to <2>
> > > +- #interrupt-cells: specifies the number of cells needed to encode an
> > > +		interrupt source. The value must be 1.
> 
> I think this propoerty should be in a separate section, since it's going
> under a sub-node (not the main node). Right?
> 
> > > +- compatible: Should contain "rockchip,rk3399-pcie"
> > > +- reg: Two register ranges as listed in the reg-names property
> > > +- reg-names: Must include the following names
> > > +	- "axi-base"
> > > +	- "apb-base"
> > > +- clocks: Must contain an entry for each entry in clock-names.
> > > +		See ../clocks/clock-bindings.txt for details.
> > > +- clock-names: Must include the following entries:
> > > +	- "aclk"
> > > +	- "aclk-perf"
> > > +	- "hclk"
> > > +	- "pm"
> > > +- msi-map: Maps a Requester ID to an MSI controller and associated.
> > > +		See ./pci-msi.txt
> > > +- phys: From PHY bindings: Phandle for the Generic PHY for PCIe.
> > > +- phy-names:  MUST be "pcie-phy".
> > > +- interrupts: Three interrupt entries must be specified.
> > > +- interrupt-names: Must include the following names
> > > +	- "sys"
> > > +	- "legacy"
> > > +	- "client"
> > > +- resets: Must contain five entries for each entry in reset-names.
> > > +	   See ../reset/reset.txt for details.
> > > +- reset-names: Must include the following names
> > > +	- "core"
> > > +	- "mgmt"
> > > +	- "mgmt-sticky"
> > > +	- "pipe"
> > > +- pinctrl-names : The pin control state names
> > > +- pinctrl-0: The "default" pinctrl state
> > > +- interrupt-map-mask and interrupt-map: standard PCI properties
> > > +- interrupt-controller: identifies the node as an interrupt controller
> 
> Same with this property.
> 
> > > +
> > > +Optional Property:
> > > +- ep-gpios: contain the entry for pre-reset gpio
> > > +- num-lanes: number of lanes to use
> > > +- vpcie3v3-supply: The phandle to the 3.3v regulator to use for pcie.
> > > +- vpcie1v8-supply: The phandle to the 1.8v regulator to use for pcie.
> > > +- vpcie0v9-supply: The phandle to the 0.9v regulator to use for pcie.
> 
> e.g., you might start a new section here describing how this controller
> acts as an intermediate IRQ controller, and that the
> interrupt-controller-related properties should be placed under a subnode.
> (Did you also need an interrupt-parent property? I'm a little fuzzy on
> the details myself, actually...)
> 
> > > +
> > > +Example:
> > > +
> > > +pcie0: pcie at f8000000 {
> > > +	compatible = "rockchip,rk3399-pcie";
> > > +	#address-cells = <3>;
> > > +	#size-cells = <2>;
> > > +	clocks = <&cru ACLK_PCIE>, <&cru ACLK_PERF_PCIE>,
> > > +		 <&cru PCLK_PCIE>, <&cru SCLK_PCIE_PM>;
> > > +	clock-names = "aclk", "aclk-perf",
> > > +		      "hclk", "pm";
> > > +	bus-range = <0x0 0x1>;
> > > +	interrupts = <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 50 IRQ_TYPE_LEVEL_HIGH>,
> > > +		     <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>;
> > > +	interrupt-names = "sys", "legacy", "client";
> > > +	assigned-clocks = <&cru SCLK_PCIEPHY_REF>;
> > > +	assigned-clock-parents = <&cru SCLK_PCIEPHY_REF100M>;
> > > +	assigned-clock-rates = <100000000>;
> > > +	ep-gpios = <&gpio3 13 GPIO_ACTIVE_HIGH>;
> > > +	ranges = <0x83000000 0x0 0xfa000000 0x0 0xfa000000 0x0 0x600000
> > > +		  0x81000000 0x0 0xfa600000 0x0 0xfa600000 0x0 0x100000>;
> > > +	num-lanes = <4>;
> > > +	msi-map = <0x0 &its 0x0 0x1000>;
> > > +	reg = < 0x0 0xf8000000 0x0 0x2000000 >, < 0x0 0xfd000000 0x0 0x1000000 >;
> > > +	reg-names = "axi-base", "apb-base";
> > > +	resets = <&cru SRST_PCIE_CORE>, <&cru SRST_PCIE_MGMT>,
> > > +		 <&cru SRST_PCIE_MGMT_STICKY>, <&cru SRST_PCIE_PIPE>;
> > > +	reset-names = "core", "mgmt", "mgmt-sticky", "pipe";
> > > +	phys = <&pcie_phy>;
> > > +	phy-names = "pcie-phy";
> > > +	pinctrl-names = "default";
> > > +	pinctrl-0 = <&pcie_clkreq>;
> > > +	#interrupt-cells = <1>;
> > > +	interrupt-controller;
> > 
> > So the pcie node itself is an interrupt controller...
> > 
> > > +	interrupt-map-mask = <0 0 0 7>;
> > > +	interrupt-map = <0 0 0 1 &pcie0_intc 1>,
> > > +			<0 0 0 2 &pcie0_intc 2>,
> > > +			<0 0 0 3 &pcie0_intc 3>,
> > > +			<0 0 0 4 &pcie0_intc 4>;
> > > +	pcie0_intc: interrupt-controller {
> > > +		interrupt-controller;
> > > +		#address-cells = <0>;
> > > +		#interrupt-cells = <1>;
> > > +	};
> > 
> > But there's also another here. I just don't understand how it works.
> 
> I believe the repeat of #interrupt-cells and interrupt-controller in the
> pcie@ node is a mistake. They should only be in the 'interrupt-controller'
> subnode. And in case this is what you're asking about... the subnode was
> present in v1 but was removed at your request:
> 
> From Marc, in https://patchwork.kernel.org/patch/9129183/:
> > > +static int rockchip_pcie_init_irq_domain(struct rockchip_pcie_port *pp)
> > > +{
> > > +	struct device *dev = pp->dev;
> > > +	struct device_node *node = dev->of_node;
> > > +	struct device_node *pcie_intc_node =  of_get_next_child(node, NULL);
> > 
> > That's really ugly, as it depends on the layout of your DT.
> > 
> > > +
> > > +	if (!pcie_intc_node) {
> > > +		dev_err(dev, "No PCIe Intc node found\n");
> > > +		return PTR_ERR(pcie_intc_node);
> > > +	}
> > > +	pp->irq_domain = irq_domain_add_linear(pcie_intc_node, 4,
> > > +					       &intx_domain_ops, pp);
> > 
> > Why can't you just register your host controller as the interrupt
> > controller? You don't need an intermediate node for that.
> 
> But then in v3, Arnd concluded it actually *should* be used:
> 
> From Arnd, in https://patchwork.kernel.org/patch/9179763/:
> > On Thursday, June 16, 2016 4:01:12 PM CEST Wenrui Li wrote:
> > > ? 2016/6/16 15:00, Arnd Bergmann ??:
> > > > On Thursday, June 16, 2016 9:50:21 AM CEST Shawn Lin wrote:
> > > >
> > > >> +    reset-names = "core", "mgmt", "mgmt-sticky", "pipe";
> > > >> +    phys = <&pcie_phy>;
> > > >> +    phy-names = "pcie-phy";
> > > >> +    pinctrl-names = "default";
> > > >> +    pinctrl-0 = <&pcie_clkreq>;
> > > >> +    #interrupt-cells = <1>;
> > > >> +    interrupt-controller;
> > > >> +    interrupt-map-mask = <0 0 0 7>;
> > > >> +    interrupt-map = <0 0 0 1 &pcie0 1>,
> > > >> +                    <0 0 0 2 &pcie0 2>,
> > > >> +                    <0 0 0 3 &pcie0 3>,
> > > >> +                    <0 0 0 4 &pcie0 4>;
> > > >> +};
> > > >>
> > > >
> > > > One thing that came up in the review of the new Marvell PCIe driver is that it's
> > > > most likely invalid for a device node to have both "interrupt-controller"
> > > > and "interrupt-map" properties. I originally thought this was a nice way to
> > > > handle embedded irqchips within the PCIe host, but it only really works
> > > > by coincidence with the current kernel, and only as long as the hwirq number
> > > > of the irqchip matches the integer representation of the irq line in the root
> > > > bridge (which it does in the example above).
> > > >
> > > > For that driver we concluded that it would be less of a hack to have the
> > > > irqchip as a child node of the PCIe host after all (just not with
> > > > device_type="pci" of course), and that makes the translation work as
> > > > expected.
> > > >
> > > >       Arnd
> > > >
> > > 
> > > Original driver have an irqchip as child node. But Marc suggested don't 
> > > need an intermediate node here.
> > > Now the conclusion is to retain the child node?
> > 
> > That is at least my view of the situation, sorry for the mixed messages
> > you have been getting. Marc, Rob, do you agree with my finding?
> 
> Rob and others agreed with the subnode. So, I hope you don't disagree in
> principle, and are only confused by the duplicate properties?

Indeed. What I object to is the messed-up example, which in turns makes
the kernel code harder to review.

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny.


From boris.brezillon at free-electrons.com  Sat Jul  2 08:47:52 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Sat, 2 Jul 2016 17:47:52 +0200
Subject: [PATCH] regulator: pwm: Fix regulator ramp delay for continuous
 mode
In-Reply-To: <CAD=FV=WKR_Fq6o34886RCY-T-6AKdRqFz9H1aTiRq7Zu=o8TWA@mail.gmail.com>
References: <1467089591-7631-1-git-send-email-dianders@chromium.org>
 <20160701080614.GA9196@sirena.org.uk>
 <CAD=FV=WKR_Fq6o34886RCY-T-6AKdRqFz9H1aTiRq7Zu=o8TWA@mail.gmail.com>
Message-ID: <20160702174752.7e893ac2@bbrezillon>

+Thierry

Hi Doug,

On Fri, 1 Jul 2016 13:17:48 -0700
Doug Anderson <dianders at chromium.org> wrote:

> Mark,
> 
> On Fri, Jul 1, 2016 at 1:06 AM, Mark Brown <broonie at kernel.org> wrote:
> > On Mon, Jun 27, 2016 at 09:53:11PM -0700, Douglas Anderson wrote:
> >  
> >> Note that this patch is atop Boris's recent PWM regulator fixes.  If
> >> desired it wouldn't be too hard to write it atop the old code, though
> >> quite honestly anyone using a PWM regulator should probably be using his
> >> new code.  
> >
> > Given that those don't seem likely to go in any time soon and aren't
> > going to go in as a fix can you please respin against current code?  
> 
> I went to write this patch and it was pretty easy to write.  ...but
> then I realized that I had no real way to test it.  I can compile test
> it, but that's it.  Do you still want it?
> 
> Specifically without Boris's patches then I don't believe the devices
> I have access to are bootable when I tell Linux about the PWM
> regulator, especially when I configure the PWM regulator to use
> continuous mode (which is where the bug is).  As I understand from
> <https://patchwork.kernel.org/patch/9175279/> the current PWM
> regulator assumes that 0% duty cycle is the lowest voltage and 100% is
> the highest voltage.  My system is backwards (0% is highest, 100%
> lowest).  Also: last I tried even the non-continuous mode without
> Boris's patches the regulator would glitch enough at bootup that my
> system would crash (the PWM regulator affects the memory controller).
> 
> Anyway, let me know if you want me to post the untested, rebased
> patch...  Personally I'd prefer to wait for Boris's patches and the
> land the tested version.
> 
> Boris: any idea for what the next steps on your series are?

I'm waiting for reviews or acks. AFAIR, I addressed all the comments
Thierry made on my v2, so I guess we're good on the PWM side, but that
would be good to have a review from Mark now that we agreed on the PWM
APIs.

That shouldn't prevent you from rebasing this patch on Linus' tree and
tag it for stable (I'll rebase my series on top of the regulator tree if
needed). 

Regards,

Boris


From sudipm.mukherjee at gmail.com  Sun Jul  3 14:01:06 2016
From: sudipm.mukherjee at gmail.com (Sudip Mukherjee)
Date: Sun,  3 Jul 2016 22:01:06 +0100
Subject: [PATCH] phy: rockhip-usb: use devm_add_action_or_reset()
Message-ID: <1467579666-9179-1-git-send-email-sudipm.mukherjee@gmail.com>

If devm_add_action() fails we are explicitly calling the cleanup to free
the resources allocated.  Lets use the helper devm_add_action_or_reset()
and return directly in case of error, as we know that the cleanup function
has been already called by the helper if there was any error.

Signed-off-by: Sudip Mukherjee <sudip.mukherjee at codethink.co.uk>
---
 drivers/phy/phy-rockchip-usb.c | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/drivers/phy/phy-rockchip-usb.c b/drivers/phy/phy-rockchip-usb.c
index d60b149..dd58f23 100644
--- a/drivers/phy/phy-rockchip-usb.c
+++ b/drivers/phy/phy-rockchip-usb.c
@@ -236,9 +236,10 @@ static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base,
 			goto err_clk_prov;
 	}
 
-	err = devm_add_action(base->dev, rockchip_usb_phy_action, rk_phy);
+	err = devm_add_action_or_reset(base->dev, rockchip_usb_phy_action,
+				       rk_phy);
 	if (err)
-		goto err_devm_action;
+		return err;
 
 	rk_phy->phy = devm_phy_create(base->dev, child, &ops);
 	if (IS_ERR(rk_phy->phy)) {
@@ -256,9 +257,6 @@ static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base,
 	else
 		return rockchip_usb_phy_power(rk_phy, 1);
 
-err_devm_action:
-	if (!rk_phy->uart_enabled)
-		of_clk_del_provider(child);
 err_clk_prov:
 	if (!rk_phy->uart_enabled)
 		clk_unregister(rk_phy->clk480m);
-- 
1.9.1



From heiko at sntech.de  Sun Jul  3 14:34:07 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Sun, 03 Jul 2016 23:34:07 +0200
Subject: [PATCH] phy: rockhip-usb: use devm_add_action_or_reset()
In-Reply-To: <1467579666-9179-1-git-send-email-sudipm.mukherjee@gmail.com>
References: <1467579666-9179-1-git-send-email-sudipm.mukherjee@gmail.com>
Message-ID: <2045236.FJVOO9eQKE@phil>

Am Sonntag, 3. Juli 2016, 22:01:06 schrieb Sudip Mukherjee:
> If devm_add_action() fails we are explicitly calling the cleanup to free
> the resources allocated.  Lets use the helper devm_add_action_or_reset()
> and return directly in case of error, as we know that the cleanup function
> has been already called by the helper if there was any error.
> 
> Signed-off-by: Sudip Mukherjee <sudip.mukherjee at codethink.co.uk>

a nice new helper this devm_add_action_or_reset.

Reviewed-by: Heiko Stuebner <heiko at sntech.de>





From eballetbo at gmail.com  Mon Jul  4 00:24:12 2016
From: eballetbo at gmail.com (Enric Balletbo Serra)
Date: Mon, 4 Jul 2016 09:24:12 +0200
Subject: Applied "ASoC: rockchip-max98090: Fix NULL pointer dereference
 while accessing to jack." to the asoc tree
In-Reply-To: <E1b0CjK-00007p-Fj@debutante>
References: <1462435421-21205-4-git-send-email-enric.balletbo@collabora.com>
 <E1b0CjK-00007p-Fj@debutante>
Message-ID: <CAFqH_53jTEG7NvWt+9NBV_BVk28PZFdNSm89_DCqZvDBV+o9ng@mail.gmail.com>

Hi Mark,

2016-05-10 20:50 GMT+02:00 Mark Brown <broonie at kernel.org>:
> The patch
>
>    ASoC: rockchip-max98090: Fix NULL pointer dereference while accessing to jack.
>
> has been applied to the asoc tree at
>
>    git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
>
> All being well this means that it will be integrated into the linux-next
> tree (usually sometime in the next 24 hours) and sent to Linus during
> the next merge window (or sooner if it is a bug fix), however if
> problems are discovered then the patch may be dropped or reverted.
>

I just saw that these patches and the others in the patch series were
not applied yet. As are fixes and were accepted since middle May, I
expected see the patches merged in this release cycle, but we're at
rc6 so I suppose I'll need to wait to see them merged for 4.8 merge
window. Please can you coordinate with Heiko and make sure that these
patches were merged before he merges the patches to enable the audio
on Veyron? Otherwise we will break Veyron support for a while. I'd
really like have audio working on Veyron.

Thanks,
  Enric


> You may get further e-mails resulting from automated or manual testing
> and review of the tree, please engage with people reporting problems and
> send followup patches addressing any issues that are reported if needed.
>
> If any updates are required or you are submitting further changes they
> should be sent as incremental updates against current git, existing
> patches will not be replaced.
>
> Please add any relevant lists and maintainers to the CCs when replying
> to this mail.
>
> Thanks,
> Mark
>
> From af637e3adaf538288cbb3e6a2d9a4059853fc15a Mon Sep 17 00:00:00 2001
> From: Enric Balletbo i Serra <enric.balletbo at collabora.com>
> Date: Mon, 9 May 2016 12:46:31 +0200
> Subject: [PATCH] ASoC: rockchip-max98090: Fix NULL pointer dereference while
>  accessing to jack.
>
> Commit f2ed6b07645e ("ASoC: Make aux_dev more like a generic
> component") caused a regression on this driver, since now a
> kernel oops is seen when rockchip-mac98090 driver is loaded.
>
> That commit changed the probing of aux_devs before checking
> new DAI links, so for this driver rk_98090_headset_init is
> called before rk_init and then the kernel oops due a NULL
> pointer dereference inside rk_98090_headset_init function
> since there is a call that tries to access the jack pointer
> which has not been allocated yet.
>
> This is the call chain that causes the crash:
>
>  rk_98090_headset_init
>    -> ts3a227e_enable_jack_detect
>       -> snd_jack_set_key
>  rk_init
>    -> snd_soc_card_jack_new
>
> This patch moves the new jack object creation from rk_init
> to rk_98090_headset_init function making sure the jack is
> created before is accessed.
>
> Signed-off-by: Enric Balletbo i Serra <enric.balletbo at collabora.com>
> Signed-off-by: Mark Brown <broonie at kernel.org>
> ---
>  sound/soc/rockchip/rockchip_max98090.c | 50 ++++++++++++++++++----------------
>  1 file changed, 27 insertions(+), 23 deletions(-)
>
> diff --git a/sound/soc/rockchip/rockchip_max98090.c b/sound/soc/rockchip/rockchip_max98090.c
> index 543610282cdb..abb64a553967 100644
> --- a/sound/soc/rockchip/rockchip_max98090.c
> +++ b/sound/soc/rockchip/rockchip_max98090.c
> @@ -114,43 +114,27 @@ static int rk_aif1_hw_params(struct snd_pcm_substream *substream,
>         return ret;
>  }
>
> -static int rk_init(struct snd_soc_pcm_runtime *runtime)
> -{
> -       /* Enable Headset and 4 Buttons Jack detection */
> -       return snd_soc_card_jack_new(runtime->card, "Headset Jack",
> -                              SND_JACK_HEADSET |
> -                              SND_JACK_BTN_0 | SND_JACK_BTN_1 |
> -                              SND_JACK_BTN_2 | SND_JACK_BTN_3,
> -                              &headset_jack,
> -                              headset_jack_pins,
> -                              ARRAY_SIZE(headset_jack_pins));
> -}
> -
> -static int rk_98090_headset_init(struct snd_soc_component *component)
> -{
> -       return ts3a227e_enable_jack_detect(component, &headset_jack);
> -}
> -
>  static struct snd_soc_ops rk_aif1_ops = {
>         .hw_params = rk_aif1_hw_params,
>  };
>
> -static struct snd_soc_aux_dev rk_98090_headset_dev = {
> -       .name = "Headset Chip",
> -       .init = rk_98090_headset_init,
> -};
> -
>  static struct snd_soc_dai_link rk_dailink = {
>         .name = "max98090",
>         .stream_name = "Audio",
>         .codec_dai_name = "HiFi",
> -       .init = rk_init,
>         .ops = &rk_aif1_ops,
>         /* set max98090 as slave */
>         .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
>                 SND_SOC_DAIFMT_CBS_CFS,
>  };
>
> +static int rk_98090_headset_init(struct snd_soc_component *component);
> +
> +static struct snd_soc_aux_dev rk_98090_headset_dev = {
> +       .name = "Headset Chip",
> +       .init = rk_98090_headset_init,
> +};
> +
>  static struct snd_soc_card snd_soc_card_rk = {
>         .name = "ROCKCHIP-I2S",
>         .owner = THIS_MODULE,
> @@ -166,6 +150,26 @@ static struct snd_soc_card snd_soc_card_rk = {
>         .num_controls = ARRAY_SIZE(rk_mc_controls),
>  };
>
> +static int rk_98090_headset_init(struct snd_soc_component *component)
> +{
> +       int ret;
> +
> +       /* Enable Headset and 4 Buttons Jack detection */
> +       ret = snd_soc_card_jack_new(&snd_soc_card_rk, "Headset Jack",
> +                                   SND_JACK_HEADSET |
> +                                   SND_JACK_BTN_0 | SND_JACK_BTN_1 |
> +                                   SND_JACK_BTN_2 | SND_JACK_BTN_3,
> +                                   &headset_jack,
> +                                   headset_jack_pins,
> +                                   ARRAY_SIZE(headset_jack_pins));
> +       if (ret)
> +               return ret;
> +
> +       ret = ts3a227e_enable_jack_detect(component, &headset_jack);
> +
> +       return ret;
> +}
> +
>  static int snd_rk_mc_probe(struct platform_device *pdev)
>  {
>         int ret = 0;
> --
> 2.8.1
>


From broonie at kernel.org  Mon Jul  4 01:47:35 2016
From: broonie at kernel.org (Mark Brown)
Date: Mon, 4 Jul 2016 10:47:35 +0200
Subject: Applied "ASoC: rockchip-max98090: Fix NULL pointer dereference
 while accessing to jack." to the asoc tree
In-Reply-To: <CAFqH_53jTEG7NvWt+9NBV_BVk28PZFdNSm89_DCqZvDBV+o9ng@mail.gmail.com>
References: <1462435421-21205-4-git-send-email-enric.balletbo@collabora.com>
 <E1b0CjK-00007p-Fj@debutante>
 <CAFqH_53jTEG7NvWt+9NBV_BVk28PZFdNSm89_DCqZvDBV+o9ng@mail.gmail.com>
Message-ID: <20160704084735.GR6247@sirena.org.uk>

On Mon, Jul 04, 2016 at 09:24:12AM +0200, Enric Balletbo Serra wrote:

> I just saw that these patches and the others in the patch series were
> not applied yet. As are fixes and were accepted since middle May, I

No, they are actually applied like the e-mail said - please see my git.

> expected see the patches merged in this release cycle, but we're at

In general if you think a fix needs to go in urgently it's best to
specifically identify this.  If nothing is specifically said and it's
not obviously related to something done recently then the tendency will
be to assume that the issue was recently introduced and is a fix for a
development version.

> rc6 so I suppose I'll need to wait to see them merged for 4.8 merge
> window. Please can you coordinate with Heiko and make sure that these
> patches were merged before he merges the patches to enable the audio
> on Veyron? Otherwise we will break Veyron support for a while. I'd
> really like have audio working on Veyron.

As things stand they'll be merged in the next merge window.  They're
buried in the middle of a branch with other things so I can't just send
the branch as-is.  If these new patches you're talking about are going
to get merged in the merge window as well so we're presumably mostly
fine?
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160704/b004a581/attachment.sig>

From broonie at kernel.org  Mon Jul  4 01:53:36 2016
From: broonie at kernel.org (Mark Brown)
Date: Mon, 4 Jul 2016 10:53:36 +0200
Subject: [PATCH] regulator: pwm: Fix regulator ramp delay for continuous
 mode
In-Reply-To: <20160702174752.7e893ac2@bbrezillon>
References: <1467089591-7631-1-git-send-email-dianders@chromium.org>
 <20160701080614.GA9196@sirena.org.uk>
 <CAD=FV=WKR_Fq6o34886RCY-T-6AKdRqFz9H1aTiRq7Zu=o8TWA@mail.gmail.com>
 <20160702174752.7e893ac2@bbrezillon>
Message-ID: <20160704085336.GT6247@sirena.org.uk>

On Sat, Jul 02, 2016 at 05:47:52PM +0200, Boris Brezillon wrote:

> I'm waiting for reviews or acks. AFAIR, I addressed all the comments
> Thierry made on my v2, so I guess we're good on the PWM side, but that
> would be good to have a review from Mark now that we agreed on the PWM
> APIs.

I've already have one round of review invalidated by changes in the PWM
bits so I've been deprioritising it until it's clear that the churn
there has stopped.  I understand things are supposed to be quieting down
but I also have a bunch of other things to look at...
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160704/7bca3717/attachment.sig>

From boris.brezillon at free-electrons.com  Mon Jul  4 02:05:04 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Mon, 4 Jul 2016 11:05:04 +0200
Subject: [PATCH] regulator: pwm: Fix regulator ramp delay for continuous
 mode
In-Reply-To: <20160704085336.GT6247@sirena.org.uk>
References: <1467089591-7631-1-git-send-email-dianders@chromium.org>
 <20160701080614.GA9196@sirena.org.uk>
 <CAD=FV=WKR_Fq6o34886RCY-T-6AKdRqFz9H1aTiRq7Zu=o8TWA@mail.gmail.com>
 <20160702174752.7e893ac2@bbrezillon>
 <20160704085336.GT6247@sirena.org.uk>
Message-ID: <20160704110504.10a15c1c@bbrezillon>

Hi Mark,

On Mon, 4 Jul 2016 10:53:36 +0200
Mark Brown <broonie at kernel.org> wrote:

> On Sat, Jul 02, 2016 at 05:47:52PM +0200, Boris Brezillon wrote:
> 
> > I'm waiting for reviews or acks. AFAIR, I addressed all the comments
> > Thierry made on my v2, so I guess we're good on the PWM side, but that
> > would be good to have a review from Mark now that we agreed on the PWM
> > APIs.  
> 
> I've already have one round of review invalidated by changes in the PWM
> bits so I've been deprioritising it until it's clear that the churn
> there has stopped.  I understand things are supposed to be quieting down
> but I also have a bunch of other things to look at...

I understand, but can we still expect to have it in 4.8 (I mean,
assuming Thierry is okay taking the first 2 patching for 4.8 too)?

Thanks,

Boris


From broonie at kernel.org  Mon Jul  4 02:13:44 2016
From: broonie at kernel.org (Mark Brown)
Date: Mon, 4 Jul 2016 11:13:44 +0200
Subject: [PATCH] regulator: pwm: Fix regulator ramp delay for continuous
 mode
In-Reply-To: <20160704110504.10a15c1c@bbrezillon>
References: <1467089591-7631-1-git-send-email-dianders@chromium.org>
 <20160701080614.GA9196@sirena.org.uk>
 <CAD=FV=WKR_Fq6o34886RCY-T-6AKdRqFz9H1aTiRq7Zu=o8TWA@mail.gmail.com>
 <20160702174752.7e893ac2@bbrezillon>
 <20160704085336.GT6247@sirena.org.uk>
 <20160704110504.10a15c1c@bbrezillon>
Message-ID: <20160704091344.GY6247@sirena.org.uk>

On Mon, Jul 04, 2016 at 11:05:04AM +0200, Boris Brezillon wrote:

> I understand, but can we still expect to have it in 4.8 (I mean,
> assuming Thierry is okay taking the first 2 patching for 4.8 too)?

Possibly - we are getting very close to the merge window now, I don't
know what Thierry's policies are on applying things as the merge window
gets near.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160704/98d7a38f/attachment.sig>

From kishon at ti.com  Mon Jul  4 03:56:58 2016
From: kishon at ti.com (Kishon Vijay Abraham I)
Date: Mon, 4 Jul 2016 16:26:58 +0530
Subject: [PATCH] phy: rockhip-usb: use devm_add_action_or_reset()
In-Reply-To: <2045236.FJVOO9eQKE@phil>
References: <1467579666-9179-1-git-send-email-sudipm.mukherjee@gmail.com>
 <2045236.FJVOO9eQKE@phil>
Message-ID: <577A40FA.5080005@ti.com>



On Monday 04 July 2016 03:04 AM, Heiko Stuebner wrote:
> Am Sonntag, 3. Juli 2016, 22:01:06 schrieb Sudip Mukherjee:
>> If devm_add_action() fails we are explicitly calling the cleanup to free
>> the resources allocated.  Lets use the helper devm_add_action_or_reset()
>> and return directly in case of error, as we know that the cleanup function
>> has been already called by the helper if there was any error.
>>
>> Signed-off-by: Sudip Mukherjee <sudip.mukherjee at codethink.co.uk>
> 
> a nice new helper this devm_add_action_or_reset.
> 
> Reviewed-by: Heiko Stuebner <heiko at sntech.de>

applied, thanks.

-Kishon


From wxt at rock-chips.com  Mon Jul  4 05:34:04 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Mon,  4 Jul 2016 20:34:04 +0800
Subject: [PATCH v3] arm64: dts: rockchip: add the power domain node for rk3399
Message-ID: <1467635644-16798-1-git-send-email-wxt@rock-chips.com>

From: Elaine Zhang <zhangqing at rock-chips.com>

In order to meet low power requirements, a power management unit (PMU) is
designed for controlling power resources in RK3399. The RK3399 PMU is
dedicated for managing the power of the whole chip.

1. add pd node for RK3399 Soc
2. create power domain tree
3. add qos node for domain

>From the DT/binds and driver can get more detail information:
The driver:
        drivers/soc/rockchip/pm_domains.c
The document:
        Documentation/devicetree/bindings/soc/rockchip/power_domain.txt

Note:
As the TRM lists many voltage domains and power domains, then this patch
adds some domains for driver. Due to some domains
(e.g. emmc, usb, core)...We can't turned off it on
bootup, or says some device driver can't handle the power domain enough.
Maybe We will add more other domains in the future or later.

Signed-off-by: Elaine Zhang <zhangqing at rock-chips.com>
Signed-off-by: Caesar Wang <wxt at rock-chips.com>
Cc: linux-arm-kernel at lists.infradead.org
Cc: linux-rockchip at lists.infradead.org
Cc: Heiko Stuebner <heiko at sntech.de>

---

Changes in v3:
- As some commnets on https://patchwork.kernel.org/patch/9209205/
- according to the alphabetically sort
- %s/RK3399_PD_VOP/RK3399_PD_VOPL, %s/RK3399_PD_IE/RK3399_PD_IEP

Changes in v2:
- As Doug/Heiko commnets on https://patchwork.kernel.org/patch/9206415/.
  drop the debugfs-dump and Add the comments for alphabetical order.

 arch/arm64/boot/dts/rockchip/rk3399.dtsi | 179 +++++++++++++++++++++++++++++++
 1 file changed, 179 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index a6dd623..4559c04 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -45,6 +45,7 @@
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 #include <dt-bindings/interrupt-controller/irq.h>
 #include <dt-bindings/pinctrl/rockchip.h>
+#include <dt-bindings/power/rk3399-power.h>
 #include <dt-bindings/thermal/thermal.h>
 
 / {
@@ -594,6 +595,184 @@
 		status = "disabled";
 	};
 
+	qos_hdcp: qos_hdcp at ffa90000 {
+		compatible = "syscon";
+		reg = <0x0 0xffa90000 0x0 0x20>;
+	};
+
+	qos_iep: qos_iep at ffa98000 {
+		compatible = "syscon";
+		reg = <0x0 0xffa98000 0x0 0x20>;
+	};
+
+	qos_isp0_m0: qos_isp0_m0 at ffaa0000 {
+		compatible = "syscon";
+		reg = <0x0 0xffaa0000 0x0 0x20>;
+	};
+
+	qos_isp0_m1: qos_isp0_m1 at ffaa0080 {
+		compatible = "syscon";
+		reg = <0x0 0xffaa0080 0x0 0x20>;
+	};
+
+	qos_isp1_m0: qos_isp1_m0 at ffaa8000 {
+		compatible = "syscon";
+		reg = <0x0 0xffaa8000 0x0 0x20>;
+	};
+
+	qos_isp1_m1: qos_isp1_m1 at ffaa8080 {
+		compatible = "syscon";
+		reg = <0x0 0xffaa8080 0x0 0x20>;
+	};
+
+	qos_rga_r: qos_rga_r at ffab0000 {
+		compatible = "syscon";
+		reg = <0x0 0xffab0000 0x0 0x20>;
+	};
+
+	qos_rga_w: qos_rga_w at ffab0080 {
+		compatible = "syscon";
+		reg = <0x0 0xffab0080 0x0 0x20>;
+	};
+
+	qos_video_m0: qos_video_m0 at ffab8000 {
+		compatible = "syscon";
+		reg = <0x0 0xffab8000 0x0 0x20>;
+	};
+
+	qos_video_m1_r: qos_video_m1_r at ffac0000 {
+		compatible = "syscon";
+		reg = <0x0 0xffac0000 0x0 0x20>;
+	};
+
+	qos_video_m1_w: qos_video_m1_w at ffac0080 {
+		compatible = "syscon";
+		reg = <0x0 0xffac0080 0x0 0x20>;
+	};
+
+	qos_vop_big_r: qos_vop_big_r at ffac8000 {
+		compatible = "syscon";
+		reg = <0x0 0xffac8000 0x0 0x20>;
+	};
+
+	qos_vop_big_w: qos_vop_big_w at ffac8080 {
+		compatible = "syscon";
+		reg = <0x0 0xffac8080 0x0 0x20>;
+	};
+
+	qos_vop_little: qos_vop_little at ffad0000 {
+		compatible = "syscon";
+		reg = <0x0 0xffad0000 0x0 0x20>;
+	};
+
+	qos_gpu: qos_gpu at ffae0000 {
+		compatible = "syscon";
+		reg = <0x0 0xffae0000 0x0 0x20>;
+	};
+
+	pmu: power-management at ff310000 {
+		compatible = "rockchip,rk3399-pmu", "syscon", "simple-mfd";
+		reg = <0x0 0xff310000 0x0 0x1000>;
+
+		/*
+		 * Note: RK3399 supports 6 voltage domains including VD_CORE_L,
+		 * VD_CORE_B, VD_CENTER, VD_GPU, VD_LOGIC and VD_PMU.
+		 * Some of the power domains are grouped together for every
+		 * voltage domain.
+		 * The detail contents as below.
+		 */
+		power: power-controller {
+			status = "okay";
+			compatible = "rockchip,rk3399-power-controller";
+			#power-domain-cells = <1>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			/* These power domains are grouped by VD_CENTER */
+			pd_iep at RK3399_PD_IEP {
+				reg = <RK3399_PD_IEP>;
+				clocks = <&cru ACLK_IEP>,
+					 <&cru HCLK_IEP>;
+				pm_qos = <&qos_iep>;
+			};
+			pd_vcodec at RK3399_PD_VCODEC {
+				reg = <RK3399_PD_VCODEC>;
+				clocks = <&cru ACLK_VCODEC>,
+					 <&cru HCLK_VCODEC>;
+				pm_qos = <&qos_video_m0>;
+			};
+			pd_vdu at RK3399_PD_VDU {
+				reg = <RK3399_PD_VDU>;
+				clocks = <&cru ACLK_VDU>,
+					 <&cru HCLK_VDU>;
+				pm_qos = <&qos_video_m1_r>,
+					 <&qos_video_m1_w>;
+			};
+			pd_rga at RK3399_PD_RGA {
+				reg = <RK3399_PD_RGA>;
+				clocks = <&cru ACLK_RGA>,
+					 <&cru HCLK_RGA>;
+				pm_qos = <&qos_rga_r>,
+					 <&qos_rga_w>;
+			};
+
+			/* These power domains are grouped by VD_GPU */
+			pd_gpu at RK3399_PD_GPU {
+				reg = <RK3399_PD_GPU>;
+				clocks = <&cru ACLK_GPU>;
+				pm_qos = <&qos_gpu>;
+			};
+
+			/* These power domains are grouped by VD_LOGIC */
+			pd_vio at RK3399_PD_VIO {
+				reg = <RK3399_PD_VIO>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				pd_hdcp at RK3399_PD_HDCP {
+					reg = <RK3399_PD_HDCP>;
+					clocks = <&cru ACLK_HDCP>,
+						 <&cru HCLK_HDCP>,
+						 <&cru PCLK_HDCP>;
+					pm_qos = <&qos_hdcp>;
+				};
+				pd_isp0 at RK3399_PD_ISP0 {
+					reg = <RK3399_PD_ISP0>;
+					clocks = <&cru ACLK_ISP0>,
+						 <&cru HCLK_ISP0>;
+					pm_qos = <&qos_isp0_m0>,
+						 <&qos_isp0_m1>;
+				};
+				pd_isp1 at RK3399_PD_ISP1 {
+					reg = <RK3399_PD_ISP1>;
+					clocks = <&cru ACLK_ISP1>,
+						 <&cru HCLK_ISP1>;
+					pm_qos = <&qos_isp1_m0>,
+						 <&qos_isp1_m1>;
+				};
+				pd_vo at RK3399_PD_VO {
+					reg = <RK3399_PD_VO>;
+					#address-cells = <1>;
+					#size-cells = <0>;
+
+					pd_vopb at RK3399_PD_VOPB {
+						reg = <RK3399_PD_VOPB>;
+						clocks = <&cru ACLK_VOP0>,
+							 <&cru HCLK_VOP0>;
+						pm_qos = <&qos_vop_big_r>,
+							 <&qos_vop_big_w>;
+					};
+					pd_vopl at RK3399_PD_VOPL {
+						reg = <RK3399_PD_VOPL>;
+						clocks = <&cru ACLK_VOP1>,
+							 <&cru HCLK_VOP1>;
+						pm_qos = <&qos_vop_little>;
+					};
+				};
+			};
+		};
+	};
+
 	pmugrf: syscon at ff320000 {
 		compatible = "rockchip,rk3399-pmugrf", "syscon", "simple-mfd";
 		reg = <0x0 0xff320000 0x0 0x1000>;
-- 
1.9.1



From eballetbo at gmail.com  Mon Jul  4 08:13:05 2016
From: eballetbo at gmail.com (Enric Balletbo Serra)
Date: Mon, 4 Jul 2016 17:13:05 +0200
Subject: Applied "ASoC: rockchip-max98090: Fix NULL pointer dereference
 while accessing to jack." to the asoc tree
In-Reply-To: <20160704084735.GR6247@sirena.org.uk>
References: <1462435421-21205-4-git-send-email-enric.balletbo@collabora.com>
 <E1b0CjK-00007p-Fj@debutante>
 <CAFqH_53jTEG7NvWt+9NBV_BVk28PZFdNSm89_DCqZvDBV+o9ng@mail.gmail.com>
 <20160704084735.GR6247@sirena.org.uk>
Message-ID: <CAFqH_52bVUgUgeHWTo8mcQh16c1W7uPMRqQ-RpgMdGusk=p=Og@mail.gmail.com>

2016-07-04 10:47 GMT+02:00 Mark Brown <broonie at kernel.org>:
> On Mon, Jul 04, 2016 at 09:24:12AM +0200, Enric Balletbo Serra wrote:
>
>> I just saw that these patches and the others in the patch series were
>> not applied yet. As are fixes and were accepted since middle May, I
>
> No, they are actually applied like the e-mail said - please see my git.
>
>> expected see the patches merged in this release cycle, but we're at
>
> In general if you think a fix needs to go in urgently it's best to
> specifically identify this.  If nothing is specifically said and it's
> not obviously related to something done recently then the tendency will
> be to assume that the issue was recently introduced and is a fix for a
> development version.
>
>> rc6 so I suppose I'll need to wait to see them merged for 4.8 merge
>> window. Please can you coordinate with Heiko and make sure that these
>> patches were merged before he merges the patches to enable the audio
>> on Veyron? Otherwise we will break Veyron support for a while. I'd
>> really like have audio working on Veyron.
>
> As things stand they'll be merged in the next merge window.  They're
> buried in the middle of a branch with other things so I can't just send
> the branch as-is.  If these new patches you're talking about are going
> to get merged in the merge window as well so we're presumably mostly
> fine?

Yeah it's fine as long as the patches will be merged in next merge
window before the rockchip DTS updates.

It's only that I expected see them merged, but probably was only
something that I assumed wrongly.

Thanks,
 Enric


From heiko at sntech.de  Mon Jul  4 17:24:42 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Tue, 05 Jul 2016 02:24:42 +0200
Subject: [PATCH v3] arm64: dts: rockchip: add the power domain node for
 rk3399
In-Reply-To: <1467635644-16798-1-git-send-email-wxt@rock-chips.com>
References: <1467635644-16798-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1904131.irFlfKsH8m@phil>

Am Montag, 4. Juli 2016, 20:34:04 schrieb Caesar Wang:
> From: Elaine Zhang <zhangqing at rock-chips.com>
> 
> In order to meet low power requirements, a power management unit (PMU) is
> designed for controlling power resources in RK3399. The RK3399 PMU is
> dedicated for managing the power of the whole chip.
> 
> 1. add pd node for RK3399 Soc
> 2. create power domain tree
> 3. add qos node for domain
> 
> From the DT/binds and driver can get more detail information:
> The driver:
>         drivers/soc/rockchip/pm_domains.c
> The document:
>         Documentation/devicetree/bindings/soc/rockchip/power_domain.txt
> 
> Note:
> As the TRM lists many voltage domains and power domains, then this patch
> adds some domains for driver. Due to some domains
> (e.g. emmc, usb, core)...We can't turned off it on
> bootup, or says some device driver can't handle the power domain enough.
> Maybe We will add more other domains in the future or later.

I talked with Caesar about that today and for example the gmac domain seems 
to cause problems right now when only using the generic power-domain 
handling the kernel provides on probe and remove.

So while this gets investigated, I'm probably ok with only using the stable 
power-domains, but will give the us-people a chance to also respond on 
wednesday :-) .


> Signed-off-by: Elaine Zhang <zhangqing at rock-chips.com>
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
> Cc: linux-arm-kernel at lists.infradead.org
> Cc: linux-rockchip at lists.infradead.org
> Cc: Heiko Stuebner <heiko at sntech.de>
> 
> ---
> 
> Changes in v3:
> - As some commnets on https://patchwork.kernel.org/patch/9209205/
> - according to the alphabetically sort
> - %s/RK3399_PD_VOP/RK3399_PD_VOPL, %s/RK3399_PD_IE/RK3399_PD_IEP
> 
> Changes in v2:
> - As Doug/Heiko commnets on https://patchwork.kernel.org/patch/9206415/.
>   drop the debugfs-dump and Add the comments for alphabetical order.
> 
>  arch/arm64/boot/dts/rockchip/rk3399.dtsi | 179
> +++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+)
> 
> diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> b/arch/arm64/boot/dts/rockchip/rk3399.dtsi index a6dd623..4559c04 100644
> --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> @@ -45,6 +45,7 @@
>  #include <dt-bindings/interrupt-controller/arm-gic.h>
>  #include <dt-bindings/interrupt-controller/irq.h>
>  #include <dt-bindings/pinctrl/rockchip.h>
> +#include <dt-bindings/power/rk3399-power.h>
>  #include <dt-bindings/thermal/thermal.h>
> 
>  / {
> @@ -594,6 +595,184 @@
>  		status = "disabled";
>  	};
> 
> +	qos_hdcp: qos_hdcp at ffa90000 {

I guess I'll just rename that to qos_hdcp: qos at ffa90000
(similar for the other nodes), as the block itself is the same for all qos 
instances. So if that stays the only comment, there is no need to resend, as 
I can do the change myself.


> +		compatible = "syscon";
> +		reg = <0x0 0xffa90000 0x0 0x20>;
> +	};



From wxt at rock-chips.com  Mon Jul  4 17:44:03 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Tue, 05 Jul 2016 08:44:03 +0800
Subject: [PATCH v3] arm64: dts: rockchip: add the power domain node for
 rk3399
In-Reply-To: <1904131.irFlfKsH8m@phil>
References: <1467635644-16798-1-git-send-email-wxt@rock-chips.com>
 <1904131.irFlfKsH8m@phil>
Message-ID: <577B02D3.3050608@rock-chips.com>


? 2016?07?05? 08:24, Heiko Stuebner ??:
> Am Montag, 4. Juli 2016, 20:34:04 schrieb Caesar Wang:
>> From: Elaine Zhang <zhangqing at rock-chips.com>
>>
>> In order to meet low power requirements, a power management unit (PMU) is
>> designed for controlling power resources in RK3399. The RK3399 PMU is
>> dedicated for managing the power of the whole chip.
>>
>> 1. add pd node for RK3399 Soc
>> 2. create power domain tree
>> 3. add qos node for domain
>>
>>  From the DT/binds and driver can get more detail information:
>> The driver:
>>          drivers/soc/rockchip/pm_domains.c
>> The document:
>>          Documentation/devicetree/bindings/soc/rockchip/power_domain.txt
>>
>> Note:
>> As the TRM lists many voltage domains and power domains, then this patch
>> adds some domains for driver. Due to some domains
>> (e.g. emmc, usb, core)...We can't turned off it on
>> bootup, or says some device driver can't handle the power domain enough.
>> Maybe We will add more other domains in the future or later.
> I talked with Caesar about that today and for example the gmac domain seems
> to cause problems right now when only using the generic power-domain
> handling the kernel provides on probe and remove.
>
> So while this gets investigated, I'm probably ok with only using the stable
> power-domains, but will give the us-people a chance to also respond on
> wednesday :-) .
>
>
>> Signed-off-by: Elaine Zhang <zhangqing at rock-chips.com>
>> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
>> Cc: linux-arm-kernel at lists.infradead.org
>> Cc: linux-rockchip at lists.infradead.org
>> Cc: Heiko Stuebner <heiko at sntech.de>
>>
>> ---
>>
>> Changes in v3:
>> - As some commnets on https://patchwork.kernel.org/patch/9209205/
>> - according to the alphabetically sort
>> - %s/RK3399_PD_VOP/RK3399_PD_VOPL, %s/RK3399_PD_IE/RK3399_PD_IEP
>>
>> Changes in v2:
>> - As Doug/Heiko commnets on https://patchwork.kernel.org/patch/9206415/.
>>    drop the debugfs-dump and Add the comments for alphabetical order.
>>
>>   arch/arm64/boot/dts/rockchip/rk3399.dtsi | 179
>> +++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+)
>>
>> diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
>> b/arch/arm64/boot/dts/rockchip/rk3399.dtsi index a6dd623..4559c04 100644
>> --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
>> +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
>> @@ -45,6 +45,7 @@
>>   #include <dt-bindings/interrupt-controller/arm-gic.h>
>>   #include <dt-bindings/interrupt-controller/irq.h>
>>   #include <dt-bindings/pinctrl/rockchip.h>
>> +#include <dt-bindings/power/rk3399-power.h>
>>   #include <dt-bindings/thermal/thermal.h>
>>
>>   / {
>> @@ -594,6 +595,184 @@
>>   		status = "disabled";
>>   	};
>>
>> +	qos_hdcp: qos_hdcp at ffa90000 {
> I guess I'll just rename that to qos_hdcp: qos at ffa90000
> (similar for the other nodes), as the block itself is the same for all qos
> instances. So if that stays the only comment, there is no need to resend, as
> I can do the change myself.

Yep, that's better.
Thanks.

-Caesar

>
>> +		compatible = "syscon";
>> +		reg = <0x0 0xffa90000 0x0 0x20>;
>> +	};
>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip


-- 
caesar wang | software engineer | wxt at rock-chip.com




From shawn.lin at rock-chips.com  Mon Jul  4 19:07:08 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Tue, 5 Jul 2016 10:07:08 +0800
Subject: [PATCH v4 1/2] Documentation: bindings: add dt doc for Rockchip
 PCIe controller
In-Reply-To: <20160702022537.GA29641@google.com>
References: <1467336290-11282-1-git-send-email-shawn.lin@rock-chips.com>
 <57766995.9070302@arm.com> <20160702022537.GA29641@google.com>
Message-ID: <9f5f92db-55af-9227-82c9-8e5815d83ebf@rock-chips.com>

? 2016/7/2 10:25, Brian Norris ??:
> Hi,
>
> On Fri, Jul 01, 2016 at 02:01:09PM +0100, Marc Zyngier wrote:
>> On 01/07/16 02:24, Shawn Lin wrote:
>>> This patch adds a binding that describes the Rockchip PCIe controller
>>> found on Rockchip SoCs PCIe interface.
>>>
>>> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
>>>
>>> ---
>>>
>>> Changes in v4:
>>> - fix example of adding intermediate interrupt controller for pcie
>>>   legacy interrrupt
>>>
>>> Changes in v3:
>>> - fix example dts code suggested by Rob and Marc
>>> - remove driver's behaviour of regulator
>>>
>>> Changes in v2:
>>> - fix lots clk/reset stuff suggested by Heiko
>>> - remove msi-parent and add msi-map suggested by Marc
>>> - drop phy related stuff
>>> - some others minor fixes
>>>
>>>  .../devicetree/bindings/pci/rockchip-pcie.txt      | 91 ++++++++++++++++++++++
>>>  1 file changed, 91 insertions(+)
>>>  create mode 100644 Documentation/devicetree/bindings/pci/rockchip-pcie.txt
>>>
>>> diff --git a/Documentation/devicetree/bindings/pci/rockchip-pcie.txt b/Documentation/devicetree/bindings/pci/rockchip-pcie.txt
>>> new file mode 100644
>>> index 0000000..8092fc5
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/pci/rockchip-pcie.txt
>>> @@ -0,0 +1,91 @@
>>> +* Rockchip AXI PCIe Root Port Bridge DT description
>>> +
>>> +Required properties:
>>> +- #address-cells: Address representation for root ports, set to <3>
>>> +- #size-cells: Size representation for root ports, set to <2>
>>> +- #interrupt-cells: specifies the number of cells needed to encode an
>>> +		interrupt source. The value must be 1.
>
> I think this propoerty should be in a separate section, since it's going
> under a sub-node (not the main node). Right?
>

main node also need it.

>>> +- compatible: Should contain "rockchip,rk3399-pcie"
>>> +- reg: Two register ranges as listed in the reg-names property
>>> +- reg-names: Must include the following names
>>> +	- "axi-base"
>>> +	- "apb-base"
>>> +- clocks: Must contain an entry for each entry in clock-names.
>>> +		See ../clocks/clock-bindings.txt for details.
>>> +- clock-names: Must include the following entries:
>>> +	- "aclk"
>>> +	- "aclk-perf"
>>> +	- "hclk"
>>> +	- "pm"
>>> +- msi-map: Maps a Requester ID to an MSI controller and associated.
>>> +		See ./pci-msi.txt
>>> +- phys: From PHY bindings: Phandle for the Generic PHY for PCIe.
>>> +- phy-names:  MUST be "pcie-phy".
>>> +- interrupts: Three interrupt entries must be specified.
>>> +- interrupt-names: Must include the following names
>>> +	- "sys"
>>> +	- "legacy"
>>> +	- "client"
>>> +- resets: Must contain five entries for each entry in reset-names.
>>> +	   See ../reset/reset.txt for details.
>>> +- reset-names: Must include the following names
>>> +	- "core"
>>> +	- "mgmt"
>>> +	- "mgmt-sticky"
>>> +	- "pipe"
>>> +- pinctrl-names : The pin control state names
>>> +- pinctrl-0: The "default" pinctrl state
>>> +- interrupt-map-mask and interrupt-map: standard PCI properties
>>> +- interrupt-controller: identifies the node as an interrupt controller
>
> Same with this property.
>
>>> +
>>> +Optional Property:
>>> +- ep-gpios: contain the entry for pre-reset gpio
>>> +- num-lanes: number of lanes to use
>>> +- vpcie3v3-supply: The phandle to the 3.3v regulator to use for pcie.
>>> +- vpcie1v8-supply: The phandle to the 1.8v regulator to use for pcie.
>>> +- vpcie0v9-supply: The phandle to the 0.9v regulator to use for pcie.
>
> e.g., you might start a new section here describing how this controller
> acts as an intermediate IRQ controller, and that the
> interrupt-controller-related properties should be placed under a subnode.
> (Did you also need an interrupt-parent property? I'm a little fuzzy on
> the details myself, actually...)

ok I will do it.

>
>>> +
>>> +Example:
>>> +
>>> +pcie0: pcie at f8000000 {
>>> +	compatible = "rockchip,rk3399-pcie";
>>> +	#address-cells = <3>;
>>> +	#size-cells = <2>;
>>> +	clocks = <&cru ACLK_PCIE>, <&cru ACLK_PERF_PCIE>,
>>> +		 <&cru PCLK_PCIE>, <&cru SCLK_PCIE_PM>;
>>> +	clock-names = "aclk", "aclk-perf",
>>> +		      "hclk", "pm";
>>> +	bus-range = <0x0 0x1>;
>>> +	interrupts = <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 50 IRQ_TYPE_LEVEL_HIGH>,
>>> +		     <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>;
>>> +	interrupt-names = "sys", "legacy", "client";
>>> +	assigned-clocks = <&cru SCLK_PCIEPHY_REF>;
>>> +	assigned-clock-parents = <&cru SCLK_PCIEPHY_REF100M>;
>>> +	assigned-clock-rates = <100000000>;
>>> +	ep-gpios = <&gpio3 13 GPIO_ACTIVE_HIGH>;
>>> +	ranges = <0x83000000 0x0 0xfa000000 0x0 0xfa000000 0x0 0x600000
>>> +		  0x81000000 0x0 0xfa600000 0x0 0xfa600000 0x0 0x100000>;
>>> +	num-lanes = <4>;
>>> +	msi-map = <0x0 &its 0x0 0x1000>;
>>> +	reg = < 0x0 0xf8000000 0x0 0x2000000 >, < 0x0 0xfd000000 0x0 0x1000000 >;
>>> +	reg-names = "axi-base", "apb-base";
>>> +	resets = <&cru SRST_PCIE_CORE>, <&cru SRST_PCIE_MGMT>,
>>> +		 <&cru SRST_PCIE_MGMT_STICKY>, <&cru SRST_PCIE_PIPE>;
>>> +	reset-names = "core", "mgmt", "mgmt-sticky", "pipe";
>>> +	phys = <&pcie_phy>;
>>> +	phy-names = "pcie-phy";
>>> +	pinctrl-names = "default";
>>> +	pinctrl-0 = <&pcie_clkreq>;
>>> +	#interrupt-cells = <1>;
>>> +	interrupt-controller;
>>
>> So the pcie node itself is an interrupt controller...
>>
>>> +	interrupt-map-mask = <0 0 0 7>;
>>> +	interrupt-map = <0 0 0 1 &pcie0_intc 1>,
>>> +			<0 0 0 2 &pcie0_intc 2>,
>>> +			<0 0 0 3 &pcie0_intc 3>,
>>> +			<0 0 0 4 &pcie0_intc 4>;
>>> +	pcie0_intc: interrupt-controller {
>>> +		interrupt-controller;
>>> +		#address-cells = <0>;
>>> +		#interrupt-cells = <1>;
>>> +	};
>>
>> But there's also another here. I just don't understand how it works.
>
> I believe the repeat of #interrupt-cells and interrupt-controller in the
> pcie@ node is a mistake. They should only be in the 'interrupt-controller'
> subnode. And in case this is what you're asking about... the subnode was
> present in v1 but was removed at your request:

Sorry for this noise, I will fix them.

>
> From Marc, in https://patchwork.kernel.org/patch/9129183/:
>>> +static int rockchip_pcie_init_irq_domain(struct rockchip_pcie_port *pp)
>>> +{
>>> +	struct device *dev = pp->dev;
>>> +	struct device_node *node = dev->of_node;
>>> +	struct device_node *pcie_intc_node =  of_get_next_child(node, NULL);
>>
>> That's really ugly, as it depends on the layout of your DT.
>>
>>> +
>>> +	if (!pcie_intc_node) {
>>> +		dev_err(dev, "No PCIe Intc node found\n");
>>> +		return PTR_ERR(pcie_intc_node);
>>> +	}
>>> +	pp->irq_domain = irq_domain_add_linear(pcie_intc_node, 4,
>>> +					       &intx_domain_ops, pp);
>>
>> Why can't you just register your host controller as the interrupt
>> controller? You don't need an intermediate node for that.
>
> But then in v3, Arnd concluded it actually *should* be used:
>
> From Arnd, in https://patchwork.kernel.org/patch/9179763/:
>> On Thursday, June 16, 2016 4:01:12 PM CEST Wenrui Li wrote:
>>> ? 2016/6/16 15:00, Arnd Bergmann ??:
>>>> On Thursday, June 16, 2016 9:50:21 AM CEST Shawn Lin wrote:
>>>>
>>>>> +    reset-names = "core", "mgmt", "mgmt-sticky", "pipe";
>>>>> +    phys = <&pcie_phy>;
>>>>> +    phy-names = "pcie-phy";
>>>>> +    pinctrl-names = "default";
>>>>> +    pinctrl-0 = <&pcie_clkreq>;
>>>>> +    #interrupt-cells = <1>;
>>>>> +    interrupt-controller;
>>>>> +    interrupt-map-mask = <0 0 0 7>;
>>>>> +    interrupt-map = <0 0 0 1 &pcie0 1>,
>>>>> +                    <0 0 0 2 &pcie0 2>,
>>>>> +                    <0 0 0 3 &pcie0 3>,
>>>>> +                    <0 0 0 4 &pcie0 4>;
>>>>> +};
>>>>>
>>>>
>>>> One thing that came up in the review of the new Marvell PCIe driver is that it's
>>>> most likely invalid for a device node to have both "interrupt-controller"
>>>> and "interrupt-map" properties. I originally thought this was a nice way to
>>>> handle embedded irqchips within the PCIe host, but it only really works
>>>> by coincidence with the current kernel, and only as long as the hwirq number
>>>> of the irqchip matches the integer representation of the irq line in the root
>>>> bridge (which it does in the example above).
>>>>
>>>> For that driver we concluded that it would be less of a hack to have the
>>>> irqchip as a child node of the PCIe host after all (just not with
>>>> device_type="pci" of course), and that makes the translation work as
>>>> expected.
>>>>
>>>>       Arnd
>>>>
>>>
>>> Original driver have an irqchip as child node. But Marc suggested don't
>>> need an intermediate node here.
>>> Now the conclusion is to retain the child node?
>>
>> That is at least my view of the situation, sorry for the mixed messages
>> you have been getting. Marc, Rob, do you agree with my finding?
>
> Rob and others agreed with the subnode. So, I hope you don't disagree in
> principle, and are only confused by the duplicate properties?
>
> Regards,
> Brian
>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip
>


-- 
Best Regards
Shawn Lin



From shawn.lin at rock-chips.com  Mon Jul  4 19:08:08 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Tue, 5 Jul 2016 10:08:08 +0800
Subject: [PATCH v4 2/2] PCI: Rockchip: Add Rockchip PCIe controller support
In-Reply-To: <577646D3.7040207@arm.com>
References: <1467336290-11282-1-git-send-email-shawn.lin@rock-chips.com>
 <1467336290-11282-2-git-send-email-shawn.lin@rock-chips.com>
 <577646D3.7040207@arm.com>
Message-ID: <dcb37af0-89c5-ec37-5ace-7e4df61724a5@rock-chips.com>

? 2016/7/1 18:32, Marc Zyngier ??:
> On 01/07/16 02:24, Shawn Lin wrote:
>> This patch adds Rockchip PCIe controller support found
>> on RK3399 Soc platform.
>>
>> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
>>
>> ---
>
> [...]
>
>> +static void rockchip_pcie_legacy_int_handler(struct irq_desc *desc)
>> +{
>> +	struct irq_chip *chip = irq_desc_get_chip(desc);
>> +	struct rockchip_pcie_port *port;
>> +	u32 reg;
>> +
>> +	chained_irq_enter(chip, desc);
>> +	port = irq_desc_get_handler_data(desc);
>> +
>> +	reg = pcie_read(port, PCIE_CLIENT_INT_STATUS);
>> +	reg = (reg & ROCKCHIP_PCIE_RPIFR1_INTR_MASK) >>
>> +	       ROCKCHIP_PCIE_RPIFR1_INTR_SHIFT;
>> +	if (reg)
>> +		generic_handle_irq(irq_find_mapping(port->irq_domain,
>> +						    ffs(reg)));
>
> Can't you get multiple pending interrupts at the same time? Here, you
> seem to only process one interrupt at a time, which is going to be
> pretty inefficient.
>

Yup, will fix it, thanks.

> How about something like:
>
> 	while (reg) {
> 		int hwirq = ffs(reg);
> 		int irq;
> 		
> 		reg &= ~BIT(hwirq);
> 		irq = irq_find_mapping(port->irq_domain, hwirq);
> 		if (irq)
> 			generic_handle_irq(irq);
> 	}
>
>> +	chained_irq_exit(chip, desc);
>> +}
>> +
>
> Thanks,
>
> 	M.
>


-- 
Best Regards
Shawn Lin



From shawn.lin at rock-chips.com  Mon Jul  4 19:42:40 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Tue,  5 Jul 2016 10:42:40 +0800
Subject: [PATCH v5 1/2] Documentation: bindings: add dt doc for Rockchip PCIe
 controller
Message-ID: <1467686561-25638-1-git-send-email-shawn.lin@rock-chips.com>

This patch adds a binding that describes the Rockchip PCIe controller
found on Rockchip SoCs PCIe interface.

Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>

---

Changes in v5:
- fix wrong example reported by Marc
- add seperate section to describe the interrupt controller child
  node

Changes in v4:
- fix example of adding intermediate interrupt controller for pcie
  legacy interrrupt

Changes in v3:
- fix example dts code suggested by Rob and Marc
- remove driver's behaviour of regulator

Changes in v2:
- fix lots clk/reset stuff suggested by Heiko
- remove msi-parent and add msi-map suggested by Marc
- drop phy related stuff
- some others minor fixes

 .../devicetree/bindings/pci/rockchip-pcie.txt      | 104 +++++++++++++++++++++
 1 file changed, 104 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/rockchip-pcie.txt

diff --git a/Documentation/devicetree/bindings/pci/rockchip-pcie.txt b/Documentation/devicetree/bindings/pci/rockchip-pcie.txt
new file mode 100644
index 0000000..7616ecc
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/rockchip-pcie.txt
@@ -0,0 +1,104 @@
+* Rockchip AXI PCIe Root Port Bridge DT description
+
+Required properties:
+- #address-cells: Address representation for root ports, set to <3>
+- #size-cells: Size representation for root ports, set to <2>
+- #interrupt-cells: specifies the number of cells needed to encode an
+		interrupt source. The value must be 1.
+- compatible: Should contain "rockchip,rk3399-pcie"
+- reg: Two register ranges as listed in the reg-names property
+- reg-names: Must include the following names
+	- "axi-base"
+	- "apb-base"
+- clocks: Must contain an entry for each entry in clock-names.
+		See ../clocks/clock-bindings.txt for details.
+- clock-names: Must include the following entries:
+	- "aclk"
+	- "aclk-perf"
+	- "hclk"
+	- "pm"
+- msi-map: Maps a Requester ID to an MSI controller and associated.
+		See ./pci-msi.txt
+- phys: From PHY bindings: Phandle for the Generic PHY for PCIe.
+- phy-names:  MUST be "pcie-phy".
+- interrupts: Three interrupt entries must be specified.
+- interrupt-names: Must include the following names
+	- "sys"
+	- "legacy"
+	- "client"
+- resets: Must contain five entries for each entry in reset-names.
+	   See ../reset/reset.txt for details.
+- reset-names: Must include the following names
+	- "core"
+	- "mgmt"
+	- "mgmt-sticky"
+	- "pipe"
+- pinctrl-names : The pin control state names
+- pinctrl-0: The "default" pinctrl state
+- #interrupt-cells: specifies the number of cells needed to encode an
+	interrupt source. The value must be 1.
+- interrupt-map-mask and interrupt-map: standard PCI properties
+
+*Interrupt controller child node*
+The core controller provides a single interrupt for legacy INTx. So,
+pcie node should create a interrupt controller node to support 'interrupt-map'
+DT functionality. The driver will create an IRQ domain for this map, decode
+the four INTx interrupts in ISR and route them to this domain.
+
+Required properties for Interrupt controller child node:
+- interrupt-controller: identifies the node as an interrupt controller
+- #address-cells: specifies the number of cells needed to encode an
+	address. The value must be 0.
+- #interrupt-cells: specifies the number of cells needed to encode an
+	interrupt source. The value must be 1.
+
+Optional Property:
+- ep-gpios: contain the entry for pre-reset gpio
+- num-lanes: number of lanes to use
+- vpcie3v3-supply: The phandle to the 3.3v regulator to use for pcie.
+- vpcie1v8-supply: The phandle to the 1.8v regulator to use for pcie.
+- vpcie0v9-supply: The phandle to the 0.9v regulator to use for pcie.
+
+Example:
+
+pcie0: pcie at f8000000 {
+	compatible = "rockchip,rk3399-pcie";
+	#address-cells = <3>;
+	#size-cells = <2>;
+	clocks = <&cru ACLK_PCIE>, <&cru ACLK_PERF_PCIE>,
+		 <&cru PCLK_PCIE>, <&cru SCLK_PCIE_PM>;
+	clock-names = "aclk", "aclk-perf",
+		      "hclk", "pm";
+	bus-range = <0x0 0x1>;
+	interrupts = <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 50 IRQ_TYPE_LEVEL_HIGH>,
+		     <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>;
+	interrupt-names = "sys", "legacy", "client";
+	assigned-clocks = <&cru SCLK_PCIEPHY_REF>;
+	assigned-clock-parents = <&cru SCLK_PCIEPHY_REF100M>;
+	assigned-clock-rates = <100000000>;
+	ep-gpios = <&gpio3 13 GPIO_ACTIVE_HIGH>;
+	ranges = <0x83000000 0x0 0xfa000000 0x0 0xfa000000 0x0 0x600000
+		  0x81000000 0x0 0xfa600000 0x0 0xfa600000 0x0 0x100000>;
+	num-lanes = <4>;
+	msi-map = <0x0 &its 0x0 0x1000>;
+	reg = < 0x0 0xf8000000 0x0 0x2000000 >, < 0x0 0xfd000000 0x0 0x1000000 >;
+	reg-names = "axi-base", "apb-base";
+	resets = <&cru SRST_PCIE_CORE>, <&cru SRST_PCIE_MGMT>,
+		 <&cru SRST_PCIE_MGMT_STICKY>, <&cru SRST_PCIE_PIPE>;
+	reset-names = "core", "mgmt", "mgmt-sticky", "pipe";
+	phys = <&pcie_phy>;
+	phy-names = "pcie-phy";
+	pinctrl-names = "default";
+	pinctrl-0 = <&pcie_clkreq>;
+	#interrupt-cells = <1>;
+	interrupt-map-mask = <0 0 0 7>;
+	interrupt-map = <0 0 0 1 &pcie0_intc 1>,
+			<0 0 0 2 &pcie0_intc 2>,
+			<0 0 0 3 &pcie0_intc 3>,
+			<0 0 0 4 &pcie0_intc 4>;
+	pcie0_intc: interrupt-controller {
+		interrupt-controller;
+		#address-cells = <0>;
+		#interrupt-cells = <1>;
+	};
+};
-- 
2.3.7




From shawn.lin at rock-chips.com  Mon Jul  4 19:42:41 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Tue,  5 Jul 2016 10:42:41 +0800
Subject: [PATCH v5 2/2] PCI: Rockchip: Add Rockchip PCIe controller support
In-Reply-To: <1467686561-25638-1-git-send-email-shawn.lin@rock-chips.com>
References: <1467686561-25638-1-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <1467686561-25638-2-git-send-email-shawn.lin@rock-chips.com>

This patch adds Rockchip PCIe controller support found
on RK3399 Soc platform.

Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>

---

Changes in v5:
- handle multiple pending INTx at the same time
  suggested by Marc

Changes in v4:
- address the comments from Brain

Changes in v3:
- remove header file
- remove struct msi_controller and move most of variables
  of rockchip_pcie_port to become the local ones.
- allow legacy int even if enabling MSI
- drop regulator set voltage operation suggested by Doug

Changes in v2:
- remove phy related stuff and call phy API
- add new head file and define lots of macro to make
  the code more readable
- remove lots msi related code suggested by Marc
- add IO window address translation
- init_port and parse_dt reconstruction suggested by Bharat
- improve wr_own_conf suggested by Arnd
- make pcie as an interrupt controller and fix wrong int handler
  suggested by Marc
- remove PCI_PROBE_ONLY suggested by Lorenzo

 drivers/pci/host/Kconfig         |   11 +
 drivers/pci/host/Makefile        |    1 +
 drivers/pci/host/pcie-rockchip.c | 1213 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 1225 insertions(+)
 create mode 100644 drivers/pci/host/pcie-rockchip.c

diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index c917e2f..65a68a4 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -264,4 +264,15 @@ config PCIE_ARTPEC6
 	  Say Y here to enable PCIe controller support on Axis ARTPEC-6
 	  SoCs.  This PCIe controller uses the DesignWare core.
 
+config PCIE_ROCKCHIP
+	tristate "Rockchip PCIe controller"
+	depends on ARM64 && ARCH_ROCKCHIP
+	depends on OF
+	select MFD_SYSCON
+	select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+	help
+	  Say Y here if you want internal PCI support on Rockchip SoC.
+	  There is 1 internal PCIe port available to support GEN2 with
+	  4 slots.
+
 endmenu
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index 5bc0af2..695f716 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -30,3 +30,4 @@ obj-$(CONFIG_PCI_HOST_THUNDER_ECAM) += pci-thunder-ecam.o
 obj-$(CONFIG_PCI_HOST_THUNDER_PEM) += pci-thunder-pem.o
 obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o
 obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o
+obj-$(CONFIG_PCIE_ROCKCHIP) += pcie-rockchip.o
diff --git a/drivers/pci/host/pcie-rockchip.c b/drivers/pci/host/pcie-rockchip.c
new file mode 100644
index 0000000..7996556
--- /dev/null
+++ b/drivers/pci/host/pcie-rockchip.c
@@ -0,0 +1,1213 @@
+/*
+ * Rockchip AXI PCIe host controller driver
+ *
+ * Copyright (c) 2016 Rockchip, Inc.
+ *
+ * Author: Shawn Lin <shawn.lin at rock-chips.com>
+ *         Wenrui Li <wenrui.li at rock-chips.com>
+ *
+ * Bits taken from Synopsys Designware Host controller driver and
+ * ARM PCI Host generic driver.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_pci.h>
+#include <linux/of_platform.h>
+#include <linux/of_irq.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/regmap.h>
+
+#define PCIE_CLIENT_BASE                0x0
+#define PCIE_RC_CONFIG_NORMAL_BASE      0x800000
+#define PCIE_RC_CONFIG_BASE             0xa00000
+#define PCIE_CORE_LINK_CTRL_STATUS      0x8000d0
+#define PCIE_CORE_CTRL_MGMT_BASE        0x900000
+#define PCIE_CORE_AXI_CONF_BASE         0xc00000
+#define PCIE_CORE_AXI_INBOUND_BASE      0xc00800
+#define PCIE_CLIENT_BASIC_STATUS0       0x44
+#define PCIE_CLIENT_BASIC_STATUS1       0x48
+#define PCIE_CLIENT_INT_MASK            0x4c
+#define PCIE_CLIENT_INT_STATUS          0x50
+#define PCIE_RC_CONFIG_RID_CCR          0x8
+#define PCIE_RC_CONFIG_LCS              0xd0
+#define PCIE_RC_BAR_CONF                0x300
+#define PCIE_CORE_OB_REGION_ADDR1       0x4
+#define PCIE_CORE_OB_REGION_DESC0       0x8
+#define PCIE_CORE_OB_REGION_DESC1       0xc
+#define PCIE_RP_IB_ADDR_TRANS           0x4
+#define PCIE_CORE_INT_MASK              0x900210
+#define PCIE_CORE_INT_STATUS            0x90020c
+
+/* Size of one AXI Region (not Region 0) */
+#define AXI_REGION_SIZE                 BIT(20)
+/* Size of Region 0, equal to sum of sizes of other regions */
+#define AXI_REGION_0_SIZE              (32 * (0x1 << 20))
+#define OB_REG_SIZE_SHIFT               5
+#define IB_ROOT_PORT_REG_SIZE_SHIFT     3
+#define AXI_WRAPPER_IO_WRITE            0x6
+#define AXI_WRAPPER_MEM_WRITE           0x2
+#define MAX_AXI_IB_ROOTPORT_REGION_NUM  3
+#define MIN_AXI_ADDR_BITS_PASSED        8
+#define ROCKCHIP_VENDOR_ID              0x1d87
+#define PCIE_ECAM_BUS(x)		(((x) & 0xFF) << 20)
+#define PCIE_ECAM_DEV(x)		(((x) & 0x1F) << 15)
+#define PCIE_ECAM_FUNC(x)		(((x) & 0x7) << 12)
+#define PCIE_ECAM_REG(x)		(((x) & 0xFFF) << 0)
+#define PCIE_ECAM_ADDR(bus, dev, func, reg) \
+	  (PCIE_ECAM_BUS(bus) | PCIE_ECAM_DEV(dev) | \
+	   PCIE_ECAM_FUNC(func) | PCIE_ECAM_REG(reg))
+
+/*
+  * The higher 16-bit of this register is used for write protection
+  * only if BIT(x + 16) set to 1 the BIT(x) can be written.
+  */
+#define HIWORD_UPDATE(val, mask, shift) \
+	((val) << (shift) | (mask) << ((shift) + 16))
+
+#define RC_REGION_0_ADDR_TRANS_H        0x00000000
+#define RC_REGION_0_ADDR_TRANS_L        0x00000000
+#define RC_REGION_0_PASS_BITS           (25 - 1)
+#define RC_REGION_1_ADDR_TRANS_H        0x00000000
+#define RC_REGION_1_ADDR_TRANS_L        0x00400000
+#define RC_REGION_1_PASS_BITS           (20 - 1)
+#define MAX_AXI_WRAPPER_REGION_NUM      33
+#define PCIE_CORE_LCSR_RETRAIN_LINK     BIT(5)
+#define PCIE_CLIENT_CONF_ENABLE         1
+#define PCIE_CLIENT_CONF_ENABLE_SHIFT   0
+#define PCIE_CLIENT_CONF_ENABLE_MASK    0x1
+#define PCIE_CLIENT_LINK_TRAIN_ENABLE   1
+#define PCIE_CLIENT_LINK_TRAIN_SHIFT    1
+#define PCIE_CLIENT_LINK_TRAIN_MASK     0x1
+#define PCIE_CLIENT_ARI_ENABLE          1
+#define PCIE_CLIENT_ARI_ENABLE_SHIFT    3
+#define PCIE_CLIENT_ARI_ENABLE_MASK     0x1
+#define PCIE_CLIENT_CONF_LANE_NUM(x)    (x / 2)
+#define PCIE_CLIENT_CONF_LANE_NUM_SHIFT 4
+#define PCIE_CLIENT_CONF_LANE_NUM_MASK  0x3
+#define PCIE_CLIENT_MODE_RC             1
+#define PCIE_CLIENT_MODE_SHIFT          6
+#define PCIE_CLIENT_MODE_MASK           0x1
+#define PCIE_CLIENT_GEN_SEL_2           1
+#define PCIE_CLIENT_GEN_SEL_1           0
+#define PCIE_CLIENT_GEN_SEL_SHIFT       7
+#define PCIE_CLIENT_GEN_SEL_MASK        0x1
+#define PCIE_CLIENT_LINK_STATUS_UP      0x3
+#define PCIE_CLIENT_LINK_STATUS_SHIFT   20
+#define PCIE_CLIENT_LINK_STATUS_MASK    0x3
+#define PCIE_CORE_PL_CONF_SPEED_2_5G     0x0
+#define PCIE_CORE_PL_CONF_SPEED_5G     0x1
+#define PCIE_CORE_PL_CONF_SPEED_8G     0x2
+#define PCIE_CORE_PL_CONF_SPEED_SHIFT   3
+#define PCIE_CORE_PL_CONF_SPEED_MASK    0x3
+#define PCIE_CORE_PL_CONF_LANE_SHIFT    1
+#define PCIE_CORE_PL_CONF_LANE_MASK     0x3
+#define PCIE_CORE_RC_CONF_SCC_SHIFT     16
+
+/* PCIE_CLIENT_INT_STATUS */
+#define PCIE_CLIENT_INT_LEGACY_DONE     BIT(15)
+#define PCIE_CLIENT_INT_MSG             BIT(14)
+#define PCIE_CLIENT_INT_HOT_RST         BIT(13)
+#define PCIE_CLIENT_INT_DPA             BIT(12)
+#define PCIE_CLIENT_INT_FATAL_ERR       BIT(11)
+#define PCIE_CLIENT_INT_NFATAL_ERR      BIT(10)
+#define PCIE_CLIENT_INT_CORR_ERR        BIT(9)
+#define PCIE_CLIENT_INT_INTD            BIT(8)
+#define PCIE_CLIENT_INT_INTC            BIT(7)
+#define PCIE_CLIENT_INT_INTB            BIT(6)
+#define PCIE_CLIENT_INT_INTA            BIT(5)
+#define PCIE_CLIENT_INT_LOCAL           BIT(4)
+#define PCIE_CLIENT_INT_UDMA            BIT(3)
+#define PCIE_CLIENT_INT_PHY             BIT(2)
+#define PCIE_CLIENT_INT_HOT_PLUG        BIT(1)
+#define PCIE_CLIENT_INT_PWR_STCG        BIT(0)
+#define PCIE_CORE_INT_PRFPE             BIT(0)
+#define PCIE_CORE_INT_CRFPE             BIT(1)
+#define PCIE_CORE_INT_RRPE              BIT(2)
+#define PCIE_CORE_INT_PRFO              BIT(3)
+#define PCIE_CORE_INT_CRFO              BIT(4)
+#define PCIE_CORE_INT_RT                BIT(5)
+#define PCIE_CORE_INT_RTR               BIT(6)
+#define PCIE_CORE_INT_PE                BIT(7)
+#define PCIE_CORE_INT_MTR               BIT(8)
+#define PCIE_CORE_INT_UCR               BIT(9)
+#define PCIE_CORE_INT_FCE               BIT(10)
+#define PCIE_CORE_INT_CT                BIT(11)
+#define PCIE_CORE_INT_UTC               BIT(18)
+#define PCIE_CORE_INT_MMVC              BIT(19)
+
+#define ROCKCHIP_PCIE_RPIFR1_INTR_MASK  GENMASK(8, 5)
+#define ROCKCHIP_PCIE_RPIFR1_INTR_SHIFT 5
+
+#define PCIE_CORE_INT \
+		(PCIE_CORE_INT_PRFPE | PCIE_CORE_INT_CRFPE | \
+		 PCIE_CORE_INT_RRPE | PCIE_CORE_INT_CRFO | \
+		 PCIE_CORE_INT_RT | PCIE_CORE_INT_RTR | \
+		 PCIE_CORE_INT_PE | PCIE_CORE_INT_MTR | \
+		 PCIE_CORE_INT_UCR | PCIE_CORE_INT_FCE | \
+		 PCIE_CORE_INT_CT | PCIE_CORE_INT_UTC | \
+		 PCIE_CORE_INT_MMVC)
+
+#define PCIE_CLIENT_INT_SUBSYSTEM \
+	(PCIE_CLIENT_INT_PWR_STCG | PCIE_CLIENT_INT_HOT_PLUG | \
+	PCIE_CLIENT_INT_PHY | PCIE_CLIENT_INT_UDMA | \
+	PCIE_CLIENT_INT_LOCAL)
+
+#define PCIE_CLIENT_INT_LEGACY \
+	(PCIE_CLIENT_INT_INTA | PCIE_CLIENT_INT_INTB | \
+	PCIE_CLIENT_INT_INTC | PCIE_CLIENT_INT_INTD)
+
+#define PCIE_CLIENT_INT_CLI \
+	(PCIE_CLIENT_INT_CORR_ERR | PCIE_CLIENT_INT_NFATAL_ERR | \
+	PCIE_CLIENT_INT_FATAL_ERR | PCIE_CLIENT_INT_DPA | \
+	PCIE_CLIENT_INT_HOT_RST | PCIE_CLIENT_INT_MSG | \
+	PCIE_CLIENT_INT_LEGACY_DONE | PCIE_CLIENT_INT_LEGACY)
+
+struct rockchip_pcie_port {
+	void	__iomem *reg_base;
+	void	__iomem *apb_base;
+	struct	phy *phy;
+	struct	reset_control *core_rst;
+	struct	reset_control *mgmt_rst;
+	struct	reset_control *mgmt_sticky_rst;
+	struct	reset_control *pipe_rst;
+	struct	clk *aclk_pcie;
+	struct	clk *aclk_perf_pcie;
+	struct	clk *hclk_pcie;
+	struct	clk *clk_pcie_pm;
+	struct	regulator *vpcie3v3; /* 3.3V power supply */
+	struct	regulator *vpcie1v8; /* 1.8V power supply */
+	struct	regulator *vpcie0v9; /* 0.9V power supply */
+	struct	gpio_desc *ep_gpio;
+	u32	lanes;
+	u8	root_bus_nr;
+	struct	device *dev;
+	struct	irq_domain *irq_domain;
+};
+
+static irqreturn_t rockchip_pcie_subsys_irq_handler(int irq, void *arg);
+static irqreturn_t rockchip_pcie_client_irq_handler(int irq, void *arg);
+static void rockchip_pcie_legacy_int_handler(struct irq_desc *desc);
+
+static inline u32 pcie_read(struct rockchip_pcie_port *port, u32 reg)
+{
+	return readl(port->apb_base + reg);
+}
+
+static inline void pcie_write(struct rockchip_pcie_port *port,
+			      u32 val, u32 reg)
+{
+	writel(val, port->apb_base + reg);
+}
+
+static int rockchip_pcie_valid_config(struct rockchip_pcie_port *pp,
+				      struct pci_bus *bus, int dev)
+{
+	/* access only one slot on each root port */
+	if (bus->number == pp->root_bus_nr && dev > 0)
+		return 0;
+
+	/*
+	 * do not read more than one device on the bus directly attached
+	 * to RC's (Virtual Bridge's) DS side.
+	 */
+	if (bus->primary == pp->root_bus_nr && dev > 0)
+		return 0;
+
+	return 1;
+}
+
+static int rockchip_pcie_rd_own_conf(struct rockchip_pcie_port *pp,
+				     int where, int size,
+				     u32 *val)
+{
+	void __iomem *addr = pp->apb_base + PCIE_RC_CONFIG_BASE + where;
+
+	if (!IS_ALIGNED((uintptr_t)addr, size)) {
+		*val = 0;
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	}
+
+	if (size == 4) {
+		*val = readl(addr);
+	} else if (size == 2) {
+		*val = readw(addr);
+	} else if (size == 1) {
+		*val = readb(addr);
+	} else {
+		*val = 0;
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	}
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int rockchip_pcie_wr_own_conf(struct rockchip_pcie_port *pp,
+				     int where, int size, u32 val)
+{
+	u32 mask, tmp, offset;
+
+	offset = (where & (~0x3));
+
+	if (size == 4) {
+		writel(val, pp->apb_base + PCIE_RC_CONFIG_BASE + offset);
+		return PCIBIOS_SUCCESSFUL;
+	}
+
+	mask = ~(((1 << (size * 8)) - 1) << ((where & 0x3) * 8));
+
+	tmp = readl(pp->apb_base + PCIE_RC_CONFIG_BASE + offset) & mask;
+	tmp |= val << ((where & 0x3) * 8);
+	writel(tmp, pp->apb_base + PCIE_RC_CONFIG_BASE + offset);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int rockchip_pcie_rd_other_conf(struct rockchip_pcie_port *pp,
+				       struct pci_bus *bus, u32 devfn,
+				       int where, int size, u32 *val)
+{
+	u32 busdev;
+
+	busdev = PCIE_ECAM_ADDR(bus->number, PCI_SLOT(devfn),
+				PCI_FUNC(devfn), where);
+
+	if (!IS_ALIGNED(busdev, size)) {
+		*val = 0;
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	}
+
+	if (size == 4) {
+		*val = readl(pp->reg_base + busdev);
+	} else if (size == 2) {
+		*val = readw(pp->reg_base + busdev);
+	} else if (size == 1) {
+		*val = readb(pp->reg_base + busdev);
+	} else {
+		*val = 0;
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	}
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int rockchip_pcie_wr_other_conf(struct rockchip_pcie_port *pp,
+				       struct pci_bus *bus, u32 devfn,
+				       int where, int size, u32 val)
+{
+	u32 busdev;
+
+	busdev = PCIE_ECAM_ADDR(bus->number, PCI_SLOT(devfn),
+				PCI_FUNC(devfn), where);
+	if (!IS_ALIGNED(busdev, size))
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+
+	if (size == 4)
+		writel(val, pp->reg_base + busdev);
+	else if (size == 2)
+		writew(val, pp->reg_base + busdev);
+	else if (size == 1)
+		writeb(val, pp->reg_base + busdev);
+	else
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int rockchip_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
+				 int size, u32 *val)
+{
+	struct rockchip_pcie_port *pp = bus->sysdata;
+	int ret;
+
+	if (rockchip_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) {
+		*val = 0xffffffff;
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	}
+
+	if (bus->number != pp->root_bus_nr)
+		ret = rockchip_pcie_rd_other_conf(pp, bus, devfn,
+						  where, size, val);
+	else
+		ret = rockchip_pcie_rd_own_conf(pp, where, size, val);
+
+	return ret;
+}
+
+static int rockchip_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
+				 int where, int size, u32 val)
+{
+	struct rockchip_pcie_port *pp = bus->sysdata;
+	int ret;
+
+	if (rockchip_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	if (bus->number != pp->root_bus_nr)
+		ret = rockchip_pcie_wr_other_conf(pp, bus, devfn,
+						  where, size, val);
+	else
+		ret = rockchip_pcie_wr_own_conf(pp, where, size, val);
+
+	return ret;
+}
+
+static struct pci_ops rockchip_pcie_ops = {
+	.read = rockchip_pcie_rd_conf,
+	.write = rockchip_pcie_wr_conf,
+};
+
+/**
+ * rockchip_pcie_init_port - Initialize hardware
+ * @port: PCIe port information
+ */
+static int rockchip_pcie_init_port(struct rockchip_pcie_port *port)
+{
+	int err;
+	u32 status;
+	unsigned long timeout;
+
+	gpiod_set_value(port->ep_gpio, 0);
+
+	err = phy_init(port->phy);
+	if (err < 0) {
+		dev_err(port->dev, "fail to init phy, err %d\n", err);
+		return err;
+	}
+
+	err = reset_control_assert(port->core_rst);
+	if (err) {
+		dev_err(port->dev, "assert core_rst err %d\n", err);
+		return err;
+	}
+
+	err = reset_control_assert(port->mgmt_rst);
+	if (err) {
+		dev_err(port->dev, "assert mgmt_rst err %d\n", err);
+		return err;
+	}
+
+	err = reset_control_assert(port->mgmt_sticky_rst);
+	if (err) {
+		dev_err(port->dev, "assert mgmt_sticky_rst err %d\n", err);
+		return err;
+	}
+
+	err = reset_control_assert(port->pipe_rst);
+	if (err) {
+		dev_err(port->dev, "assert pipe_rst err %d\n", err);
+		return err;
+	}
+
+	pcie_write(port,
+		   HIWORD_UPDATE(PCIE_CLIENT_CONF_ENABLE,
+				 PCIE_CLIENT_CONF_ENABLE_MASK,
+				 PCIE_CLIENT_CONF_ENABLE_SHIFT) |
+		   HIWORD_UPDATE(PCIE_CLIENT_CONF_LANE_NUM(port->lanes),
+				 PCIE_CLIENT_CONF_LANE_NUM_MASK,
+				 PCIE_CLIENT_CONF_LANE_NUM_SHIFT) |
+		   HIWORD_UPDATE(PCIE_CLIENT_MODE_RC,
+				 PCIE_CLIENT_MODE_MASK,
+				 PCIE_CLIENT_MODE_SHIFT) |
+		   HIWORD_UPDATE(PCIE_CLIENT_ARI_ENABLE,
+				 PCIE_CLIENT_ARI_ENABLE_MASK,
+				 PCIE_CLIENT_ARI_ENABLE_SHIFT) |
+		   HIWORD_UPDATE(PCIE_CLIENT_GEN_SEL_2,
+				 PCIE_CLIENT_GEN_SEL_MASK,
+				 PCIE_CLIENT_GEN_SEL_SHIFT),
+		   PCIE_CLIENT_BASE);
+
+	err = phy_power_on(port->phy);
+	if (err) {
+		dev_err(port->dev, "fail to power on phy, err %d\n", err);
+		return err;
+	}
+
+	err = reset_control_deassert(port->core_rst);
+	if (err) {
+		dev_err(port->dev, "deassert core_rst err %d\n", err);
+		return err;
+	}
+
+	err = reset_control_deassert(port->mgmt_rst);
+	if (err) {
+		dev_err(port->dev, "deassert mgmt_rst err %d\n", err);
+		return err;
+	}
+
+	err = reset_control_deassert(port->mgmt_sticky_rst);
+	if (err) {
+		dev_err(port->dev, "deassert mgmt_sticky_rst err %d\n", err);
+		return err;
+	}
+
+	err = reset_control_deassert(port->pipe_rst);
+	if (err) {
+		dev_err(port->dev, "deassert pipe_rst err %d\n", err);
+		return err;
+	}
+
+	/* Enable Gen1 training */
+	pcie_write(port,
+		   HIWORD_UPDATE(PCIE_CLIENT_LINK_TRAIN_ENABLE,
+				 PCIE_CLIENT_LINK_TRAIN_MASK,
+				 PCIE_CLIENT_LINK_TRAIN_SHIFT),
+		   PCIE_CLIENT_BASE);
+
+	gpiod_set_value(port->ep_gpio, 1);
+
+	/* 500ms timeout value should be enough for gen1/2 taining */
+	timeout = jiffies + msecs_to_jiffies(500);
+
+	for (;;) {
+		status = pcie_read(port, PCIE_CLIENT_BASIC_STATUS1);
+		if (((status >> PCIE_CLIENT_LINK_STATUS_SHIFT) &
+		      PCIE_CLIENT_LINK_STATUS_MASK) ==
+		      PCIE_CLIENT_LINK_STATUS_UP) {
+			dev_dbg(port->dev, "pcie link training gen1 pass!\n");
+			break;
+		}
+
+		msleep(20);
+
+		if (!time_before(jiffies, timeout))
+			break;
+
+	}
+	status = pcie_read(port, PCIE_CLIENT_BASIC_STATUS1);
+	err = (((status >> PCIE_CLIENT_LINK_STATUS_SHIFT) &
+		 PCIE_CLIENT_LINK_STATUS_MASK) ==
+		 PCIE_CLIENT_LINK_STATUS_UP) ? 0 : -ETIMEDOUT;
+	if (err) {
+		dev_err(port->dev, "pcie link training gen1 timeout!\n");
+		return err;
+	}
+
+	/*
+	 * Enable retrain for gen2. This should be configured only after
+	 * gen1 finished.
+	 */
+	status = pcie_read(port,
+			   PCIE_RC_CONFIG_LCS + PCIE_RC_CONFIG_BASE);
+	status |= PCIE_CORE_LCSR_RETRAIN_LINK;
+	pcie_write(port, status,
+		   PCIE_RC_CONFIG_LCS + PCIE_RC_CONFIG_BASE);
+
+	timeout = jiffies + msecs_to_jiffies(500);
+	for (;;) {
+		status = pcie_read(port, PCIE_CORE_CTRL_MGMT_BASE);
+		if (((status >> PCIE_CORE_PL_CONF_SPEED_SHIFT) &
+		      PCIE_CORE_PL_CONF_SPEED_MASK) ==
+		      PCIE_CORE_PL_CONF_SPEED_5G) {
+			dev_dbg(port->dev, "pcie link training gen2 pass!\n");
+			break;
+		}
+
+		msleep(20);
+
+		if (!time_before(jiffies, timeout))
+			break;
+	}
+	status = pcie_read(port, PCIE_CORE_CTRL_MGMT_BASE);
+	err = (((status >> PCIE_CORE_PL_CONF_SPEED_SHIFT) &
+		PCIE_CORE_PL_CONF_SPEED_MASK) ==
+		PCIE_CORE_PL_CONF_SPEED_5G) ? 0 : -ETIMEDOUT;
+	if (err)
+		dev_dbg(port->dev, "pcie link training gen2 timeout, fall back to gen1!\n");
+
+	/* Check the final link with from negotiated lane counter from MGMT */
+	status = pcie_read(port, PCIE_CORE_CTRL_MGMT_BASE);
+	status =  0x1 << ((status >> PCIE_CORE_PL_CONF_LANE_SHIFT) &
+			   PCIE_CORE_PL_CONF_LANE_MASK);
+	dev_dbg(port->dev, "current link width is x%d\n", status);
+
+	pcie_write(port, ROCKCHIP_VENDOR_ID, PCIE_RC_CONFIG_BASE);
+	pcie_write(port, PCI_CLASS_BRIDGE_PCI << PCIE_CORE_RC_CONF_SCC_SHIFT,
+		   PCIE_RC_CONFIG_BASE + PCIE_RC_CONFIG_RID_CCR);
+	pcie_write(port, 0x0, PCIE_CORE_CTRL_MGMT_BASE + PCIE_RC_BAR_CONF);
+
+	pcie_write(port, (RC_REGION_0_ADDR_TRANS_L + RC_REGION_0_PASS_BITS),
+		   PCIE_CORE_AXI_CONF_BASE);
+	pcie_write(port, RC_REGION_0_ADDR_TRANS_H,
+		   PCIE_CORE_AXI_CONF_BASE + PCIE_CORE_OB_REGION_ADDR1);
+	pcie_write(port, 0x0080000a,
+		   PCIE_CORE_AXI_CONF_BASE + PCIE_CORE_OB_REGION_DESC0);
+	pcie_write(port, 0x0,
+		   PCIE_CORE_AXI_CONF_BASE + PCIE_CORE_OB_REGION_DESC1);
+
+	return 0;
+}
+
+/**
+ * rockchip_pcie_parse_dt - Parse Device tree
+ * @port: PCIe port information
+ *
+ * Return: '0' on success and error value on failure
+ */
+static int rockchip_pcie_parse_dt(struct rockchip_pcie_port *port)
+{
+	struct device *dev = port->dev;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct device_node *node = dev->of_node;
+	struct resource *regs;
+	int irq;
+	int err = -ENODEV;
+
+	regs = platform_get_resource_byname(pdev,
+					    IORESOURCE_MEM,
+					    "axi-base");
+	if (!regs) {
+		dev_err(dev, "missing axi-base property\n");
+		return err;
+	}
+
+	port->reg_base = devm_ioremap_resource(dev, regs);
+	if (IS_ERR(port->reg_base))
+		return PTR_ERR(port->reg_base);
+
+	regs = platform_get_resource_byname(pdev,
+					    IORESOURCE_MEM,
+					    "apb-base");
+	if (!regs) {
+		dev_err(dev, "missing apb-base property\n");
+		return err;
+	}
+
+	port->apb_base = devm_ioremap_resource(dev, regs);
+	if (IS_ERR(port->apb_base))
+		return PTR_ERR(port->apb_base);
+
+	port->phy = devm_phy_get(dev, "pcie-phy");
+	if (IS_ERR(port->phy)) {
+		if (PTR_ERR(port->phy) != -EPROBE_DEFER)
+			dev_err(dev, "Missing phy\n");
+		return PTR_ERR(port->phy);
+	}
+
+	port->lanes = 1;
+	err = of_property_read_u32(node, "num-lanes", &port->lanes);
+	if (!err && ((port->lanes == 0) ||
+		     (port->lanes == 3) ||
+		     (port->lanes > 4))) {
+		dev_warn(dev, "invalid num-lanes, default use one lane\n");
+		port->lanes = 1;
+	}
+
+	port->core_rst = devm_reset_control_get(dev, "core");
+	if (IS_ERR(port->core_rst)) {
+		if (PTR_ERR(port->core_rst) != -EPROBE_DEFER)
+			dev_err(dev, "missing core rst property in node %s\n",
+				node->name);
+		return PTR_ERR(port->core_rst);
+	}
+
+	port->mgmt_rst = devm_reset_control_get(dev, "mgmt");
+	if (IS_ERR(port->mgmt_rst)) {
+		if (PTR_ERR(port->mgmt_rst) != -EPROBE_DEFER)
+			dev_err(dev, "missing mgmt rst property in node %s\n",
+				node->name);
+		return PTR_ERR(port->mgmt_rst);
+	}
+
+	port->mgmt_sticky_rst = devm_reset_control_get(dev, "mgmt-sticky");
+	if (IS_ERR(port->mgmt_sticky_rst)) {
+		if (PTR_ERR(port->mgmt_sticky_rst) != -EPROBE_DEFER)
+			dev_err(dev, "missing mgmt-sticky rst property in node %s\n",
+				node->name);
+		return PTR_ERR(port->mgmt_sticky_rst);
+	}
+
+	port->pipe_rst = devm_reset_control_get(dev, "pipe");
+	if (IS_ERR(port->pipe_rst)) {
+		if (PTR_ERR(port->pipe_rst) != -EPROBE_DEFER)
+			dev_err(dev, "missing pipe rst property in node %s\n",
+				node->name);
+		return PTR_ERR(port->pipe_rst);
+	}
+
+	port->ep_gpio = devm_gpiod_get(dev, "ep", GPIOD_OUT_HIGH);
+	if (IS_ERR(port->ep_gpio)) {
+		dev_err(dev, "missing ep-gpios property in node %s\n",
+			node->name);
+		return PTR_ERR(port->ep_gpio);
+	}
+
+	port->aclk_pcie = devm_clk_get(dev, "aclk");
+	if (IS_ERR(port->aclk_pcie)) {
+		dev_err(dev, "aclk clock not found.\n");
+		return PTR_ERR(port->aclk_pcie);
+	}
+
+	port->aclk_perf_pcie = devm_clk_get(dev, "aclk-perf");
+	if (IS_ERR(port->aclk_perf_pcie)) {
+		dev_err(dev, "aclk_perf clock not found.\n");
+		return PTR_ERR(port->aclk_perf_pcie);
+	}
+
+	port->hclk_pcie = devm_clk_get(dev, "hclk");
+	if (IS_ERR(port->hclk_pcie)) {
+		dev_err(dev, "hclk clock not found.\n");
+		return PTR_ERR(port->hclk_pcie);
+	}
+
+	port->clk_pcie_pm = devm_clk_get(dev, "pm");
+	if (IS_ERR(port->clk_pcie_pm)) {
+		dev_err(dev, "pm clock not found.\n");
+		return PTR_ERR(port->clk_pcie_pm);
+	}
+
+	irq = platform_get_irq_byname(pdev, "sys");
+	if (irq < 0) {
+		dev_err(dev, "missing pcie_sys IRQ resource\n");
+		return -EINVAL;
+	}
+
+	err = devm_request_irq(dev, irq, rockchip_pcie_subsys_irq_handler,
+			       IRQF_SHARED, "pcie-sys", port);
+	if (err) {
+		dev_err(dev, "failed to request pcie subsystem irq\n");
+		return err;
+	}
+
+	irq = platform_get_irq_byname(pdev, "legacy");
+	if (irq < 0) {
+		dev_err(dev, "missing pcie_legacy IRQ resource\n");
+		return -EINVAL;
+	}
+
+	irq_set_chained_handler_and_data(irq,
+					 rockchip_pcie_legacy_int_handler,
+					 port);
+
+	irq = platform_get_irq_byname(pdev, "client");
+	if (irq < 0) {
+		dev_err(dev, "missing pcie-client IRQ resource\n");
+		return -EINVAL;
+	}
+
+	err = devm_request_irq(dev, irq, rockchip_pcie_client_irq_handler,
+			       IRQF_SHARED, "pcie-client", port);
+	if (err) {
+		dev_err(dev, "failed to request pcie client irq\n");
+		return err;
+	}
+
+	port->vpcie3v3 = devm_regulator_get_optional(dev, "vpcie3v3");
+	if (IS_ERR(port->vpcie3v3)) {
+		if (PTR_ERR(port->vpcie3v3) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+		dev_info(dev, "No vpcie3v3 regulator found.\n");
+	}
+
+	port->vpcie1v8 = devm_regulator_get_optional(dev, "vpcie1v8");
+	if (IS_ERR(port->vpcie1v8)) {
+		if (PTR_ERR(port->vpcie1v8) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+		dev_info(dev, "No vpcie1v8 regulator found.\n");
+	}
+
+	port->vpcie0v9 = devm_regulator_get_optional(dev, "vpcie0v9");
+	if (IS_ERR(port->vpcie0v9)) {
+		if (PTR_ERR(port->vpcie0v9) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+		dev_info(dev, "No vpcie0v9 regulator found.\n");
+	}
+
+	return 0;
+}
+
+static int rockchip_pcie_set_vpcie(struct rockchip_pcie_port *port)
+{
+	int err;
+
+	if (!IS_ERR(port->vpcie3v3)) {
+		err = regulator_enable(port->vpcie3v3);
+		if (err) {
+			dev_err(port->dev, "Fail to enable vpcie3v3 regulator.\n");
+			goto err_out;
+		}
+	}
+
+	if (!IS_ERR(port->vpcie1v8)) {
+		err = regulator_enable(port->vpcie1v8);
+		if (err) {
+			dev_err(port->dev, "Fail to enable vpcie1v8 regulator.\n");
+			goto err_disable_3v3;
+		}
+	}
+
+	if (!IS_ERR(port->vpcie0v9)) {
+		err = regulator_enable(port->vpcie0v9);
+		if (err) {
+			dev_err(port->dev, "Fail to enable vpcie0v9 regulator.\n");
+			goto err_disable_1v8;
+		}
+	}
+
+	return 0;
+
+err_disable_1v8:
+	if (!IS_ERR(port->vpcie1v8))
+		regulator_disable(port->vpcie1v8);
+err_disable_3v3:
+	if (!IS_ERR(port->vpcie3v3))
+		regulator_disable(port->vpcie3v3);
+err_out:
+	return err;
+}
+
+static void rockchip_pcie_enable_interrupts(struct rockchip_pcie_port *port)
+{
+	pcie_write(port, (PCIE_CLIENT_INT_CLI << 16) &
+		   (~PCIE_CLIENT_INT_CLI), PCIE_CLIENT_INT_MASK);
+	pcie_write(port, PCIE_CORE_INT, PCIE_CORE_INT_MASK);
+}
+
+static int rockchip_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
+				  irq_hw_number_t hwirq)
+{
+	irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq);
+	irq_set_chip_data(irq, domain->host_data);
+
+	return 0;
+}
+
+static const struct irq_domain_ops intx_domain_ops = {
+	.map = rockchip_pcie_intx_map,
+};
+
+static int rockchip_pcie_init_irq_domain(struct rockchip_pcie_port *pp)
+{
+	struct device *dev = pp->dev;
+
+	pp->irq_domain = irq_domain_add_linear(dev->of_node, 4,
+					       &intx_domain_ops, pp);
+	if (!pp->irq_domain) {
+		dev_err(dev, "Failed to get a INTx IRQ domain\n");
+		return PTR_ERR(pp->irq_domain);
+	}
+
+	return 0;
+}
+
+static irqreturn_t rockchip_pcie_subsys_irq_handler(int irq, void *arg)
+{
+	struct rockchip_pcie_port *pp = arg;
+	u32 reg;
+	u32 sub_reg;
+
+	reg = pcie_read(pp, PCIE_CLIENT_INT_STATUS);
+	if (reg & PCIE_CLIENT_INT_LOCAL) {
+		dev_dbg(pp->dev, "local interrupt recived\n");
+		sub_reg = pcie_read(pp, PCIE_CORE_INT_STATUS);
+		if (sub_reg & PCIE_CORE_INT_PRFPE)
+			dev_dbg(pp->dev, "Parity error detected while reading from the PNP Receive FIFO RAM\n");
+
+		if (sub_reg & PCIE_CORE_INT_CRFPE)
+			dev_dbg(pp->dev, "Parity error detected while reading from the Completion Receive FIFO RAM\n");
+
+		if (sub_reg & PCIE_CORE_INT_RRPE)
+			dev_dbg(pp->dev, "Parity error detected while reading from Replay Buffer RAM\n");
+
+		if (sub_reg & PCIE_CORE_INT_PRFO)
+			dev_dbg(pp->dev, "Overflow occurred in the PNP Receive FIFO\n");
+
+		if (sub_reg & PCIE_CORE_INT_CRFO)
+			dev_dbg(pp->dev, "Overflow occurred in the Completion Receive FIFO\n");
+
+		if (sub_reg & PCIE_CORE_INT_RT)
+			dev_dbg(pp->dev, "Replay timer timed out\n");
+
+		if (sub_reg & PCIE_CORE_INT_RTR)
+			dev_dbg(pp->dev, "Replay timer rolled over after 4 transmissions of the same TLP\n");
+
+		if (sub_reg & PCIE_CORE_INT_PE)
+			dev_dbg(pp->dev, "Phy error detected on receive side\n");
+
+		if (sub_reg & PCIE_CORE_INT_MTR)
+			dev_dbg(pp->dev, "Malformed TLP received from the link\n");
+
+		if (sub_reg & PCIE_CORE_INT_UCR)
+			dev_dbg(pp->dev, "Malformed TLP received from the link\n");
+
+		if (sub_reg & PCIE_CORE_INT_FCE)
+			dev_dbg(pp->dev, "An error was observed in the flow control advertisements from the other side\n");
+
+		if (sub_reg & PCIE_CORE_INT_CT)
+			dev_dbg(pp->dev, "A request timed out waiting for completion\n");
+
+		if (sub_reg & PCIE_CORE_INT_UTC)
+			dev_dbg(pp->dev, "Unmapped TC error\n");
+
+		if (sub_reg & PCIE_CORE_INT_MMVC)
+			dev_dbg(pp->dev, "MSI mask register changes\n");
+
+		pcie_write(pp, sub_reg, PCIE_CORE_INT_STATUS);
+	}
+
+	pcie_write(pp, PCIE_CLIENT_INT_SUBSYSTEM | PCIE_CLIENT_INT_LOCAL,
+		   PCIE_CLIENT_INT_STATUS);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t rockchip_pcie_client_irq_handler(int irq, void *arg)
+{
+	struct rockchip_pcie_port *pp = arg;
+	u32 reg;
+
+	reg = pcie_read(pp, PCIE_CLIENT_INT_STATUS);
+	if (reg & PCIE_CLIENT_INT_LEGACY_DONE)
+		dev_dbg(pp->dev, "legacy done interrupt recived\n");
+
+	if (reg & PCIE_CLIENT_INT_MSG)
+		dev_dbg(pp->dev, "message done interrupt recived\n");
+
+	if (reg & PCIE_CLIENT_INT_HOT_RST)
+		dev_dbg(pp->dev, "hot reset interrupt recived\n");
+
+	if (reg & PCIE_CLIENT_INT_DPA)
+		dev_dbg(pp->dev, "dpa interrupt recived\n");
+
+	if (reg & PCIE_CLIENT_INT_FATAL_ERR)
+		dev_dbg(pp->dev, "fatal error interrupt recived\n");
+
+	if (reg & PCIE_CLIENT_INT_NFATAL_ERR)
+		dev_dbg(pp->dev, "no fatal error interrupt recived\n");
+
+	if (reg & PCIE_CLIENT_INT_CORR_ERR)
+		dev_dbg(pp->dev, "correctable error interrupt recived\n");
+
+	pcie_write(pp, reg, PCIE_CLIENT_INT_STATUS);
+
+	return IRQ_HANDLED;
+}
+
+static void rockchip_pcie_legacy_int_handler(struct irq_desc *desc)
+{
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	struct rockchip_pcie_port *port;
+	u32 reg;
+	u32 hwirq;
+	u32 virq;
+
+	chained_irq_enter(chip, desc);
+	port = irq_desc_get_handler_data(desc);
+
+	reg = pcie_read(port, PCIE_CLIENT_INT_STATUS);
+	reg = (reg & ROCKCHIP_PCIE_RPIFR1_INTR_MASK) >>
+	       ROCKCHIP_PCIE_RPIFR1_INTR_SHIFT;
+
+	while (reg) {
+		hwirq = ffs(reg);
+		reg &= ~BIT(hwirq);
+
+		virq = irq_find_mapping(port->irq_domain, hwirq);
+		if (virq)
+			generic_handle_irq(virq);
+		else
+			dev_err(port->dev, "unexpected IRQ, INT%d\n", hwirq);
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static int rockchip_pcie_prog_ob_atu(struct rockchip_pcie_port *pp,
+				     int region_no,
+				     int type, u8 num_pass_bits,
+				     u32 lower_addr, u32 upper_addr)
+{
+	u32 ob_addr_0 = 0;
+	u32 ob_addr_1 = 0;
+	u32 ob_desc_0 = 0;
+	u32 ob_desc_1 = 0;
+	void __iomem *aw_base;
+
+	if (!pp)
+		return -EINVAL;
+	if (region_no >= MAX_AXI_WRAPPER_REGION_NUM)
+		return -EINVAL;
+	if ((num_pass_bits + 1) < 8)
+		return -EINVAL;
+	if (num_pass_bits > 63)
+		return -EINVAL;
+	if (region_no == 0) {
+		if (AXI_REGION_0_SIZE < (2ULL << num_pass_bits))
+		return -EINVAL;
+	}
+	if (region_no != 0) {
+		if (AXI_REGION_SIZE < (2ULL << num_pass_bits))
+		return -EINVAL;
+	}
+	aw_base = pp->apb_base + PCIE_CORE_AXI_CONF_BASE;
+	aw_base += (region_no << OB_REG_SIZE_SHIFT);
+
+	ob_addr_0 = (ob_addr_0 &
+		     ~0x0000003fU) | (num_pass_bits &
+		     0x0000003fU);
+	ob_addr_0 = (ob_addr_0 &
+		     ~0xffffff00U) | (lower_addr & 0xffffff00U);
+	ob_addr_1 = upper_addr;
+	ob_desc_0 = (1 << 23 | type);
+
+	writel(ob_addr_0, aw_base);
+	writel(ob_addr_1, aw_base + PCIE_CORE_OB_REGION_ADDR1);
+	writel(ob_desc_0, aw_base + PCIE_CORE_OB_REGION_DESC0);
+	writel(ob_desc_1, aw_base + PCIE_CORE_OB_REGION_DESC1);
+
+	return 0;
+}
+
+static int rockchip_pcie_prog_ib_atu(struct rockchip_pcie_port *pp,
+				     int region_no,
+				     u8 num_pass_bits,
+				     u32 lower_addr,
+				     u32 upper_addr)
+{
+	u32 ib_addr_0 = 0;
+	u32 ib_addr_1 = 0;
+	void __iomem *aw_base;
+
+	if (!pp)
+		return -EINVAL;
+	if (region_no > MAX_AXI_IB_ROOTPORT_REGION_NUM)
+		return -EINVAL;
+	if ((num_pass_bits + 1) < MIN_AXI_ADDR_BITS_PASSED)
+		return -EINVAL;
+	if (num_pass_bits > 63)
+		return -EINVAL;
+	aw_base = pp->apb_base + PCIE_CORE_AXI_INBOUND_BASE;
+	aw_base += (region_no << IB_ROOT_PORT_REG_SIZE_SHIFT);
+	ib_addr_0 = (ib_addr_0 &
+		     ~0x0000003fU) | (num_pass_bits &
+		     0x0000003fU);
+
+	ib_addr_0 = (ib_addr_0 & ~0xffffff00U) |
+		     ((lower_addr << 8) & 0xffffff00U);
+	ib_addr_1 = upper_addr;
+
+	writel(ib_addr_0, aw_base);
+	writel(ib_addr_1, aw_base + PCIE_RP_IB_ADDR_TRANS);
+
+	return 0;
+}
+
+static int rockchip_pcie_probe(struct platform_device *pdev)
+{
+	struct rockchip_pcie_port *port;
+	struct device *dev = &pdev->dev;
+	struct pci_bus *bus, *child;
+	struct resource_entry *win;
+	resource_size_t io_base;
+	struct resource	*busn = NULL;
+	struct resource	*mem = NULL;
+	struct resource	*io = NULL;
+	phys_addr_t io_bus_addr = 0;
+	u32 io_size = 0;
+	phys_addr_t mem_bus_addr = 0;
+	u32 mem_size = 0;
+	int reg_no = 0;
+	int err = 0;
+	int offset = 0;
+
+	LIST_HEAD(res);
+
+	if (!dev->of_node)
+		return -ENODEV;
+
+	port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	port->dev = dev;
+
+	err = rockchip_pcie_parse_dt(port);
+	if (err)
+		return err;
+
+	err = clk_prepare_enable(port->aclk_pcie);
+	if (err) {
+		dev_err(dev, "Unable to enable aclk_pcie clock.\n");
+		goto err_aclk_pcie;
+	}
+
+	err = clk_prepare_enable(port->aclk_perf_pcie);
+	if (err) {
+		dev_err(dev, "Unable to enable aclk_perf_pcie clock.\n");
+		goto err_aclk_perf_pcie;
+	}
+
+	err = clk_prepare_enable(port->hclk_pcie);
+	if (err) {
+		dev_err(dev, "Unable to enable hclk_pcie clock.\n");
+		goto err_hclk_pcie;
+	}
+
+	err = clk_prepare_enable(port->clk_pcie_pm);
+	if (err) {
+		dev_err(dev, "Unable to enable hclk_pcie clock.\n");
+		goto err_pcie_pm;
+	}
+
+	err = rockchip_pcie_set_vpcie(port);
+	if (err) {
+		dev_err(port->dev, "Fail to set vpcie regulator.\n");
+		goto err_set_vpcie;
+	}
+
+	err = rockchip_pcie_init_port(port);
+	if (err)
+		goto err_vpcie;
+
+	platform_set_drvdata(pdev, port);
+
+	rockchip_pcie_enable_interrupts(port);
+
+	err = rockchip_pcie_init_irq_domain(port);
+	if (err < 0)
+		goto err_vpcie;
+
+	err = of_pci_get_host_bridge_resources(dev->of_node, 0, 0xff,
+					       &res, &io_base);
+	if (err)
+		goto err_vpcie;
+
+	/* Get the I/O and memory ranges from DT */
+	resource_list_for_each_entry(win, &res) {
+		switch (resource_type(win->res)) {
+		case IORESOURCE_IO:
+			io = win->res;
+			io->name = "I/O";
+			io_size = resource_size(io);
+			io_bus_addr = io->start - win->offset;
+			err = pci_remap_iospace(io, io_base);
+			if (err) {
+				dev_warn(port->dev, "error %d: failed to map resource %pR\n",
+					 err, io);
+				continue;
+			}
+			break;
+		case IORESOURCE_MEM:
+			mem = win->res;
+			mem->name = "MEM";
+			mem_size = resource_size(mem);
+			mem_bus_addr = mem->start - win->offset;
+			break;
+		case IORESOURCE_BUS:
+			busn = win->res;
+			break;
+		default:
+			continue;
+		}
+	}
+
+	if (mem_size)
+		for (reg_no = 0; reg_no < (mem_size >> 20); reg_no++) {
+			err = rockchip_pcie_prog_ob_atu(port, reg_no + 1,
+							AXI_WRAPPER_MEM_WRITE,
+							20 - 1,
+							mem_bus_addr +
+							(reg_no << 20),
+							0);
+			if (err) {
+				dev_err(dev, "Program RC mem outbound atu failed\n");
+				goto err_vpcie;
+			}
+		}
+
+	err = rockchip_pcie_prog_ib_atu(port, 2, 32 - 1, 0x0, 0);
+	if (err) {
+		dev_err(dev, "Program RC mem inbound atu failed\n");
+		goto err_vpcie;
+	}
+
+	offset = mem_size >> 20;
+
+	if (io_size)
+		for (reg_no = 0; reg_no < (io_size >> 20); reg_no++) {
+			err = rockchip_pcie_prog_ob_atu(port,
+							reg_no + 1 + offset,
+							AXI_WRAPPER_IO_WRITE,
+							20 - 1,
+							io_bus_addr +
+							(reg_no << 20),
+							0);
+			if (err) {
+				dev_err(dev, "Program RC io outbound atu failed\n");
+				goto err_vpcie;
+			}
+		}
+
+	port->root_bus_nr = busn->start;
+
+	bus = pci_scan_root_bus(&pdev->dev, 0,
+				&rockchip_pcie_ops, port, &res);
+
+	if (!bus) {
+		err = -ENOMEM;
+		goto err_vpcie;
+	}
+
+	pci_bus_size_bridges(bus);
+	pci_bus_assign_resources(bus);
+	list_for_each_entry(child, &bus->children, node)
+		pcie_bus_configure_settings(child);
+
+	pci_bus_add_devices(bus);
+
+	return err;
+
+err_vpcie:
+	if (!IS_ERR(port->vpcie3v3))
+		regulator_disable(port->vpcie3v3);
+	if (!IS_ERR(port->vpcie1v8))
+		regulator_disable(port->vpcie1v8);
+	if (!IS_ERR(port->vpcie0v9))
+		regulator_disable(port->vpcie0v9);
+err_set_vpcie:
+	clk_disable_unprepare(port->clk_pcie_pm);
+err_pcie_pm:
+	clk_disable_unprepare(port->hclk_pcie);
+err_hclk_pcie:
+	clk_disable_unprepare(port->aclk_perf_pcie);
+err_aclk_perf_pcie:
+	clk_disable_unprepare(port->aclk_pcie);
+err_aclk_pcie:
+	return err;
+}
+
+static const struct of_device_id rockchip_pcie_of_match[] = {
+	{ .compatible = "rockchip,rk3399-pcie", },
+	{}
+};
+
+static struct platform_driver rockchip_pcie_driver = {
+	.driver = {
+		.name = "rockchip-pcie",
+		.of_match_table = rockchip_pcie_of_match,
+	},
+	.probe = rockchip_pcie_probe,
+
+};
+module_platform_driver(rockchip_pcie_driver);
+
+MODULE_AUTHOR("Rockchip Inc");
+MODULE_DESCRIPTION("Rockchip AXI PCIe driver");
+MODULE_LICENSE("GPL v2");
-- 
2.3.7




From sugar.zhang at rock-chips.com  Mon Jul  4 19:45:11 2016
From: sugar.zhang at rock-chips.com (Sugar Zhang)
Date: Tue,  5 Jul 2016 10:45:11 +0800
Subject: [PATCH] ASoC: rockchip: implement system suspend/resume for i2s
Message-ID: <1467686711-52347-1-git-send-email-sugar.zhang@rock-chips.com>

restore hw registers after power loss during a suspend/resume cycle.

Signed-off-by: Sugar Zhang <sugar.zhang at rock-chips.com>
---

 sound/soc/rockchip/rockchip_i2s.c | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c
index 574c6af..53970ac 100644
--- a/sound/soc/rockchip/rockchip_i2s.c
+++ b/sound/soc/rockchip/rockchip_i2s.c
@@ -614,9 +614,35 @@ static const struct of_device_id rockchip_i2s_match[] = {
 	{},
 };
 
+#ifdef CONFIG_PM_SLEEP
+static int rockchip_i2s_suspend(struct device *dev)
+{
+	struct rk_i2s_dev *i2s = dev_get_drvdata(dev);
+
+	regcache_mark_dirty(i2s->regmap);
+
+	return 0;
+}
+
+static int rockchip_i2s_resume(struct device *dev)
+{
+	struct rk_i2s_dev *i2s = dev_get_drvdata(dev);
+	int ret;
+
+	ret = pm_runtime_get_sync(dev);
+	if (ret < 0)
+		return ret;
+	ret = regcache_sync(i2s->regmap);
+	pm_runtime_put(dev);
+
+	return ret;
+}
+#endif
+
 static const struct dev_pm_ops rockchip_i2s_pm_ops = {
 	SET_RUNTIME_PM_OPS(i2s_runtime_suspend, i2s_runtime_resume,
 			   NULL)
+	SET_SYSTEM_SLEEP_PM_OPS(rockchip_i2s_suspend, rockchip_i2s_resume)
 };
 
 static struct platform_driver rockchip_i2s_driver = {
-- 
1.9.1




From sugar.zhang at rock-chips.com  Mon Jul  4 19:48:06 2016
From: sugar.zhang at rock-chips.com (Sugar Zhang)
Date: Tue,  5 Jul 2016 10:48:06 +0800
Subject: [PATCH] ASoC: rockchip: implement system suspend/resume for spdif
Message-ID: <1467686886-53257-1-git-send-email-sugar.zhang@rock-chips.com>

restore hw registers after power loss during a suspend/resume cycle.

Signed-off-by: Sugar Zhang <sugar.zhang at rock-chips.com>
---

 sound/soc/rockchip/rockchip_spdif.c | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c
index 100781e..00ae0e5 100644
--- a/sound/soc/rockchip/rockchip_spdif.c
+++ b/sound/soc/rockchip/rockchip_spdif.c
@@ -392,9 +392,35 @@ static int rk_spdif_remove(struct platform_device *pdev)
 	return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int rockchip_spdif_suspend(struct device *dev)
+{
+	struct rk_spdif_dev *spdif = dev_get_drvdata(dev);
+
+	regcache_mark_dirty(spdif->regmap);
+
+	return 0;
+}
+
+static int rockchip_spdif_resume(struct device *dev)
+{
+	struct rk_spdif_dev *spdif = dev_get_drvdata(dev);
+	int ret;
+
+	ret = pm_runtime_get_sync(dev);
+	if (ret < 0)
+		return ret;
+	ret = regcache_sync(spdif->regmap);
+	pm_runtime_put(dev);
+
+	return ret;
+}
+#endif
+
 static const struct dev_pm_ops rk_spdif_pm_ops = {
 	SET_RUNTIME_PM_OPS(rk_spdif_runtime_suspend, rk_spdif_runtime_resume,
 			   NULL)
+	SET_SYSTEM_SLEEP_PM_OPS(rockchip_spdif_suspend, rockchip_spdif_resume)
 };
 
 static struct platform_driver rk_spdif_driver = {
-- 
1.9.1




From zhangqing at rock-chips.com  Tue Jul  5 00:27:30 2016
From: zhangqing at rock-chips.com (Elaine Zhang)
Date: Tue, 05 Jul 2016 15:27:30 +0800
Subject: [RFC PATCH 2/3] clk: adjust clocks to their requested rate after
 parent changes
In-Reply-To: <1462206982-10444-3-git-send-email-heiko@sntech.de>
References: <1462206982-10444-1-git-send-email-heiko@sntech.de>
 <1462206982-10444-3-git-send-email-heiko@sntech.de>
Message-ID: <577B6162.3020609@rock-chips.com>



On 05/03/2016 12:36 AM, Heiko Stuebner wrote:
> Given a hirarchy of clk1 -> [div] -> clk2, when the rate of clk1 gets
> changed, clk2 changes as well as the divider stays the same. There may
> be cases where a user of clk2 needs it at a specific rate, so clk2
> needs to be readjusted for the changed rate of clk1.
>
> So if a rate was requested for the clock, and its rate changed during
> the underlying rate-change, with this change the clock framework now
> tries to readjust the rate back to/near the requested one.
>
> The whole process is protected by a new clock-flag to not force this
> behaviour change onto every clock defined in the ccf.
>
> Signed-off-by: Heiko Stuebner <heiko at sntech.de>
> ---
>   drivers/clk/clk.c            | 13 +++++++++++--
>   include/linux/clk-provider.h |  1 +
>   2 files changed, 12 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> index 65e0aad..22be369 100644
> --- a/drivers/clk/clk.c
> +++ b/drivers/clk/clk.c
> @@ -1410,6 +1410,9 @@ static struct clk_core *clk_propagate_rate_change(struct clk_core *core,
>   	return fail_clk;
>   }
>
> +static int clk_core_set_rate_nolock(struct clk_core *core,
> +				    unsigned long req_rate);
> +
>   /*
>    * walk down a subtree and set the new rates notifying the rate
>    * change on the way
> @@ -1494,6 +1497,12 @@ static void clk_change_rate(struct clk_core *core)
>   	/* handle the new child who might not be in core->children yet */
>   	if (core->new_child)
>   		clk_change_rate(core->new_child);
> +
> +	/* handle a changed clock that needs to readjust its rate */
> +	if (core->flags & CLK_KEEP_REQ_RATE && core->req_rate
> +					    && core->new_rate != old_rate
> +					    && core->new_rate != core->req_rate)
> +		clk_core_set_rate_nolock(core, core->req_rate);
>   }

I tests found a problem, about set the freq order.
e.p:
[VPLL]
     |
     ------ [div] ----- dclk_vop
If I change VPLL freq 148.5M to 594M, dclk_vop freq will changed as:
148.5M->24M->594M->1485.5M.
But we not hope the dclk_vop have a high freq,it will make the system 
crash or make vop not work well.

So if the VPLL is improve the freq, we need to set dclk_vop div first, 
and than set VPLL freq.
If VPLL is reduce the freq, we need to set vpll first,and set dclk_vop div.

This is just a example,for all change parent freq, we need follow this 
operation.
Do you have a better idea for this problem?

>
>   static int clk_core_set_rate_nolock(struct clk_core *core,
> @@ -1529,11 +1538,11 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
>   		return -EBUSY;
>   	}
>
> +	core->req_rate = req_rate;
> +
>   	/* change the rates */
>   	clk_change_rate(top);
>
> -	core->req_rate = req_rate;
> -
>   	return ret;
>   }
>
> diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
> index 0c72204..06f189e 100644
> --- a/include/linux/clk-provider.h
> +++ b/include/linux/clk-provider.h
> @@ -33,6 +33,7 @@
>   #define CLK_RECALC_NEW_RATES	BIT(9) /* recalc rates after notifications */
>   #define CLK_SET_RATE_UNGATE	BIT(10) /* clock needs to run to set rate */
>   #define CLK_IS_CRITICAL		BIT(11) /* do not gate, ever */
> +#define CLK_KEEP_REQ_RATE	BIT(12) /* keep reqrate on parent rate change */
>
>   struct clk;
>   struct clk_hw;
>



From arnd at arndb.de  Tue Jul  5 01:15:33 2016
From: arnd at arndb.de (Arnd Bergmann)
Date: Tue, 05 Jul 2016 10:15:33 +0200
Subject: [PATCH v5 2/2] PCI: Rockchip: Add Rockchip PCIe controller support
In-Reply-To: <1467686561-25638-2-git-send-email-shawn.lin@rock-chips.com>
References: <1467686561-25638-1-git-send-email-shawn.lin@rock-chips.com>
 <1467686561-25638-2-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <3471444.31RlTMvDfn@wuerfel>

On Tuesday, July 5, 2016 10:42:41 AM CEST Shawn Lin wrote:
> +config PCIE_ROCKCHIP
> +       tristate "Rockchip PCIe controller"
> +       depends on ARM64 && ARCH_ROCKCHIP
> +       depends on OF
> +       select MFD_SYSCON
> +       select PCI_MSI_IRQ_DOMAIN if PCI_MSI

Please use "depends on PCI_MSI_IRQ_DOMAIN" now, instead of "select PCI_MSI_IRQ_DOMAIN
if PCI_MSI", we have reworked how this is handled for consistency.

	Arnd


From robh at kernel.org  Tue Jul  5 07:23:50 2016
From: robh at kernel.org (Rob Herring)
Date: Tue, 5 Jul 2016 09:23:50 -0500
Subject: [PATCH] drivers/perf: arm-pmu: Handle per-interrupt affinity mask
In-Reply-To: <1467379291-18413-1-git-send-email-marc.zyngier@arm.com>
References: <1467379291-18413-1-git-send-email-marc.zyngier@arm.com>
Message-ID: <20160705142350.GA24441@rob-hp-laptop>

On Fri, Jul 01, 2016 at 02:21:31PM +0100, Marc Zyngier wrote:
> On a big-little system, PMUs can be wired to CPUs using per CPU
> interrups (PPI). In this case, it is important to make sure that
> the enable/disable do happen on the right set of CPUs.
> 
> So instead of relying on the interrupt-affinity property, we can
> use the actual percpu affinity that DT exposes as part of the
> interrupt specifier. The DT binding is also updated to reflect
> the fact that the interrupt-affinity property shouldn't be used
> in that case.
> 
> Signed-off-by: Marc Zyngier <marc.zyngier at arm.com>
> ---
>  Documentation/devicetree/bindings/arm/pmu.txt |  4 +++-

Acked-by: Rob Herring <robh at kernel.org>

>  drivers/perf/arm_pmu.c                        | 22 +++++++++++++++++-----
>  2 files changed, 20 insertions(+), 6 deletions(-)


From broonie at kernel.org  Tue Jul  5 07:30:40 2016
From: broonie at kernel.org (Mark Brown)
Date: Tue, 5 Jul 2016 16:30:40 +0200
Subject: [PATCH v3 10/14] regulator: pwm: Switch to the atomic PWM API
In-Reply-To: <1465895602-31008-11-git-send-email-boris.brezillon@free-electrons.com>
References: <1465895602-31008-1-git-send-email-boris.brezillon@free-electrons.com>
 <1465895602-31008-11-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <20160705143040.GX6247@sirena.org.uk>

On Tue, Jun 14, 2016 at 11:13:18AM +0200, Boris Brezillon wrote:

> Use the atomic API wherever appropriate and get rid of pwm_apply_args()
> call (the reference period and polarity are now explicitly set when
> calling pwm_apply_state()).

Acked-by: Mark Brown <broonie at kernel.org>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160705/47249be8/attachment.sig>

From broonie at kernel.org  Tue Jul  5 07:31:35 2016
From: broonie at kernel.org (Mark Brown)
Date: Tue, 5 Jul 2016 16:31:35 +0200
Subject: [PATCH v3 11/14] regulator: pwm: Properly initialize the ->state
 field
In-Reply-To: <1465895602-31008-12-git-send-email-boris.brezillon@free-electrons.com>
References: <1465895602-31008-1-git-send-email-boris.brezillon@free-electrons.com>
 <1465895602-31008-12-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <20160705143135.GY6247@sirena.org.uk>

On Tue, Jun 14, 2016 at 11:13:19AM +0200, Boris Brezillon wrote:
> The ->state field is currently initialized to 0, thus referencing the
> voltage selector at index 0, which might not reflect the current
> voltage value.
> If possible, retrieve the current voltage selector from the PWM state,
> else return -EINVAL.

Acked-by: Mark Brown <broonie at kernel.org>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160705/42b0fc2e/attachment-0001.sig>

From broonie at kernel.org  Tue Jul  5 07:32:09 2016
From: broonie at kernel.org (Mark Brown)
Date: Tue, 5 Jul 2016 16:32:09 +0200
Subject: [PATCH v3 12/14] regulator: pwm: Retrieve correct voltage
In-Reply-To: <1465895602-31008-13-git-send-email-boris.brezillon@free-electrons.com>
References: <1465895602-31008-1-git-send-email-boris.brezillon@free-electrons.com>
 <1465895602-31008-13-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <20160705143209.GZ6247@sirena.org.uk>

On Tue, Jun 14, 2016 at 11:13:20AM +0200, Boris Brezillon wrote:
> The continuous PWM voltage regulator is caching the voltage value in
> the ->volt_uV field. While most of the time this value should reflect the
> real voltage, sometime it can be sightly different if the PWM device
> rounded the set_duty_cycle request.
> Moreover, this value is not valid until someone has modified the regulator
> output.

Acked-by: Mark Brown <broonie at kernel.org>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160705/fe3ae844/attachment.sig>

From broonie at kernel.org  Tue Jul  5 07:34:23 2016
From: broonie at kernel.org (Mark Brown)
Date: Tue, 5 Jul 2016 16:34:23 +0200
Subject: [PATCH v3 13/14] regulator: pwm: Support extra continuous mode
 cases
In-Reply-To: <1465895602-31008-14-git-send-email-boris.brezillon@free-electrons.com>
References: <1465895602-31008-1-git-send-email-boris.brezillon@free-electrons.com>
 <1465895602-31008-14-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <20160705143423.GA6247@sirena.org.uk>

On Tue, Jun 14, 2016 at 11:13:21AM +0200, Boris Brezillon wrote:
> The continuous mode allows one to declare a PWM regulator without having
> to declare the voltage <-> dutycycle association table. It works fine as
> long as your voltage(dutycycle) function is linear, but also has the
> following constraints:

Acked-by: Mark Brown <broonie at kernel.org>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160705/28f0bfe8/attachment.sig>

From broonie at kernel.org  Tue Jul  5 07:36:09 2016
From: broonie at kernel.org (Mark Brown)
Date: Tue, 5 Jul 2016 16:36:09 +0200
Subject: [PATCH v3 14/14] regulator: pwm: Document pwm-dutycycle-unit and
 pwm-dutycycle-range
In-Reply-To: <1465895602-31008-15-git-send-email-boris.brezillon@free-electrons.com>
References: <1465895602-31008-1-git-send-email-boris.brezillon@free-electrons.com>
 <1465895602-31008-15-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <20160705143609.GB6247@sirena.org.uk>

On Tue, Jun 14, 2016 at 11:13:22AM +0200, Boris Brezillon wrote:
> Document the pwm-dutycycle-unit and pwm-dutycycle-range properties.

Acked-by: Mark Brown <broonie at kernel.org>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160705/043f326e/attachment.sig>

From robh at kernel.org  Tue Jul  5 13:38:44 2016
From: robh at kernel.org (Rob Herring)
Date: Tue, 5 Jul 2016 15:38:44 -0500
Subject: [PATCH v5 1/2] Documentation: bindings: add dt doc for Rockchip
 PCIe controller
In-Reply-To: <1467686561-25638-1-git-send-email-shawn.lin@rock-chips.com>
References: <1467686561-25638-1-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <20160705203844.GA25930@rob-hp-laptop>

On Tue, Jul 05, 2016 at 10:42:40AM +0800, Shawn Lin wrote:
> This patch adds a binding that describes the Rockchip PCIe controller
> found on Rockchip SoCs PCIe interface.
> 
> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
> 
> ---
> 
> Changes in v5:
> - fix wrong example reported by Marc
> - add seperate section to describe the interrupt controller child
>   node
> 
> Changes in v4:
> - fix example of adding intermediate interrupt controller for pcie
>   legacy interrrupt
> 
> Changes in v3:
> - fix example dts code suggested by Rob and Marc
> - remove driver's behaviour of regulator
> 
> Changes in v2:
> - fix lots clk/reset stuff suggested by Heiko
> - remove msi-parent and add msi-map suggested by Marc
> - drop phy related stuff
> - some others minor fixes
> 
>  .../devicetree/bindings/pci/rockchip-pcie.txt      | 104 +++++++++++++++++++++
>  1 file changed, 104 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/pci/rockchip-pcie.txt

Acked-by: Rob Herring <robh at kernel.org>


From heiko at sntech.de  Tue Jul  5 15:24:09 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Wed, 06 Jul 2016 00:24:09 +0200
Subject: [RFC PATCH 2/3] clk: adjust clocks to their requested rate after
 parent changes
In-Reply-To: <577B6162.3020609@rock-chips.com>
References: <1462206982-10444-1-git-send-email-heiko@sntech.de>
 <1462206982-10444-3-git-send-email-heiko@sntech.de>
 <577B6162.3020609@rock-chips.com>
Message-ID: <1601887.i7XxTCUQZu@phil>

Hi Elaine,

Am Dienstag, 5. Juli 2016, 15:27:30 schrieb Elaine Zhang:
> On 05/03/2016 12:36 AM, Heiko Stuebner wrote:
> > Given a hirarchy of clk1 -> [div] -> clk2, when the rate of clk1 gets
> > changed, clk2 changes as well as the divider stays the same. There may
> > be cases where a user of clk2 needs it at a specific rate, so clk2
> > needs to be readjusted for the changed rate of clk1.
> > 
> > So if a rate was requested for the clock, and its rate changed during
> > the underlying rate-change, with this change the clock framework now
> > tries to readjust the rate back to/near the requested one.
> > 
> > The whole process is protected by a new clock-flag to not force this
> > behaviour change onto every clock defined in the ccf.
> > 
> > Signed-off-by: Heiko Stuebner <heiko at sntech.de>
> > ---
> > 
> >   drivers/clk/clk.c            | 13 +++++++++++--
> >   include/linux/clk-provider.h |  1 +
> >   2 files changed, 12 insertions(+), 2 deletions(-)
> > 
> > diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> > index 65e0aad..22be369 100644
> > --- a/drivers/clk/clk.c
> > +++ b/drivers/clk/clk.c
> > @@ -1410,6 +1410,9 @@ static struct clk_core
> > *clk_propagate_rate_change(struct clk_core *core,> 
> >   	return fail_clk;
> >   
> >   }
> > 
> > +static int clk_core_set_rate_nolock(struct clk_core *core,
> > +				    unsigned long req_rate);
> > +
> > 
> >   /*
> >   
> >    * walk down a subtree and set the new rates notifying the rate
> >    * change on the way
> > 
> > @@ -1494,6 +1497,12 @@ static void clk_change_rate(struct clk_core
> > *core)
> > 
> >   	/* handle the new child who might not be in core->children yet */
> >   	if (core->new_child)
> >   	
> >   		clk_change_rate(core->new_child);
> > 
> > +
> > +	/* handle a changed clock that needs to readjust its rate */
> > +	if (core->flags & CLK_KEEP_REQ_RATE && core->req_rate
> > +					    && core->new_rate != old_rate
> > +					    && core->new_rate != core-
>req_rate)
> > +		clk_core_set_rate_nolock(core, core->req_rate);
> > 
> >   }
> 
> I tests found a problem, about set the freq order.
> e.p:
> [VPLL]
> 
>      ------ [div] ----- dclk_vop
> If I change VPLL freq 148.5M to 594M, dclk_vop freq will changed as:
> 148.5M->24M->594M->1485.5M.
> But we not hope the dclk_vop have a high freq,it will make the system
> crash or make vop not work well.
> 
> So if the VPLL is improve the freq, we need to set dclk_vop div first,
> and than set VPLL freq.
> If VPLL is reduce the freq, we need to set vpll first,and set dclk_vop
> div.
> 
> This is just a example,for all change parent freq, we need follow this
> operation.
> Do you have a better idea for this problem?

In general it seems my simplicistic approach only really works for really 
simple clock-setups and thus is likely not really usable for general things. 

For the VPLL on the rk3399 we were discussion a different approach in [0], 
as VPLL usage (aka which vop gets to control it) is likely to complicated to 
have this done in the clock-framework-

Doug wanted to take a look and add some thoughts and I guess he'll just do 
that after the 4th of july celebrations.


Heiko


[0] http://lists.infradead.org/pipermail/linux-rockchip/2016-June/010400.html



From zhangqing at rock-chips.com  Tue Jul  5 18:39:30 2016
From: zhangqing at rock-chips.com (Elaine Zhang)
Date: Wed, 06 Jul 2016 09:39:30 +0800
Subject: [RFC PATCH 2/3] clk: adjust clocks to their requested rate after
 parent changes
In-Reply-To: <1601887.i7XxTCUQZu@phil>
References: <1462206982-10444-1-git-send-email-heiko@sntech.de>
 <1462206982-10444-3-git-send-email-heiko@sntech.de>
 <577B6162.3020609@rock-chips.com> <1601887.i7XxTCUQZu@phil>
Message-ID: <577C6152.4010702@rock-chips.com>



On 07/06/2016 06:24 AM, Heiko Stuebner wrote:
> Hi Elaine,
>
> Am Dienstag, 5. Juli 2016, 15:27:30 schrieb Elaine Zhang:
>> On 05/03/2016 12:36 AM, Heiko Stuebner wrote:
>>> Given a hirarchy of clk1 -> [div] -> clk2, when the rate of clk1 gets
>>> changed, clk2 changes as well as the divider stays the same. There may
>>> be cases where a user of clk2 needs it at a specific rate, so clk2
>>> needs to be readjusted for the changed rate of clk1.
>>>
>>> So if a rate was requested for the clock, and its rate changed during
>>> the underlying rate-change, with this change the clock framework now
>>> tries to readjust the rate back to/near the requested one.
>>>
>>> The whole process is protected by a new clock-flag to not force this
>>> behaviour change onto every clock defined in the ccf.
>>>
>>> Signed-off-by: Heiko Stuebner <heiko at sntech.de>
>>> ---
>>>
>>>    drivers/clk/clk.c            | 13 +++++++++++--
>>>    include/linux/clk-provider.h |  1 +
>>>    2 files changed, 12 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
>>> index 65e0aad..22be369 100644
>>> --- a/drivers/clk/clk.c
>>> +++ b/drivers/clk/clk.c
>>> @@ -1410,6 +1410,9 @@ static struct clk_core
>>> *clk_propagate_rate_change(struct clk_core *core,>
>>>    	return fail_clk;
>>>
>>>    }
>>>
>>> +static int clk_core_set_rate_nolock(struct clk_core *core,
>>> +				    unsigned long req_rate);
>>> +
>>>
>>>    /*
>>>
>>>     * walk down a subtree and set the new rates notifying the rate
>>>     * change on the way
>>>
>>> @@ -1494,6 +1497,12 @@ static void clk_change_rate(struct clk_core
>>> *core)
>>>
>>>    	/* handle the new child who might not be in core->children yet */
>>>    	if (core->new_child)
>>>    	
>>>    		clk_change_rate(core->new_child);
>>>
>>> +
>>> +	/* handle a changed clock that needs to readjust its rate */
>>> +	if (core->flags & CLK_KEEP_REQ_RATE && core->req_rate
>>> +					    && core->new_rate != old_rate
>>> +					    && core->new_rate != core-
>> req_rate)
>>> +		clk_core_set_rate_nolock(core, core->req_rate);
>>>
>>>    }
>>
>> I tests found a problem, about set the freq order.
>> e.p:
>> [VPLL]
>>
>>       ------ [div] ----- dclk_vop
>> If I change VPLL freq 148.5M to 594M, dclk_vop freq will changed as:
>> 148.5M->24M->594M->1485.5M.
>> But we not hope the dclk_vop have a high freq,it will make the system
>> crash or make vop not work well.
>>
>> So if the VPLL is improve the freq, we need to set dclk_vop div first,
>> and than set VPLL freq.
>> If VPLL is reduce the freq, we need to set vpll first,and set dclk_vop
>> div.
>>
>> This is just a example,for all change parent freq, we need follow this
>> operation.
>> Do you have a better idea for this problem?
>
> In general it seems my simplicistic approach only really works for really
> simple clock-setups and thus is likely not really usable for general things.
>
> For the VPLL on the rk3399 we were discussion a different approach in [0],
> as VPLL usage (aka which vop gets to control it) is likely to complicated to
> have this done in the clock-framework-
>
> Doug wanted to take a look and add some thoughts and I guess he'll just do
> that after the 4th of july celebrations.
>
OK, Regardless of the VPLL and vop.
The current software of clock, there are some problems.
e.p:
[GPLL]
        ------ [div] ----- aclk_perilp
If I set GPLL 1200M use "assigned-clock-rate".My be the default div of 
aclk_perilp is 2, GPLL default freq is 600M.
If I set GPLL first,the aclk_perilp freq will changed as 
300M->600M->300M.But aclk_perilp 600M is a unsafety.It will make the 
system crash.
Aclk_perilp is sizeoff at 300M.It not allowed over frequency.

So if I init the PLL freq use "assigned-clock-rate", I need to list it's 
child clk, make sure it's child clk div is enough,make sure the child 
clk freq is below the  sizeoff freq.

Do you have a better idea for this problem?
>
> Heiko
>
>
> [0] http://lists.infradead.org/pipermail/linux-rockchip/2016-June/010400.html
>
>
>
>



From andy.yan at rock-chips.com  Tue Jul  5 20:15:43 2016
From: andy.yan at rock-chips.com (Andy Yan)
Date: Wed, 6 Jul 2016 11:15:43 +0800
Subject: [RESEND PATCH v5 1/5] mfd: RK808: Add RK818 support
In-Reply-To: <5759277D.7090101@phytec.de>
References: <1464850228-17244-1-git-send-email-w.egorov@phytec.de>
 <20160608141735.GE14888@dell> <5759277D.7090101@phytec.de>
Message-ID: <577C77DF.9060706@rock-chips.com>

Hi Wadim:

On 2016?06?09? 16:23, Wadim Egorov wrote:
> Hi,
>
> On 08.06.2016 16:17, Lee Jones wrote:
>> On Thu, 02 Jun 2016, Wadim Egorov wrote:
>>
>>> The RK818 chip is a power management IC for multimedia and handheld
>> "Power Management IC (PMIC)"
>>
>>> devices. It contains the following components:
>>>
>>> - Regulators
>>> - RTC
>>> - Clkout
>> Clocking
>>
>>> - battery support
>> Battery support
>>
>>> Both chips RK808 and RK818 are using a similar register map.
>> "Both RK808 ad RK818 chips"
>>
>>> So we can reuse the RTC and Clkout functionality.
>> Swap '.' for ','.
>>
>>> Signed-off-by: Wadim Egorov <w.egorov at phytec.de>
>>> ---
>>>   drivers/mfd/Kconfig       |   4 +-
>>>   drivers/mfd/rk808.c       | 231 ++++++++++++++++++++++++++++++++++++++--------
>>>   include/linux/mfd/rk808.h | 162 ++++++++++++++++++++++++++++++--
>>>   3 files changed, 350 insertions(+), 47 deletions(-)
>>>
>>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>>> index 1bcf601..7ba464b 100644
>>> --- a/drivers/mfd/Kconfig
>>> +++ b/drivers/mfd/Kconfig
>>> @@ -839,13 +839,13 @@ config MFD_RC5T583
>>>   	  different functionality of the device.
>>>   
>>>   config MFD_RK808
>>> -	tristate "Rockchip RK808 Power Management chip"
>>> +	tristate "Rockchip RK808/RK818 Power Management chip"
>> "Chip"
>>
>>>   	depends on I2C && OF
>>>   	select MFD_CORE
>>>   	select REGMAP_I2C
>>>   	select REGMAP_IRQ
>>>   	help
>>> -	  If you say yes here you get support for the RK808
>>> +	  If you say yes here you get support for the RK808 and RK818
>>>   	  Power Management chips.
>>>   	  This driver provides common support for accessing the device
>>>   	  through I2C interface. The device supports multiple sub-devices
>>> diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c
>>> index 49d7f62..3cf9724 100644
>>> --- a/drivers/mfd/rk808.c
>>> +++ b/drivers/mfd/rk808.c
>>> @@ -1,11 +1,15 @@
>>>   /*
>>> - * MFD core driver for Rockchip RK808
>>> + * MFD core driver for Rockchip RK808/RK818
>>>    *
>>>    * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
>>>    *
>>>    * Author: Chris Zhong <zyw at rock-chips.com>
>>>    * Author: Zhang Qing <zhangqing at rock-chips.com>
>>>    *
>>> + * Copyright (C) 2016 PHYTEC Messtechnik GmbH
>>> + *
>>> + * Author: Wadim Egorov <w.egorov at phytec.de>
>>> + *
>>>    * This program is free software; you can redistribute it and/or modify it
>>>    * under the terms and conditions of the GNU General Public License,
>>>    * version 2, as published by the Free Software Foundation.
>>> @@ -22,12 +26,7 @@
>>>   #include <linux/mfd/core.h>
>>>   #include <linux/module.h>
>>>   #include <linux/regmap.h>
>>> -
>>> -struct rk808_reg_data {
>>> -	int addr;
>>> -	int mask;
>>> -	int value;
>>> -};
>> Why are you moving this to the header?
> It is now part of the rk808 struct.
>
>>> +#include <linux/of_device.h>
>>>   
>>>   static bool rk808_is_volatile_reg(struct device *dev, unsigned int reg)
>>>   {
>>> @@ -57,6 +56,14 @@ static bool rk808_is_volatile_reg(struct device *dev, unsigned int reg)
>>>   	return false;
>>>   }
>>>   
>>> +static const struct regmap_config rk818_regmap_config = {
>>> +	.reg_bits = 8,
>>> +	.val_bits = 8,
>>> +	.max_register = RK818_USB_CTRL_REG,
>>> +	.cache_type = REGCACHE_RBTREE,
>>> +	.volatile_reg = rk808_is_volatile_reg,
>>> +};
>>> +
>>>   static const struct regmap_config rk808_regmap_config = {
>>>   	.reg_bits = 8,
>>>   	.val_bits = 8,
>>> @@ -83,7 +90,17 @@ static const struct mfd_cell rk808s[] = {
>>>   	},
>>>   };
>>>   
>>> -static const struct rk808_reg_data pre_init_reg[] = {
>>> +static const struct mfd_cell rk818s[] = {
>>> +	{ .name = "rk808-clkout", },
>> How does this differ to a normal -clock driver?
> I don't know. It is a normal clock driver.
>
>>> +	{ .name = "rk808-regulator", },
>>> +	{
>>> +		.name = "rk808-rtc",
>>> +		.num_resources = ARRAY_SIZE(rtc_resources),
>>> +		.resources = &rtc_resources[0],
>> .resources = rtc_resources,  ?
>>
>>> +	},
>>> +};
>>> +
>>> +static const struct rk8xx_reg_data rk808_pre_init_reg[] = {
>>>   	{ RK808_BUCK3_CONFIG_REG, BUCK_ILMIN_MASK,  BUCK_ILMIN_150MA },
>>>   	{ RK808_BUCK4_CONFIG_REG, BUCK_ILMIN_MASK,  BUCK_ILMIN_200MA },
>>>   	{ RK808_BOOST_CONFIG_REG, BOOST_ILMIN_MASK, BOOST_ILMIN_100MA },
>>> @@ -94,6 +111,22 @@ static const struct rk808_reg_data pre_init_reg[] = {
>>>   						    VB_LO_SEL_3500MV },
>>>   };
>>>   
>>> +static const struct rk8xx_reg_data rk818_pre_init_reg[] = {
>>> +	{ RK818_USB_CTRL_REG,	RK818_USB_ILIM_SEL_MASK,
>>> +						RK818_USB_ILMIN_2000MA },
>>> +	/* close charger when usb lower then 3.4V */
>>> +	{ RK818_USB_CTRL_REG,	RK818_USB_CHG_SD_VSEL_MASK, (0x7 << 4) },
>>> +	/* no action when vref */
>>> +	{ RK818_H5V_EN_REG,	BIT(1),		RK818_REF_RDY_CTRL },
>>> +	/* enable HDMI 5V */
>>> +	{ RK818_H5V_EN_REG,	BIT(0),		RK818_H5V_EN },
>>> +	/* improve efficiency */
>>> +	{ RK818_BUCK2_CONFIG_REG, BUCK2_RATE_MASK,	BUCK_ILMIN_250MA },
>>> +	{ RK818_BUCK4_CONFIG_REG, BUCK_ILMIN_MASK,	BUCK_ILMIN_250MA },
>>> +	{ RK818_BOOST_CONFIG_REG, BOOST_ILMIN_MASK,	BOOST_ILMIN_100MA },
>>> +	{ RK808_VB_MON_REG,	MASK_ALL,	VB_LO_ACT | VB_LO_SEL_3500MV },
>>> +};
>> The alignment here looks odd.
> I will fix it in the next version.
>
>>>   static const struct regmap_irq rk808_irqs[] = {
>>>   	/* INT_STS */
>>>   	[RK808_IRQ_VOUT_LO] = {
>>> @@ -136,6 +169,76 @@ static const struct regmap_irq rk808_irqs[] = {
>>>   	},
>>>   };
>>>   
>>> +static const struct regmap_irq rk818_irqs[] = {
>>> +	/* INT_STS */
>>> +	[RK818_IRQ_VOUT_LO] = {
>>> +		.mask = RK818_IRQ_VOUT_LO_MSK,
>>> +		.reg_offset = 0,
>>> +	},
>>> +	[RK818_IRQ_VB_LO] = {
>>> +		.mask = RK818_IRQ_VB_LO_MSK,
>>> +		.reg_offset = 0,
>>> +	},
>>> +	[RK818_IRQ_PWRON] = {
>>> +		.mask = RK818_IRQ_PWRON_MSK,
>>> +		.reg_offset = 0,
>>> +	},
>>> +	[RK818_IRQ_PWRON_LP] = {
>>> +		.mask = RK818_IRQ_PWRON_LP_MSK,
>>> +		.reg_offset = 0,
>>> +	},
>>> +	[RK818_IRQ_HOTDIE] = {
>>> +		.mask = RK818_IRQ_HOTDIE_MSK,
>>> +		.reg_offset = 0,
>>> +	},
>>> +	[RK818_IRQ_RTC_ALARM] = {
>>> +		.mask = RK818_IRQ_RTC_ALARM_MSK,
>>> +		.reg_offset = 0,
>>> +	},
>>> +	[RK818_IRQ_RTC_PERIOD] = {
>>> +		.mask = RK818_IRQ_RTC_PERIOD_MSK,
>>> +		.reg_offset = 0,
>>> +	},
>>> +	[RK818_IRQ_USB_OV] = {
>>> +		.mask = RK818_IRQ_USB_OV_MSK,
>>> +		.reg_offset = 0,
>>> +	},
>>> +
>>> +	/* INT_STS2 */
>>> +	[RK818_IRQ_PLUG_IN] = {
>>> +		.mask = RK818_IRQ_PLUG_IN_MSK,
>>> +		.reg_offset = 1,
>>> +	},
>>> +	[RK818_IRQ_PLUG_OUT] = {
>>> +		.mask = RK818_IRQ_PLUG_OUT_MSK,
>>> +		.reg_offset = 1,
>>> +	},
>>> +	[RK818_IRQ_CHG_OK] = {
>>> +		.mask = RK818_IRQ_CHG_OK_MSK,
>>> +		.reg_offset = 1,
>>> +	},
>>> +	[RK818_IRQ_CHG_TE] = {
>>> +		.mask = RK818_IRQ_CHG_TE_MSK,
>>> +		.reg_offset = 1,
>>> +	},
>>> +	[RK818_IRQ_CHG_TS1] = {
>>> +		.mask = RK818_IRQ_CHG_TS1_MSK,
>>> +		.reg_offset = 1,
>>> +	},
>>> +	[RK818_IRQ_TS2] = {
>>> +		.mask = RK818_IRQ_TS2_MSK,
>>> +		.reg_offset = 1,
>>> +	},
>>> +	[RK818_IRQ_CHG_CVTLIM] = {
>>> +		.mask = RK818_IRQ_CHG_CVTLIM_MSK,
>>> +		.reg_offset = 1,
>>> +	},
>>> +	[RK818_IRQ_DISCHG_ILIM] = {
>>> +		.mask = RK818_IRQ_DISCHG_ILIM_MSK,
>>> +		.reg_offset = 1,
>>> +	},
>>> +};
>>> +
>>>   static struct regmap_irq_chip rk808_irq_chip = {
>>>   	.name = "rk808",
>>>   	.irqs = rk808_irqs,
>>> @@ -148,6 +251,18 @@ static struct regmap_irq_chip rk808_irq_chip = {
>>>   	.init_ack_masked = true,
>>>   };
>>>   
>>> +static struct regmap_irq_chip rk818_irq_chip = {
>>> +	.name = "rk818",
>>> +	.irqs = rk818_irqs,
>>> +	.num_irqs = ARRAY_SIZE(rk818_irqs),
>>> +	.num_regs = 2,
>>> +	.irq_reg_stride = 2,
>>> +	.status_base = RK818_INT_STS_REG1,
>>> +	.mask_base = RK818_INT_STS_MSK_REG1,
>>> +	.ack_base = RK818_INT_STS_REG1,
>>> +	.init_ack_masked = true,
>>> +};
>>> +
>>>   static struct i2c_client *rk808_i2c_client;
>>>   static void rk808_device_shutdown(void)
>>>   {
>>> @@ -167,6 +282,48 @@ static void rk808_device_shutdown(void)
>>>   		dev_err(&rk808_i2c_client->dev, "power off error!\n");
>>>   }
>>>   
>>> +static const struct of_device_id rk808_of_match[] = {
>>> +	{ .compatible = "rockchip,rk808", .data = (void *) RK808_ID },
>>> +	{ .compatible = "rockchip,rk818", .data = (void *) RK818_ID },
>>> +	{ },
>>> +};
>>> +MODULE_DEVICE_TABLE(of, rk808_of_match);
>>> +
>>> +static int rk8xx_match_device(struct rk808 *rk808, struct device *dev)
>> If you're going to do it this way, please switch these parameters.
>>
>> It's more common to return the pointer however.
> If it's more common I will return a pointer to rk808.
>
>>> +{
>>> +	const struct of_device_id *of_id;
>>> +
>>> +	of_id = of_match_device(rk808_of_match, dev);
>>> +	if (!of_id) {
>>> +		dev_err(dev, "Unable to match OF ID\n");
>>> +		return -ENODEV;
>>> +	}
>>> +	rk808->variant = (long) of_id->data;
>>> +
>>> +	switch (rk808->variant) {
>>> +	case RK808_ID:
>>> +		rk808->nr_cells = ARRAY_SIZE(rk808s);
>>> +		rk808->cells = rk808s;
>>> +		rk808->regmap_cfg = &rk808_regmap_config;
>>> +		rk808->regmap_irq_chip = &rk808_irq_chip;
>>> +		rk808->pre_init_reg = rk808_pre_init_reg;
>>> +		rk808->nr_pre_init_regs = ARRAY_SIZE(rk808_pre_init_reg);
>>> +		break;
>>> +	case RK818_ID:
>>> +		rk808->nr_cells = ARRAY_SIZE(rk818s);
>>> +		rk808->cells = rk818s;
>>> +		rk808->regmap_cfg = &rk818_regmap_config;
>>> +		rk808->regmap_irq_chip = &rk818_irq_chip;
>>> +		rk808->pre_init_reg = rk818_pre_init_reg;
>>> +		rk808->nr_pre_init_regs = ARRAY_SIZE(rk818_pre_init_reg);
>>> +		break;
>>> +	default:
>>> +		dev_err(dev, "unsupported RK8XX ID %lu\n", rk808->variant);
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>>   static int rk808_probe(struct i2c_client *client,
>>>   		       const struct i2c_device_id *id)
>>>   {
>>> @@ -176,46 +333,52 @@ static int rk808_probe(struct i2c_client *client,
>>>   	int ret;
>>>   	int i;
>>>   
>>> -	if (!client->irq) {
>>> -		dev_err(&client->dev, "No interrupt support, no core IRQ\n");
>>> -		return -EINVAL;
>>> -	}
>>> -
>>>   	rk808 = devm_kzalloc(&client->dev, sizeof(*rk808), GFP_KERNEL);
>>>   	if (!rk808)
>>>   		return -ENOMEM;
>>>   
>>> -	rk808->regmap = devm_regmap_init_i2c(client, &rk808_regmap_config);
>>> +	ret = rk8xx_match_device(rk808, &client->dev);
>> Is there a way to dynamically probe the device?  No device ID you can
>> read directly from the silicon?
> AFAIK there is no device ID register. At least it is not documented in
> the manual.

register 0x17 and 0x18 is the ID register, 0x17 is the MSB, 0x18 is LSB

for RK818, register 0x17 is 0x81, 0x18 is 0x81
>
> Wadim
>
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	rk808->i2c = client;
>>> +	i2c_set_clientdata(client, rk808);
>>> +
>>> +	rk808->regmap = devm_regmap_init_i2c(client, rk808->regmap_cfg);
>>>   	if (IS_ERR(rk808->regmap)) {
>>>   		dev_err(&client->dev, "regmap initialization failed\n");
>>>   		return PTR_ERR(rk808->regmap);
>>>   	}
>>>   
>>> -	for (i = 0; i < ARRAY_SIZE(pre_init_reg); i++) {
>>> -		ret = regmap_update_bits(rk808->regmap, pre_init_reg[i].addr,
>>> -					 pre_init_reg[i].mask,
>>> -					 pre_init_reg[i].value);
>>> -		if (ret) {
>>> -			dev_err(&client->dev,
>>> -				"0x%x write err\n", pre_init_reg[i].addr);
>>> -			return ret;
>>> -		}
>>> +	if (!client->irq) {
>>> +		dev_err(&client->dev, "No interrupt support, no core IRQ\n");
>>> +		return -EINVAL;
>>>   	}
>>>   
>>>   	ret = regmap_add_irq_chip(rk808->regmap, client->irq,
>>>   				  IRQF_ONESHOT, -1,
>>> -				  &rk808_irq_chip, &rk808->irq_data);
>>> +				  rk808->regmap_irq_chip, &rk808->irq_data);
>>>   	if (ret) {
>>>   		dev_err(&client->dev, "Failed to add irq_chip %d\n", ret);
>>>   		return ret;
>>>   	}
>>>   
>>> -	rk808->i2c = client;
>>> -	i2c_set_clientdata(client, rk808);
>>> +	for (i = 0; i < rk808->nr_pre_init_regs; i++) {
>>> +		ret = regmap_update_bits(rk808->regmap,
>>> +					rk808->pre_init_reg[i].addr,
>>> +					rk808->pre_init_reg[i].mask,
>>> +					rk808->pre_init_reg[i].value);
>>> +		if (ret) {
>>> +			dev_err(&client->dev,
>>> +				"0x%x write err\n",
>>> +				rk808->pre_init_reg[i].addr);
>>> +			return ret;
>>> +		}
>>> +	}
>>>   
>>> -	ret = devm_mfd_add_devices(&client->dev, -1,
>>> -				   rk808s, ARRAY_SIZE(rk808s), NULL, 0,
>>> -				   regmap_irq_get_domain(rk808->irq_data));
>>> +	ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE,
>>> +			      rk808->cells, rk808->nr_cells, NULL, 0,
>>> +			      regmap_irq_get_domain(rk808->irq_data));
>>>   	if (ret) {
>>>   		dev_err(&client->dev, "failed to add MFD devices %d\n", ret);
>>>   		goto err_irq;
>>> @@ -245,14 +408,9 @@ static int rk808_remove(struct i2c_client *client)
>>>   	return 0;
>>>   }
>>>   
>>> -static const struct of_device_id rk808_of_match[] = {
>>> -	{ .compatible = "rockchip,rk808" },
>>> -	{ },
>>> -};
>>> -MODULE_DEVICE_TABLE(of, rk808_of_match);
>>> -
>>>   static const struct i2c_device_id rk808_ids[] = {
>>>   	{ "rk808" },
>>> +	{ "rk818" },
>>>   	{ },
>>>   };
>>>   MODULE_DEVICE_TABLE(i2c, rk808_ids);
>>> @@ -272,4 +430,5 @@ module_i2c_driver(rk808_i2c_driver);
>>>   MODULE_LICENSE("GPL");
>>>   MODULE_AUTHOR("Chris Zhong <zyw at rock-chips.com>");
>>>   MODULE_AUTHOR("Zhang Qing <zhangqing at rock-chips.com>");
>>> -MODULE_DESCRIPTION("RK808 PMIC driver");
>>> +MODULE_AUTHOR("Wadim Egorov <w.egorov at phytec.de>");
>>> +MODULE_DESCRIPTION("RK808/RK818 PMIC driver");
>>> diff --git a/include/linux/mfd/rk808.h b/include/linux/mfd/rk808.h
>>> index 441b6ee..5ce810c 100644
>>> --- a/include/linux/mfd/rk808.h
>>> +++ b/include/linux/mfd/rk808.h
>>> @@ -1,11 +1,15 @@
>>>   /*
>>> - * rk808.h for Rockchip RK808
>>> + * Register definitions for Rockchip's RK808/RK818 PMIC
>>>    *
>>>    * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
>>>    *
>>>    * Author: Chris Zhong <zyw at rock-chips.com>
>>>    * Author: Zhang Qing <zhangqing at rock-chips.com>
>>>    *
>>> + * Copyright (C) 2016 PHYTEC Messtechnik GmbH
>>> + *
>>> + * Author: Wadim Egorov <w.egorov at phytec.de>
>>> + *
>>>    * This program is free software; you can redistribute it and/or modify it
>>>    * under the terms and conditions of the GNU General Public License,
>>>    * version 2, as published by the Free Software Foundation.
>>> @@ -16,8 +20,8 @@
>>>    * more details.
>>>    */
>>>   
>>> -#ifndef __LINUX_REGULATOR_rk808_H
>>> -#define __LINUX_REGULATOR_rk808_H
>>> +#ifndef __LINUX_REGULATOR_RK808_H
>>> +#define __LINUX_REGULATOR_RK808_H
>>>   
>>>   #include <linux/regulator/machine.h>
>>>   #include <linux/regmap.h>
>>> @@ -28,7 +32,7 @@
>>>   
>>>   #define RK808_DCDC1	0 /* (0+RK808_START) */
>>>   #define RK808_LDO1	4 /* (4+RK808_START) */
>>> -#define RK808_NUM_REGULATORS   14
>>> +#define RK808_NUM_REGULATORS	14
>>>   
>>>   enum rk808_reg {
>>>   	RK808_ID_DCDC1,
>>> @@ -115,7 +119,92 @@ enum rk808_reg {
>>>   #define RK808_INT_STS_MSK_REG2	0x4f
>>>   #define RK808_IO_POL_REG	0x50
>>>   
>>> -/* IRQ Definitions */
>>> +/* RK818 */
>>> +#define RK818_DCDC1			0
>>> +#define RK818_LDO1			4
>>> +#define RK818_NUM_REGULATORS		17
>>> +
>>> +enum rk818_reg {
>>> +	RK818_ID_DCDC1,
>>> +	RK818_ID_DCDC2,
>>> +	RK818_ID_DCDC3,
>>> +	RK818_ID_DCDC4,
>>> +	RK818_ID_BOOST,
>>> +	RK818_ID_LDO1,
>>> +	RK818_ID_LDO2,
>>> +	RK818_ID_LDO3,
>>> +	RK818_ID_LDO4,
>>> +	RK818_ID_LDO5,
>>> +	RK818_ID_LDO6,
>>> +	RK818_ID_LDO7,
>>> +	RK818_ID_LDO8,
>>> +	RK818_ID_LDO9,
>>> +	RK818_ID_SWITCH,
>>> +	RK818_ID_HDMI_SWITCH,
>>> +	RK818_ID_OTG_SWITCH,
>>> +};
>>> +
>>> +#define RK818_DCDC_EN_REG		0x23
>>> +#define RK818_LDO_EN_REG		0x24
>>> +#define RK818_SLEEP_SET_OFF_REG1	0x25
>>> +#define RK818_SLEEP_SET_OFF_REG2	0x26
>>> +#define RK818_DCDC_UV_STS_REG		0x27
>>> +#define RK818_DCDC_UV_ACT_REG		0x28
>>> +#define RK818_LDO_UV_STS_REG		0x29
>>> +#define RK818_LDO_UV_ACT_REG		0x2a
>>> +#define RK818_DCDC_PG_REG		0x2b
>>> +#define RK818_LDO_PG_REG		0x2c
>>> +#define RK818_VOUT_MON_TDB_REG		0x2d
>>> +#define RK818_BUCK1_CONFIG_REG		0x2e
>>> +#define RK818_BUCK1_ON_VSEL_REG		0x2f
>>> +#define RK818_BUCK1_SLP_VSEL_REG	0x30
>>> +#define RK818_BUCK2_CONFIG_REG		0x32
>>> +#define RK818_BUCK2_ON_VSEL_REG		0x33
>>> +#define RK818_BUCK2_SLP_VSEL_REG	0x34
>>> +#define RK818_BUCK3_CONFIG_REG		0x36
>>> +#define RK818_BUCK4_CONFIG_REG		0x37
>>> +#define RK818_BUCK4_ON_VSEL_REG		0x38
>>> +#define RK818_BUCK4_SLP_VSEL_REG	0x39
>>> +#define RK818_BOOST_CONFIG_REG		0x3a
>>> +#define RK818_LDO1_ON_VSEL_REG		0x3b
>>> +#define RK818_LDO1_SLP_VSEL_REG		0x3c
>>> +#define RK818_LDO2_ON_VSEL_REG		0x3d
>>> +#define RK818_LDO2_SLP_VSEL_REG		0x3e
>>> +#define RK818_LDO3_ON_VSEL_REG		0x3f
>>> +#define RK818_LDO3_SLP_VSEL_REG		0x40
>>> +#define RK818_LDO4_ON_VSEL_REG		0x41
>>> +#define RK818_LDO4_SLP_VSEL_REG		0x42
>>> +#define RK818_LDO5_ON_VSEL_REG		0x43
>>> +#define RK818_LDO5_SLP_VSEL_REG		0x44
>>> +#define RK818_LDO6_ON_VSEL_REG		0x45
>>> +#define RK818_LDO6_SLP_VSEL_REG		0x46
>>> +#define RK818_LDO7_ON_VSEL_REG		0x47
>>> +#define RK818_LDO7_SLP_VSEL_REG		0x48
>>> +#define RK818_LDO8_ON_VSEL_REG		0x49
>>> +#define RK818_LDO8_SLP_VSEL_REG		0x4a
>>> +#define RK818_BOOST_LDO9_ON_VSEL_REG	0x54
>>> +#define RK818_BOOST_LDO9_SLP_VSEL_REG	0x55
>>> +#define RK818_DEVCTRL_REG		0x4b
>>> +#define RK818_INT_STS_REG1		0X4c
>>> +#define RK818_INT_STS_MSK_REG1		0x4d
>>> +#define RK818_INT_STS_REG2		0x4e
>>> +#define RK818_INT_STS_MSK_REG2		0x4f
>>> +#define RK818_IO_POL_REG		0x50
>>> +#define RK818_H5V_EN_REG		0x52
>>> +#define RK818_SLEEP_SET_OFF_REG3	0x53
>>> +#define RK818_BOOST_LDO9_ON_VSEL_REG	0x54
>>> +#define RK818_BOOST_LDO9_SLP_VSEL_REG	0x55
>>> +#define RK818_BOOST_CTRL_REG		0x56
>>> +#define RK818_DCDC_ILMAX		0x90
>>> +#define RK818_USB_CTRL_REG		0xa1
>>> +
>>> +#define RK818_H5V_EN			BIT(0)
>>> +#define RK818_REF_RDY_CTRL		BIT(1)
>>> +#define RK818_USB_ILIM_SEL_MASK		0xf
>>> +#define RK818_USB_ILMIN_2000MA		0x7
>>> +#define RK818_USB_CHG_SD_VSEL_MASK	0x70
>>> +
>>> +/* RK808 IRQ Definitions */
>>>   #define RK808_IRQ_VOUT_LO	0
>>>   #define RK808_IRQ_VB_LO		1
>>>   #define RK808_IRQ_PWRON		2
>>> @@ -137,6 +226,43 @@ enum rk808_reg {
>>>   #define RK808_IRQ_PLUG_IN_INT_MSK	BIT(0)
>>>   #define RK808_IRQ_PLUG_OUT_INT_MSK	BIT(1)
>>>   
>>> +/* RK818 IRQ Definitions */
>>> +#define RK818_IRQ_VOUT_LO	0
>>> +#define RK818_IRQ_VB_LO		1
>>> +#define RK818_IRQ_PWRON		2
>>> +#define RK818_IRQ_PWRON_LP	3
>>> +#define RK818_IRQ_HOTDIE	4
>>> +#define RK818_IRQ_RTC_ALARM	5
>>> +#define RK818_IRQ_RTC_PERIOD	6
>>> +#define RK818_IRQ_USB_OV	7
>>> +#define RK818_IRQ_PLUG_IN	8
>>> +#define RK818_IRQ_PLUG_OUT	9
>>> +#define RK818_IRQ_CHG_OK	10
>>> +#define RK818_IRQ_CHG_TE	11
>>> +#define RK818_IRQ_CHG_TS1	12
>>> +#define RK818_IRQ_TS2		13
>>> +#define RK818_IRQ_CHG_CVTLIM	14
>>> +#define RK818_IRQ_DISCHG_ILIM	7
>>> +
>>> +#define RK818_IRQ_VOUT_LO_MSK		BIT(0)
>>> +#define RK818_IRQ_VB_LO_MSK		BIT(1)
>>> +#define RK818_IRQ_PWRON_MSK		BIT(2)
>>> +#define RK818_IRQ_PWRON_LP_MSK		BIT(3)
>>> +#define RK818_IRQ_HOTDIE_MSK		BIT(4)
>>> +#define RK818_IRQ_RTC_ALARM_MSK		BIT(5)
>>> +#define RK818_IRQ_RTC_PERIOD_MSK	BIT(6)
>>> +#define RK818_IRQ_USB_OV_MSK		BIT(7)
>>> +#define RK818_IRQ_PLUG_IN_MSK		BIT(0)
>>> +#define RK818_IRQ_PLUG_OUT_MSK		BIT(1)
>>> +#define RK818_IRQ_CHG_OK_MSK		BIT(2)
>>> +#define RK818_IRQ_CHG_TE_MSK		BIT(3)
>>> +#define RK818_IRQ_CHG_TS1_MSK		BIT(4)
>>> +#define RK818_IRQ_TS2_MSK		BIT(5)
>>> +#define RK818_IRQ_CHG_CVTLIM_MSK	BIT(6)
>>> +#define RK818_IRQ_DISCHG_ILIM_MSK	BIT(7)
>>> +
>>> +#define RK818_NUM_IRQ		16
>>> +
>>>   #define RK808_VBAT_LOW_2V8	0x00
>>>   #define RK808_VBAT_LOW_2V9	0x01
>>>   #define RK808_VBAT_LOW_3V0	0x02
>>> @@ -191,9 +317,27 @@ enum {
>>>   	BOOST_ILMIN_250MA,
>>>   };
>>>   
>>> +enum {
>>> +	RK808_ID,
>>> +	RK818_ID,
>>> +};
>>> +
>>> +struct rk8xx_reg_data {
>>> +	int addr;
>>> +	int mask;
>>> +	int value;
>>> +};
>>> +
>>>   struct rk808 {
>>> -	struct i2c_client *i2c;
>>> -	struct regmap_irq_chip_data *irq_data;
>>> -	struct regmap *regmap;
>>> +	struct i2c_client		*i2c;
>>> +	struct regmap_irq_chip_data	*irq_data;
>>> +	struct regmap			*regmap;
>>> +	long				variant;
>>> +	int				nr_cells;
>>> +	const struct mfd_cell		*cells;
>>> +	const struct regmap_config	*regmap_cfg;
>>> +	const struct regmap_irq_chip	*regmap_irq_chip;
>>> +	const struct rk8xx_reg_data	*pre_init_reg;
>>> +	int				nr_pre_init_regs;
>>>   };
>>> -#endif /* __LINUX_REGULATOR_rk808_H */
>>> +#endif /* __LINUX_REGULATOR_RK808_H */
>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip
>
>
>




From shawn.lin at rock-chips.com  Wed Jul  6 00:16:37 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Wed,  6 Jul 2016 15:16:37 +0800
Subject: [PATCH v6 1/2] Documentation: bindings: add dt doc for Rockchip PCIe
 controller
Message-ID: <1467789398-13501-1-git-send-email-shawn.lin@rock-chips.com>

This patch adds a binding that describes the Rockchip PCIe controller
found on Rockchip SoCs PCIe interface.

Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>

Acked-by: Rob Herring <robh at kernel.org>
---

Changes in v6:
- add ack tag from Rob

Changes in v5:
- fix wrong example reported by Marc
- add seperate section to describe the interrupt controller child
  node

Changes in v4:
- fix example of adding intermediate interrupt controller for pcie
  legacy interrrupt

Changes in v3:
- fix example dts code suggested by Rob and Marc
- remove driver's behaviour of regulator

Changes in v2:
- fix lots clk/reset stuff suggested by Heiko
- remove msi-parent and add msi-map suggested by Marc
- drop phy related stuff
- some others minor fixes

 .../devicetree/bindings/pci/rockchip-pcie.txt      | 104 +++++++++++++++++++++
 1 file changed, 104 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/rockchip-pcie.txt

diff --git a/Documentation/devicetree/bindings/pci/rockchip-pcie.txt b/Documentation/devicetree/bindings/pci/rockchip-pcie.txt
new file mode 100644
index 0000000..7616ecc
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/rockchip-pcie.txt
@@ -0,0 +1,104 @@
+* Rockchip AXI PCIe Root Port Bridge DT description
+
+Required properties:
+- #address-cells: Address representation for root ports, set to <3>
+- #size-cells: Size representation for root ports, set to <2>
+- #interrupt-cells: specifies the number of cells needed to encode an
+		interrupt source. The value must be 1.
+- compatible: Should contain "rockchip,rk3399-pcie"
+- reg: Two register ranges as listed in the reg-names property
+- reg-names: Must include the following names
+	- "axi-base"
+	- "apb-base"
+- clocks: Must contain an entry for each entry in clock-names.
+		See ../clocks/clock-bindings.txt for details.
+- clock-names: Must include the following entries:
+	- "aclk"
+	- "aclk-perf"
+	- "hclk"
+	- "pm"
+- msi-map: Maps a Requester ID to an MSI controller and associated.
+		See ./pci-msi.txt
+- phys: From PHY bindings: Phandle for the Generic PHY for PCIe.
+- phy-names:  MUST be "pcie-phy".
+- interrupts: Three interrupt entries must be specified.
+- interrupt-names: Must include the following names
+	- "sys"
+	- "legacy"
+	- "client"
+- resets: Must contain five entries for each entry in reset-names.
+	   See ../reset/reset.txt for details.
+- reset-names: Must include the following names
+	- "core"
+	- "mgmt"
+	- "mgmt-sticky"
+	- "pipe"
+- pinctrl-names : The pin control state names
+- pinctrl-0: The "default" pinctrl state
+- #interrupt-cells: specifies the number of cells needed to encode an
+	interrupt source. The value must be 1.
+- interrupt-map-mask and interrupt-map: standard PCI properties
+
+*Interrupt controller child node*
+The core controller provides a single interrupt for legacy INTx. So,
+pcie node should create a interrupt controller node to support 'interrupt-map'
+DT functionality. The driver will create an IRQ domain for this map, decode
+the four INTx interrupts in ISR and route them to this domain.
+
+Required properties for Interrupt controller child node:
+- interrupt-controller: identifies the node as an interrupt controller
+- #address-cells: specifies the number of cells needed to encode an
+	address. The value must be 0.
+- #interrupt-cells: specifies the number of cells needed to encode an
+	interrupt source. The value must be 1.
+
+Optional Property:
+- ep-gpios: contain the entry for pre-reset gpio
+- num-lanes: number of lanes to use
+- vpcie3v3-supply: The phandle to the 3.3v regulator to use for pcie.
+- vpcie1v8-supply: The phandle to the 1.8v regulator to use for pcie.
+- vpcie0v9-supply: The phandle to the 0.9v regulator to use for pcie.
+
+Example:
+
+pcie0: pcie at f8000000 {
+	compatible = "rockchip,rk3399-pcie";
+	#address-cells = <3>;
+	#size-cells = <2>;
+	clocks = <&cru ACLK_PCIE>, <&cru ACLK_PERF_PCIE>,
+		 <&cru PCLK_PCIE>, <&cru SCLK_PCIE_PM>;
+	clock-names = "aclk", "aclk-perf",
+		      "hclk", "pm";
+	bus-range = <0x0 0x1>;
+	interrupts = <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 50 IRQ_TYPE_LEVEL_HIGH>,
+		     <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>;
+	interrupt-names = "sys", "legacy", "client";
+	assigned-clocks = <&cru SCLK_PCIEPHY_REF>;
+	assigned-clock-parents = <&cru SCLK_PCIEPHY_REF100M>;
+	assigned-clock-rates = <100000000>;
+	ep-gpios = <&gpio3 13 GPIO_ACTIVE_HIGH>;
+	ranges = <0x83000000 0x0 0xfa000000 0x0 0xfa000000 0x0 0x600000
+		  0x81000000 0x0 0xfa600000 0x0 0xfa600000 0x0 0x100000>;
+	num-lanes = <4>;
+	msi-map = <0x0 &its 0x0 0x1000>;
+	reg = < 0x0 0xf8000000 0x0 0x2000000 >, < 0x0 0xfd000000 0x0 0x1000000 >;
+	reg-names = "axi-base", "apb-base";
+	resets = <&cru SRST_PCIE_CORE>, <&cru SRST_PCIE_MGMT>,
+		 <&cru SRST_PCIE_MGMT_STICKY>, <&cru SRST_PCIE_PIPE>;
+	reset-names = "core", "mgmt", "mgmt-sticky", "pipe";
+	phys = <&pcie_phy>;
+	phy-names = "pcie-phy";
+	pinctrl-names = "default";
+	pinctrl-0 = <&pcie_clkreq>;
+	#interrupt-cells = <1>;
+	interrupt-map-mask = <0 0 0 7>;
+	interrupt-map = <0 0 0 1 &pcie0_intc 1>,
+			<0 0 0 2 &pcie0_intc 2>,
+			<0 0 0 3 &pcie0_intc 3>,
+			<0 0 0 4 &pcie0_intc 4>;
+	pcie0_intc: interrupt-controller {
+		interrupt-controller;
+		#address-cells = <0>;
+		#interrupt-cells = <1>;
+	};
+};
-- 
2.3.7




From shawn.lin at rock-chips.com  Wed Jul  6 00:16:38 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Wed,  6 Jul 2016 15:16:38 +0800
Subject: [PATCH v6 2/2] PCI: Rockchip: Add Rockchip PCIe controller support
In-Reply-To: <1467789398-13501-1-git-send-email-shawn.lin@rock-chips.com>
References: <1467789398-13501-1-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <1467789398-13501-2-git-send-email-shawn.lin@rock-chips.com>

This patch adds Rockchip PCIe controller support found
on RK3399 Soc platform.

Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>

---

Changes in v6:
- use "depends on PCI_MSI_IRQ_DOMAIN" suggested by Arnd

Changes in v5:
- handle multiple pending INTx at the same time
  suggested by Marc

Changes in v4:
- address the comments from Brain

Changes in v3:
- remove header file
- remove struct msi_controller and move most of variables
  of rockchip_pcie_port to become the local ones.
- allow legacy int even if enabling MSI
- drop regulator set voltage operation suggested by Doug

Changes in v2:
- remove phy related stuff and call phy API
- add new head file and define lots of macro to make
  the code more readable
- remove lots msi related code suggested by Marc
- add IO window address translation
- init_port and parse_dt reconstruction suggested by Bharat
- improve wr_own_conf suggested by Arnd
- make pcie as an interrupt controller and fix wrong int handler
  suggested by Marc
- remove PCI_PROBE_ONLY suggested by Lorenzo

 drivers/pci/host/Kconfig         |   11 +
 drivers/pci/host/Makefile        |    1 +
 drivers/pci/host/pcie-rockchip.c | 1213 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 1225 insertions(+)
 create mode 100644 drivers/pci/host/pcie-rockchip.c

diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index c917e2f..4bf6db7 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -264,4 +264,15 @@ config PCIE_ARTPEC6
 	  Say Y here to enable PCIe controller support on Axis ARTPEC-6
 	  SoCs.  This PCIe controller uses the DesignWare core.
 
+config PCIE_ROCKCHIP
+	tristate "Rockchip PCIe controller"
+	depends on ARM64 && ARCH_ROCKCHIP
+	depends on OF
+	depends on PCI_MSI_IRQ_DOMAIN
+	select MFD_SYSCON
+	help
+	  Say Y here if you want internal PCI support on Rockchip SoC.
+	  There is 1 internal PCIe port available to support GEN2 with
+	  4 slots.
+
 endmenu
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index 5bc0af2..695f716 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -30,3 +30,4 @@ obj-$(CONFIG_PCI_HOST_THUNDER_ECAM) += pci-thunder-ecam.o
 obj-$(CONFIG_PCI_HOST_THUNDER_PEM) += pci-thunder-pem.o
 obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o
 obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o
+obj-$(CONFIG_PCIE_ROCKCHIP) += pcie-rockchip.o
diff --git a/drivers/pci/host/pcie-rockchip.c b/drivers/pci/host/pcie-rockchip.c
new file mode 100644
index 0000000..7996556
--- /dev/null
+++ b/drivers/pci/host/pcie-rockchip.c
@@ -0,0 +1,1213 @@
+/*
+ * Rockchip AXI PCIe host controller driver
+ *
+ * Copyright (c) 2016 Rockchip, Inc.
+ *
+ * Author: Shawn Lin <shawn.lin at rock-chips.com>
+ *         Wenrui Li <wenrui.li at rock-chips.com>
+ *
+ * Bits taken from Synopsys Designware Host controller driver and
+ * ARM PCI Host generic driver.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_pci.h>
+#include <linux/of_platform.h>
+#include <linux/of_irq.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/regmap.h>
+
+#define PCIE_CLIENT_BASE                0x0
+#define PCIE_RC_CONFIG_NORMAL_BASE      0x800000
+#define PCIE_RC_CONFIG_BASE             0xa00000
+#define PCIE_CORE_LINK_CTRL_STATUS      0x8000d0
+#define PCIE_CORE_CTRL_MGMT_BASE        0x900000
+#define PCIE_CORE_AXI_CONF_BASE         0xc00000
+#define PCIE_CORE_AXI_INBOUND_BASE      0xc00800
+#define PCIE_CLIENT_BASIC_STATUS0       0x44
+#define PCIE_CLIENT_BASIC_STATUS1       0x48
+#define PCIE_CLIENT_INT_MASK            0x4c
+#define PCIE_CLIENT_INT_STATUS          0x50
+#define PCIE_RC_CONFIG_RID_CCR          0x8
+#define PCIE_RC_CONFIG_LCS              0xd0
+#define PCIE_RC_BAR_CONF                0x300
+#define PCIE_CORE_OB_REGION_ADDR1       0x4
+#define PCIE_CORE_OB_REGION_DESC0       0x8
+#define PCIE_CORE_OB_REGION_DESC1       0xc
+#define PCIE_RP_IB_ADDR_TRANS           0x4
+#define PCIE_CORE_INT_MASK              0x900210
+#define PCIE_CORE_INT_STATUS            0x90020c
+
+/* Size of one AXI Region (not Region 0) */
+#define AXI_REGION_SIZE                 BIT(20)
+/* Size of Region 0, equal to sum of sizes of other regions */
+#define AXI_REGION_0_SIZE              (32 * (0x1 << 20))
+#define OB_REG_SIZE_SHIFT               5
+#define IB_ROOT_PORT_REG_SIZE_SHIFT     3
+#define AXI_WRAPPER_IO_WRITE            0x6
+#define AXI_WRAPPER_MEM_WRITE           0x2
+#define MAX_AXI_IB_ROOTPORT_REGION_NUM  3
+#define MIN_AXI_ADDR_BITS_PASSED        8
+#define ROCKCHIP_VENDOR_ID              0x1d87
+#define PCIE_ECAM_BUS(x)		(((x) & 0xFF) << 20)
+#define PCIE_ECAM_DEV(x)		(((x) & 0x1F) << 15)
+#define PCIE_ECAM_FUNC(x)		(((x) & 0x7) << 12)
+#define PCIE_ECAM_REG(x)		(((x) & 0xFFF) << 0)
+#define PCIE_ECAM_ADDR(bus, dev, func, reg) \
+	  (PCIE_ECAM_BUS(bus) | PCIE_ECAM_DEV(dev) | \
+	   PCIE_ECAM_FUNC(func) | PCIE_ECAM_REG(reg))
+
+/*
+  * The higher 16-bit of this register is used for write protection
+  * only if BIT(x + 16) set to 1 the BIT(x) can be written.
+  */
+#define HIWORD_UPDATE(val, mask, shift) \
+	((val) << (shift) | (mask) << ((shift) + 16))
+
+#define RC_REGION_0_ADDR_TRANS_H        0x00000000
+#define RC_REGION_0_ADDR_TRANS_L        0x00000000
+#define RC_REGION_0_PASS_BITS           (25 - 1)
+#define RC_REGION_1_ADDR_TRANS_H        0x00000000
+#define RC_REGION_1_ADDR_TRANS_L        0x00400000
+#define RC_REGION_1_PASS_BITS           (20 - 1)
+#define MAX_AXI_WRAPPER_REGION_NUM      33
+#define PCIE_CORE_LCSR_RETRAIN_LINK     BIT(5)
+#define PCIE_CLIENT_CONF_ENABLE         1
+#define PCIE_CLIENT_CONF_ENABLE_SHIFT   0
+#define PCIE_CLIENT_CONF_ENABLE_MASK    0x1
+#define PCIE_CLIENT_LINK_TRAIN_ENABLE   1
+#define PCIE_CLIENT_LINK_TRAIN_SHIFT    1
+#define PCIE_CLIENT_LINK_TRAIN_MASK     0x1
+#define PCIE_CLIENT_ARI_ENABLE          1
+#define PCIE_CLIENT_ARI_ENABLE_SHIFT    3
+#define PCIE_CLIENT_ARI_ENABLE_MASK     0x1
+#define PCIE_CLIENT_CONF_LANE_NUM(x)    (x / 2)
+#define PCIE_CLIENT_CONF_LANE_NUM_SHIFT 4
+#define PCIE_CLIENT_CONF_LANE_NUM_MASK  0x3
+#define PCIE_CLIENT_MODE_RC             1
+#define PCIE_CLIENT_MODE_SHIFT          6
+#define PCIE_CLIENT_MODE_MASK           0x1
+#define PCIE_CLIENT_GEN_SEL_2           1
+#define PCIE_CLIENT_GEN_SEL_1           0
+#define PCIE_CLIENT_GEN_SEL_SHIFT       7
+#define PCIE_CLIENT_GEN_SEL_MASK        0x1
+#define PCIE_CLIENT_LINK_STATUS_UP      0x3
+#define PCIE_CLIENT_LINK_STATUS_SHIFT   20
+#define PCIE_CLIENT_LINK_STATUS_MASK    0x3
+#define PCIE_CORE_PL_CONF_SPEED_2_5G     0x0
+#define PCIE_CORE_PL_CONF_SPEED_5G     0x1
+#define PCIE_CORE_PL_CONF_SPEED_8G     0x2
+#define PCIE_CORE_PL_CONF_SPEED_SHIFT   3
+#define PCIE_CORE_PL_CONF_SPEED_MASK    0x3
+#define PCIE_CORE_PL_CONF_LANE_SHIFT    1
+#define PCIE_CORE_PL_CONF_LANE_MASK     0x3
+#define PCIE_CORE_RC_CONF_SCC_SHIFT     16
+
+/* PCIE_CLIENT_INT_STATUS */
+#define PCIE_CLIENT_INT_LEGACY_DONE     BIT(15)
+#define PCIE_CLIENT_INT_MSG             BIT(14)
+#define PCIE_CLIENT_INT_HOT_RST         BIT(13)
+#define PCIE_CLIENT_INT_DPA             BIT(12)
+#define PCIE_CLIENT_INT_FATAL_ERR       BIT(11)
+#define PCIE_CLIENT_INT_NFATAL_ERR      BIT(10)
+#define PCIE_CLIENT_INT_CORR_ERR        BIT(9)
+#define PCIE_CLIENT_INT_INTD            BIT(8)
+#define PCIE_CLIENT_INT_INTC            BIT(7)
+#define PCIE_CLIENT_INT_INTB            BIT(6)
+#define PCIE_CLIENT_INT_INTA            BIT(5)
+#define PCIE_CLIENT_INT_LOCAL           BIT(4)
+#define PCIE_CLIENT_INT_UDMA            BIT(3)
+#define PCIE_CLIENT_INT_PHY             BIT(2)
+#define PCIE_CLIENT_INT_HOT_PLUG        BIT(1)
+#define PCIE_CLIENT_INT_PWR_STCG        BIT(0)
+#define PCIE_CORE_INT_PRFPE             BIT(0)
+#define PCIE_CORE_INT_CRFPE             BIT(1)
+#define PCIE_CORE_INT_RRPE              BIT(2)
+#define PCIE_CORE_INT_PRFO              BIT(3)
+#define PCIE_CORE_INT_CRFO              BIT(4)
+#define PCIE_CORE_INT_RT                BIT(5)
+#define PCIE_CORE_INT_RTR               BIT(6)
+#define PCIE_CORE_INT_PE                BIT(7)
+#define PCIE_CORE_INT_MTR               BIT(8)
+#define PCIE_CORE_INT_UCR               BIT(9)
+#define PCIE_CORE_INT_FCE               BIT(10)
+#define PCIE_CORE_INT_CT                BIT(11)
+#define PCIE_CORE_INT_UTC               BIT(18)
+#define PCIE_CORE_INT_MMVC              BIT(19)
+
+#define ROCKCHIP_PCIE_RPIFR1_INTR_MASK  GENMASK(8, 5)
+#define ROCKCHIP_PCIE_RPIFR1_INTR_SHIFT 5
+
+#define PCIE_CORE_INT \
+		(PCIE_CORE_INT_PRFPE | PCIE_CORE_INT_CRFPE | \
+		 PCIE_CORE_INT_RRPE | PCIE_CORE_INT_CRFO | \
+		 PCIE_CORE_INT_RT | PCIE_CORE_INT_RTR | \
+		 PCIE_CORE_INT_PE | PCIE_CORE_INT_MTR | \
+		 PCIE_CORE_INT_UCR | PCIE_CORE_INT_FCE | \
+		 PCIE_CORE_INT_CT | PCIE_CORE_INT_UTC | \
+		 PCIE_CORE_INT_MMVC)
+
+#define PCIE_CLIENT_INT_SUBSYSTEM \
+	(PCIE_CLIENT_INT_PWR_STCG | PCIE_CLIENT_INT_HOT_PLUG | \
+	PCIE_CLIENT_INT_PHY | PCIE_CLIENT_INT_UDMA | \
+	PCIE_CLIENT_INT_LOCAL)
+
+#define PCIE_CLIENT_INT_LEGACY \
+	(PCIE_CLIENT_INT_INTA | PCIE_CLIENT_INT_INTB | \
+	PCIE_CLIENT_INT_INTC | PCIE_CLIENT_INT_INTD)
+
+#define PCIE_CLIENT_INT_CLI \
+	(PCIE_CLIENT_INT_CORR_ERR | PCIE_CLIENT_INT_NFATAL_ERR | \
+	PCIE_CLIENT_INT_FATAL_ERR | PCIE_CLIENT_INT_DPA | \
+	PCIE_CLIENT_INT_HOT_RST | PCIE_CLIENT_INT_MSG | \
+	PCIE_CLIENT_INT_LEGACY_DONE | PCIE_CLIENT_INT_LEGACY)
+
+struct rockchip_pcie_port {
+	void	__iomem *reg_base;
+	void	__iomem *apb_base;
+	struct	phy *phy;
+	struct	reset_control *core_rst;
+	struct	reset_control *mgmt_rst;
+	struct	reset_control *mgmt_sticky_rst;
+	struct	reset_control *pipe_rst;
+	struct	clk *aclk_pcie;
+	struct	clk *aclk_perf_pcie;
+	struct	clk *hclk_pcie;
+	struct	clk *clk_pcie_pm;
+	struct	regulator *vpcie3v3; /* 3.3V power supply */
+	struct	regulator *vpcie1v8; /* 1.8V power supply */
+	struct	regulator *vpcie0v9; /* 0.9V power supply */
+	struct	gpio_desc *ep_gpio;
+	u32	lanes;
+	u8	root_bus_nr;
+	struct	device *dev;
+	struct	irq_domain *irq_domain;
+};
+
+static irqreturn_t rockchip_pcie_subsys_irq_handler(int irq, void *arg);
+static irqreturn_t rockchip_pcie_client_irq_handler(int irq, void *arg);
+static void rockchip_pcie_legacy_int_handler(struct irq_desc *desc);
+
+static inline u32 pcie_read(struct rockchip_pcie_port *port, u32 reg)
+{
+	return readl(port->apb_base + reg);
+}
+
+static inline void pcie_write(struct rockchip_pcie_port *port,
+			      u32 val, u32 reg)
+{
+	writel(val, port->apb_base + reg);
+}
+
+static int rockchip_pcie_valid_config(struct rockchip_pcie_port *pp,
+				      struct pci_bus *bus, int dev)
+{
+	/* access only one slot on each root port */
+	if (bus->number == pp->root_bus_nr && dev > 0)
+		return 0;
+
+	/*
+	 * do not read more than one device on the bus directly attached
+	 * to RC's (Virtual Bridge's) DS side.
+	 */
+	if (bus->primary == pp->root_bus_nr && dev > 0)
+		return 0;
+
+	return 1;
+}
+
+static int rockchip_pcie_rd_own_conf(struct rockchip_pcie_port *pp,
+				     int where, int size,
+				     u32 *val)
+{
+	void __iomem *addr = pp->apb_base + PCIE_RC_CONFIG_BASE + where;
+
+	if (!IS_ALIGNED((uintptr_t)addr, size)) {
+		*val = 0;
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	}
+
+	if (size == 4) {
+		*val = readl(addr);
+	} else if (size == 2) {
+		*val = readw(addr);
+	} else if (size == 1) {
+		*val = readb(addr);
+	} else {
+		*val = 0;
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	}
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int rockchip_pcie_wr_own_conf(struct rockchip_pcie_port *pp,
+				     int where, int size, u32 val)
+{
+	u32 mask, tmp, offset;
+
+	offset = (where & (~0x3));
+
+	if (size == 4) {
+		writel(val, pp->apb_base + PCIE_RC_CONFIG_BASE + offset);
+		return PCIBIOS_SUCCESSFUL;
+	}
+
+	mask = ~(((1 << (size * 8)) - 1) << ((where & 0x3) * 8));
+
+	tmp = readl(pp->apb_base + PCIE_RC_CONFIG_BASE + offset) & mask;
+	tmp |= val << ((where & 0x3) * 8);
+	writel(tmp, pp->apb_base + PCIE_RC_CONFIG_BASE + offset);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int rockchip_pcie_rd_other_conf(struct rockchip_pcie_port *pp,
+				       struct pci_bus *bus, u32 devfn,
+				       int where, int size, u32 *val)
+{
+	u32 busdev;
+
+	busdev = PCIE_ECAM_ADDR(bus->number, PCI_SLOT(devfn),
+				PCI_FUNC(devfn), where);
+
+	if (!IS_ALIGNED(busdev, size)) {
+		*val = 0;
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	}
+
+	if (size == 4) {
+		*val = readl(pp->reg_base + busdev);
+	} else if (size == 2) {
+		*val = readw(pp->reg_base + busdev);
+	} else if (size == 1) {
+		*val = readb(pp->reg_base + busdev);
+	} else {
+		*val = 0;
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	}
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int rockchip_pcie_wr_other_conf(struct rockchip_pcie_port *pp,
+				       struct pci_bus *bus, u32 devfn,
+				       int where, int size, u32 val)
+{
+	u32 busdev;
+
+	busdev = PCIE_ECAM_ADDR(bus->number, PCI_SLOT(devfn),
+				PCI_FUNC(devfn), where);
+	if (!IS_ALIGNED(busdev, size))
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+
+	if (size == 4)
+		writel(val, pp->reg_base + busdev);
+	else if (size == 2)
+		writew(val, pp->reg_base + busdev);
+	else if (size == 1)
+		writeb(val, pp->reg_base + busdev);
+	else
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int rockchip_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
+				 int size, u32 *val)
+{
+	struct rockchip_pcie_port *pp = bus->sysdata;
+	int ret;
+
+	if (rockchip_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) {
+		*val = 0xffffffff;
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	}
+
+	if (bus->number != pp->root_bus_nr)
+		ret = rockchip_pcie_rd_other_conf(pp, bus, devfn,
+						  where, size, val);
+	else
+		ret = rockchip_pcie_rd_own_conf(pp, where, size, val);
+
+	return ret;
+}
+
+static int rockchip_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
+				 int where, int size, u32 val)
+{
+	struct rockchip_pcie_port *pp = bus->sysdata;
+	int ret;
+
+	if (rockchip_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	if (bus->number != pp->root_bus_nr)
+		ret = rockchip_pcie_wr_other_conf(pp, bus, devfn,
+						  where, size, val);
+	else
+		ret = rockchip_pcie_wr_own_conf(pp, where, size, val);
+
+	return ret;
+}
+
+static struct pci_ops rockchip_pcie_ops = {
+	.read = rockchip_pcie_rd_conf,
+	.write = rockchip_pcie_wr_conf,
+};
+
+/**
+ * rockchip_pcie_init_port - Initialize hardware
+ * @port: PCIe port information
+ */
+static int rockchip_pcie_init_port(struct rockchip_pcie_port *port)
+{
+	int err;
+	u32 status;
+	unsigned long timeout;
+
+	gpiod_set_value(port->ep_gpio, 0);
+
+	err = phy_init(port->phy);
+	if (err < 0) {
+		dev_err(port->dev, "fail to init phy, err %d\n", err);
+		return err;
+	}
+
+	err = reset_control_assert(port->core_rst);
+	if (err) {
+		dev_err(port->dev, "assert core_rst err %d\n", err);
+		return err;
+	}
+
+	err = reset_control_assert(port->mgmt_rst);
+	if (err) {
+		dev_err(port->dev, "assert mgmt_rst err %d\n", err);
+		return err;
+	}
+
+	err = reset_control_assert(port->mgmt_sticky_rst);
+	if (err) {
+		dev_err(port->dev, "assert mgmt_sticky_rst err %d\n", err);
+		return err;
+	}
+
+	err = reset_control_assert(port->pipe_rst);
+	if (err) {
+		dev_err(port->dev, "assert pipe_rst err %d\n", err);
+		return err;
+	}
+
+	pcie_write(port,
+		   HIWORD_UPDATE(PCIE_CLIENT_CONF_ENABLE,
+				 PCIE_CLIENT_CONF_ENABLE_MASK,
+				 PCIE_CLIENT_CONF_ENABLE_SHIFT) |
+		   HIWORD_UPDATE(PCIE_CLIENT_CONF_LANE_NUM(port->lanes),
+				 PCIE_CLIENT_CONF_LANE_NUM_MASK,
+				 PCIE_CLIENT_CONF_LANE_NUM_SHIFT) |
+		   HIWORD_UPDATE(PCIE_CLIENT_MODE_RC,
+				 PCIE_CLIENT_MODE_MASK,
+				 PCIE_CLIENT_MODE_SHIFT) |
+		   HIWORD_UPDATE(PCIE_CLIENT_ARI_ENABLE,
+				 PCIE_CLIENT_ARI_ENABLE_MASK,
+				 PCIE_CLIENT_ARI_ENABLE_SHIFT) |
+		   HIWORD_UPDATE(PCIE_CLIENT_GEN_SEL_2,
+				 PCIE_CLIENT_GEN_SEL_MASK,
+				 PCIE_CLIENT_GEN_SEL_SHIFT),
+		   PCIE_CLIENT_BASE);
+
+	err = phy_power_on(port->phy);
+	if (err) {
+		dev_err(port->dev, "fail to power on phy, err %d\n", err);
+		return err;
+	}
+
+	err = reset_control_deassert(port->core_rst);
+	if (err) {
+		dev_err(port->dev, "deassert core_rst err %d\n", err);
+		return err;
+	}
+
+	err = reset_control_deassert(port->mgmt_rst);
+	if (err) {
+		dev_err(port->dev, "deassert mgmt_rst err %d\n", err);
+		return err;
+	}
+
+	err = reset_control_deassert(port->mgmt_sticky_rst);
+	if (err) {
+		dev_err(port->dev, "deassert mgmt_sticky_rst err %d\n", err);
+		return err;
+	}
+
+	err = reset_control_deassert(port->pipe_rst);
+	if (err) {
+		dev_err(port->dev, "deassert pipe_rst err %d\n", err);
+		return err;
+	}
+
+	/* Enable Gen1 training */
+	pcie_write(port,
+		   HIWORD_UPDATE(PCIE_CLIENT_LINK_TRAIN_ENABLE,
+				 PCIE_CLIENT_LINK_TRAIN_MASK,
+				 PCIE_CLIENT_LINK_TRAIN_SHIFT),
+		   PCIE_CLIENT_BASE);
+
+	gpiod_set_value(port->ep_gpio, 1);
+
+	/* 500ms timeout value should be enough for gen1/2 taining */
+	timeout = jiffies + msecs_to_jiffies(500);
+
+	for (;;) {
+		status = pcie_read(port, PCIE_CLIENT_BASIC_STATUS1);
+		if (((status >> PCIE_CLIENT_LINK_STATUS_SHIFT) &
+		      PCIE_CLIENT_LINK_STATUS_MASK) ==
+		      PCIE_CLIENT_LINK_STATUS_UP) {
+			dev_dbg(port->dev, "pcie link training gen1 pass!\n");
+			break;
+		}
+
+		msleep(20);
+
+		if (!time_before(jiffies, timeout))
+			break;
+
+	}
+	status = pcie_read(port, PCIE_CLIENT_BASIC_STATUS1);
+	err = (((status >> PCIE_CLIENT_LINK_STATUS_SHIFT) &
+		 PCIE_CLIENT_LINK_STATUS_MASK) ==
+		 PCIE_CLIENT_LINK_STATUS_UP) ? 0 : -ETIMEDOUT;
+	if (err) {
+		dev_err(port->dev, "pcie link training gen1 timeout!\n");
+		return err;
+	}
+
+	/*
+	 * Enable retrain for gen2. This should be configured only after
+	 * gen1 finished.
+	 */
+	status = pcie_read(port,
+			   PCIE_RC_CONFIG_LCS + PCIE_RC_CONFIG_BASE);
+	status |= PCIE_CORE_LCSR_RETRAIN_LINK;
+	pcie_write(port, status,
+		   PCIE_RC_CONFIG_LCS + PCIE_RC_CONFIG_BASE);
+
+	timeout = jiffies + msecs_to_jiffies(500);
+	for (;;) {
+		status = pcie_read(port, PCIE_CORE_CTRL_MGMT_BASE);
+		if (((status >> PCIE_CORE_PL_CONF_SPEED_SHIFT) &
+		      PCIE_CORE_PL_CONF_SPEED_MASK) ==
+		      PCIE_CORE_PL_CONF_SPEED_5G) {
+			dev_dbg(port->dev, "pcie link training gen2 pass!\n");
+			break;
+		}
+
+		msleep(20);
+
+		if (!time_before(jiffies, timeout))
+			break;
+	}
+	status = pcie_read(port, PCIE_CORE_CTRL_MGMT_BASE);
+	err = (((status >> PCIE_CORE_PL_CONF_SPEED_SHIFT) &
+		PCIE_CORE_PL_CONF_SPEED_MASK) ==
+		PCIE_CORE_PL_CONF_SPEED_5G) ? 0 : -ETIMEDOUT;
+	if (err)
+		dev_dbg(port->dev, "pcie link training gen2 timeout, fall back to gen1!\n");
+
+	/* Check the final link with from negotiated lane counter from MGMT */
+	status = pcie_read(port, PCIE_CORE_CTRL_MGMT_BASE);
+	status =  0x1 << ((status >> PCIE_CORE_PL_CONF_LANE_SHIFT) &
+			   PCIE_CORE_PL_CONF_LANE_MASK);
+	dev_dbg(port->dev, "current link width is x%d\n", status);
+
+	pcie_write(port, ROCKCHIP_VENDOR_ID, PCIE_RC_CONFIG_BASE);
+	pcie_write(port, PCI_CLASS_BRIDGE_PCI << PCIE_CORE_RC_CONF_SCC_SHIFT,
+		   PCIE_RC_CONFIG_BASE + PCIE_RC_CONFIG_RID_CCR);
+	pcie_write(port, 0x0, PCIE_CORE_CTRL_MGMT_BASE + PCIE_RC_BAR_CONF);
+
+	pcie_write(port, (RC_REGION_0_ADDR_TRANS_L + RC_REGION_0_PASS_BITS),
+		   PCIE_CORE_AXI_CONF_BASE);
+	pcie_write(port, RC_REGION_0_ADDR_TRANS_H,
+		   PCIE_CORE_AXI_CONF_BASE + PCIE_CORE_OB_REGION_ADDR1);
+	pcie_write(port, 0x0080000a,
+		   PCIE_CORE_AXI_CONF_BASE + PCIE_CORE_OB_REGION_DESC0);
+	pcie_write(port, 0x0,
+		   PCIE_CORE_AXI_CONF_BASE + PCIE_CORE_OB_REGION_DESC1);
+
+	return 0;
+}
+
+/**
+ * rockchip_pcie_parse_dt - Parse Device tree
+ * @port: PCIe port information
+ *
+ * Return: '0' on success and error value on failure
+ */
+static int rockchip_pcie_parse_dt(struct rockchip_pcie_port *port)
+{
+	struct device *dev = port->dev;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct device_node *node = dev->of_node;
+	struct resource *regs;
+	int irq;
+	int err = -ENODEV;
+
+	regs = platform_get_resource_byname(pdev,
+					    IORESOURCE_MEM,
+					    "axi-base");
+	if (!regs) {
+		dev_err(dev, "missing axi-base property\n");
+		return err;
+	}
+
+	port->reg_base = devm_ioremap_resource(dev, regs);
+	if (IS_ERR(port->reg_base))
+		return PTR_ERR(port->reg_base);
+
+	regs = platform_get_resource_byname(pdev,
+					    IORESOURCE_MEM,
+					    "apb-base");
+	if (!regs) {
+		dev_err(dev, "missing apb-base property\n");
+		return err;
+	}
+
+	port->apb_base = devm_ioremap_resource(dev, regs);
+	if (IS_ERR(port->apb_base))
+		return PTR_ERR(port->apb_base);
+
+	port->phy = devm_phy_get(dev, "pcie-phy");
+	if (IS_ERR(port->phy)) {
+		if (PTR_ERR(port->phy) != -EPROBE_DEFER)
+			dev_err(dev, "Missing phy\n");
+		return PTR_ERR(port->phy);
+	}
+
+	port->lanes = 1;
+	err = of_property_read_u32(node, "num-lanes", &port->lanes);
+	if (!err && ((port->lanes == 0) ||
+		     (port->lanes == 3) ||
+		     (port->lanes > 4))) {
+		dev_warn(dev, "invalid num-lanes, default use one lane\n");
+		port->lanes = 1;
+	}
+
+	port->core_rst = devm_reset_control_get(dev, "core");
+	if (IS_ERR(port->core_rst)) {
+		if (PTR_ERR(port->core_rst) != -EPROBE_DEFER)
+			dev_err(dev, "missing core rst property in node %s\n",
+				node->name);
+		return PTR_ERR(port->core_rst);
+	}
+
+	port->mgmt_rst = devm_reset_control_get(dev, "mgmt");
+	if (IS_ERR(port->mgmt_rst)) {
+		if (PTR_ERR(port->mgmt_rst) != -EPROBE_DEFER)
+			dev_err(dev, "missing mgmt rst property in node %s\n",
+				node->name);
+		return PTR_ERR(port->mgmt_rst);
+	}
+
+	port->mgmt_sticky_rst = devm_reset_control_get(dev, "mgmt-sticky");
+	if (IS_ERR(port->mgmt_sticky_rst)) {
+		if (PTR_ERR(port->mgmt_sticky_rst) != -EPROBE_DEFER)
+			dev_err(dev, "missing mgmt-sticky rst property in node %s\n",
+				node->name);
+		return PTR_ERR(port->mgmt_sticky_rst);
+	}
+
+	port->pipe_rst = devm_reset_control_get(dev, "pipe");
+	if (IS_ERR(port->pipe_rst)) {
+		if (PTR_ERR(port->pipe_rst) != -EPROBE_DEFER)
+			dev_err(dev, "missing pipe rst property in node %s\n",
+				node->name);
+		return PTR_ERR(port->pipe_rst);
+	}
+
+	port->ep_gpio = devm_gpiod_get(dev, "ep", GPIOD_OUT_HIGH);
+	if (IS_ERR(port->ep_gpio)) {
+		dev_err(dev, "missing ep-gpios property in node %s\n",
+			node->name);
+		return PTR_ERR(port->ep_gpio);
+	}
+
+	port->aclk_pcie = devm_clk_get(dev, "aclk");
+	if (IS_ERR(port->aclk_pcie)) {
+		dev_err(dev, "aclk clock not found.\n");
+		return PTR_ERR(port->aclk_pcie);
+	}
+
+	port->aclk_perf_pcie = devm_clk_get(dev, "aclk-perf");
+	if (IS_ERR(port->aclk_perf_pcie)) {
+		dev_err(dev, "aclk_perf clock not found.\n");
+		return PTR_ERR(port->aclk_perf_pcie);
+	}
+
+	port->hclk_pcie = devm_clk_get(dev, "hclk");
+	if (IS_ERR(port->hclk_pcie)) {
+		dev_err(dev, "hclk clock not found.\n");
+		return PTR_ERR(port->hclk_pcie);
+	}
+
+	port->clk_pcie_pm = devm_clk_get(dev, "pm");
+	if (IS_ERR(port->clk_pcie_pm)) {
+		dev_err(dev, "pm clock not found.\n");
+		return PTR_ERR(port->clk_pcie_pm);
+	}
+
+	irq = platform_get_irq_byname(pdev, "sys");
+	if (irq < 0) {
+		dev_err(dev, "missing pcie_sys IRQ resource\n");
+		return -EINVAL;
+	}
+
+	err = devm_request_irq(dev, irq, rockchip_pcie_subsys_irq_handler,
+			       IRQF_SHARED, "pcie-sys", port);
+	if (err) {
+		dev_err(dev, "failed to request pcie subsystem irq\n");
+		return err;
+	}
+
+	irq = platform_get_irq_byname(pdev, "legacy");
+	if (irq < 0) {
+		dev_err(dev, "missing pcie_legacy IRQ resource\n");
+		return -EINVAL;
+	}
+
+	irq_set_chained_handler_and_data(irq,
+					 rockchip_pcie_legacy_int_handler,
+					 port);
+
+	irq = platform_get_irq_byname(pdev, "client");
+	if (irq < 0) {
+		dev_err(dev, "missing pcie-client IRQ resource\n");
+		return -EINVAL;
+	}
+
+	err = devm_request_irq(dev, irq, rockchip_pcie_client_irq_handler,
+			       IRQF_SHARED, "pcie-client", port);
+	if (err) {
+		dev_err(dev, "failed to request pcie client irq\n");
+		return err;
+	}
+
+	port->vpcie3v3 = devm_regulator_get_optional(dev, "vpcie3v3");
+	if (IS_ERR(port->vpcie3v3)) {
+		if (PTR_ERR(port->vpcie3v3) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+		dev_info(dev, "No vpcie3v3 regulator found.\n");
+	}
+
+	port->vpcie1v8 = devm_regulator_get_optional(dev, "vpcie1v8");
+	if (IS_ERR(port->vpcie1v8)) {
+		if (PTR_ERR(port->vpcie1v8) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+		dev_info(dev, "No vpcie1v8 regulator found.\n");
+	}
+
+	port->vpcie0v9 = devm_regulator_get_optional(dev, "vpcie0v9");
+	if (IS_ERR(port->vpcie0v9)) {
+		if (PTR_ERR(port->vpcie0v9) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+		dev_info(dev, "No vpcie0v9 regulator found.\n");
+	}
+
+	return 0;
+}
+
+static int rockchip_pcie_set_vpcie(struct rockchip_pcie_port *port)
+{
+	int err;
+
+	if (!IS_ERR(port->vpcie3v3)) {
+		err = regulator_enable(port->vpcie3v3);
+		if (err) {
+			dev_err(port->dev, "Fail to enable vpcie3v3 regulator.\n");
+			goto err_out;
+		}
+	}
+
+	if (!IS_ERR(port->vpcie1v8)) {
+		err = regulator_enable(port->vpcie1v8);
+		if (err) {
+			dev_err(port->dev, "Fail to enable vpcie1v8 regulator.\n");
+			goto err_disable_3v3;
+		}
+	}
+
+	if (!IS_ERR(port->vpcie0v9)) {
+		err = regulator_enable(port->vpcie0v9);
+		if (err) {
+			dev_err(port->dev, "Fail to enable vpcie0v9 regulator.\n");
+			goto err_disable_1v8;
+		}
+	}
+
+	return 0;
+
+err_disable_1v8:
+	if (!IS_ERR(port->vpcie1v8))
+		regulator_disable(port->vpcie1v8);
+err_disable_3v3:
+	if (!IS_ERR(port->vpcie3v3))
+		regulator_disable(port->vpcie3v3);
+err_out:
+	return err;
+}
+
+static void rockchip_pcie_enable_interrupts(struct rockchip_pcie_port *port)
+{
+	pcie_write(port, (PCIE_CLIENT_INT_CLI << 16) &
+		   (~PCIE_CLIENT_INT_CLI), PCIE_CLIENT_INT_MASK);
+	pcie_write(port, PCIE_CORE_INT, PCIE_CORE_INT_MASK);
+}
+
+static int rockchip_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
+				  irq_hw_number_t hwirq)
+{
+	irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq);
+	irq_set_chip_data(irq, domain->host_data);
+
+	return 0;
+}
+
+static const struct irq_domain_ops intx_domain_ops = {
+	.map = rockchip_pcie_intx_map,
+};
+
+static int rockchip_pcie_init_irq_domain(struct rockchip_pcie_port *pp)
+{
+	struct device *dev = pp->dev;
+
+	pp->irq_domain = irq_domain_add_linear(dev->of_node, 4,
+					       &intx_domain_ops, pp);
+	if (!pp->irq_domain) {
+		dev_err(dev, "Failed to get a INTx IRQ domain\n");
+		return PTR_ERR(pp->irq_domain);
+	}
+
+	return 0;
+}
+
+static irqreturn_t rockchip_pcie_subsys_irq_handler(int irq, void *arg)
+{
+	struct rockchip_pcie_port *pp = arg;
+	u32 reg;
+	u32 sub_reg;
+
+	reg = pcie_read(pp, PCIE_CLIENT_INT_STATUS);
+	if (reg & PCIE_CLIENT_INT_LOCAL) {
+		dev_dbg(pp->dev, "local interrupt recived\n");
+		sub_reg = pcie_read(pp, PCIE_CORE_INT_STATUS);
+		if (sub_reg & PCIE_CORE_INT_PRFPE)
+			dev_dbg(pp->dev, "Parity error detected while reading from the PNP Receive FIFO RAM\n");
+
+		if (sub_reg & PCIE_CORE_INT_CRFPE)
+			dev_dbg(pp->dev, "Parity error detected while reading from the Completion Receive FIFO RAM\n");
+
+		if (sub_reg & PCIE_CORE_INT_RRPE)
+			dev_dbg(pp->dev, "Parity error detected while reading from Replay Buffer RAM\n");
+
+		if (sub_reg & PCIE_CORE_INT_PRFO)
+			dev_dbg(pp->dev, "Overflow occurred in the PNP Receive FIFO\n");
+
+		if (sub_reg & PCIE_CORE_INT_CRFO)
+			dev_dbg(pp->dev, "Overflow occurred in the Completion Receive FIFO\n");
+
+		if (sub_reg & PCIE_CORE_INT_RT)
+			dev_dbg(pp->dev, "Replay timer timed out\n");
+
+		if (sub_reg & PCIE_CORE_INT_RTR)
+			dev_dbg(pp->dev, "Replay timer rolled over after 4 transmissions of the same TLP\n");
+
+		if (sub_reg & PCIE_CORE_INT_PE)
+			dev_dbg(pp->dev, "Phy error detected on receive side\n");
+
+		if (sub_reg & PCIE_CORE_INT_MTR)
+			dev_dbg(pp->dev, "Malformed TLP received from the link\n");
+
+		if (sub_reg & PCIE_CORE_INT_UCR)
+			dev_dbg(pp->dev, "Malformed TLP received from the link\n");
+
+		if (sub_reg & PCIE_CORE_INT_FCE)
+			dev_dbg(pp->dev, "An error was observed in the flow control advertisements from the other side\n");
+
+		if (sub_reg & PCIE_CORE_INT_CT)
+			dev_dbg(pp->dev, "A request timed out waiting for completion\n");
+
+		if (sub_reg & PCIE_CORE_INT_UTC)
+			dev_dbg(pp->dev, "Unmapped TC error\n");
+
+		if (sub_reg & PCIE_CORE_INT_MMVC)
+			dev_dbg(pp->dev, "MSI mask register changes\n");
+
+		pcie_write(pp, sub_reg, PCIE_CORE_INT_STATUS);
+	}
+
+	pcie_write(pp, PCIE_CLIENT_INT_SUBSYSTEM | PCIE_CLIENT_INT_LOCAL,
+		   PCIE_CLIENT_INT_STATUS);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t rockchip_pcie_client_irq_handler(int irq, void *arg)
+{
+	struct rockchip_pcie_port *pp = arg;
+	u32 reg;
+
+	reg = pcie_read(pp, PCIE_CLIENT_INT_STATUS);
+	if (reg & PCIE_CLIENT_INT_LEGACY_DONE)
+		dev_dbg(pp->dev, "legacy done interrupt recived\n");
+
+	if (reg & PCIE_CLIENT_INT_MSG)
+		dev_dbg(pp->dev, "message done interrupt recived\n");
+
+	if (reg & PCIE_CLIENT_INT_HOT_RST)
+		dev_dbg(pp->dev, "hot reset interrupt recived\n");
+
+	if (reg & PCIE_CLIENT_INT_DPA)
+		dev_dbg(pp->dev, "dpa interrupt recived\n");
+
+	if (reg & PCIE_CLIENT_INT_FATAL_ERR)
+		dev_dbg(pp->dev, "fatal error interrupt recived\n");
+
+	if (reg & PCIE_CLIENT_INT_NFATAL_ERR)
+		dev_dbg(pp->dev, "no fatal error interrupt recived\n");
+
+	if (reg & PCIE_CLIENT_INT_CORR_ERR)
+		dev_dbg(pp->dev, "correctable error interrupt recived\n");
+
+	pcie_write(pp, reg, PCIE_CLIENT_INT_STATUS);
+
+	return IRQ_HANDLED;
+}
+
+static void rockchip_pcie_legacy_int_handler(struct irq_desc *desc)
+{
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	struct rockchip_pcie_port *port;
+	u32 reg;
+	u32 hwirq;
+	u32 virq;
+
+	chained_irq_enter(chip, desc);
+	port = irq_desc_get_handler_data(desc);
+
+	reg = pcie_read(port, PCIE_CLIENT_INT_STATUS);
+	reg = (reg & ROCKCHIP_PCIE_RPIFR1_INTR_MASK) >>
+	       ROCKCHIP_PCIE_RPIFR1_INTR_SHIFT;
+
+	while (reg) {
+		hwirq = ffs(reg);
+		reg &= ~BIT(hwirq);
+
+		virq = irq_find_mapping(port->irq_domain, hwirq);
+		if (virq)
+			generic_handle_irq(virq);
+		else
+			dev_err(port->dev, "unexpected IRQ, INT%d\n", hwirq);
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static int rockchip_pcie_prog_ob_atu(struct rockchip_pcie_port *pp,
+				     int region_no,
+				     int type, u8 num_pass_bits,
+				     u32 lower_addr, u32 upper_addr)
+{
+	u32 ob_addr_0 = 0;
+	u32 ob_addr_1 = 0;
+	u32 ob_desc_0 = 0;
+	u32 ob_desc_1 = 0;
+	void __iomem *aw_base;
+
+	if (!pp)
+		return -EINVAL;
+	if (region_no >= MAX_AXI_WRAPPER_REGION_NUM)
+		return -EINVAL;
+	if ((num_pass_bits + 1) < 8)
+		return -EINVAL;
+	if (num_pass_bits > 63)
+		return -EINVAL;
+	if (region_no == 0) {
+		if (AXI_REGION_0_SIZE < (2ULL << num_pass_bits))
+		return -EINVAL;
+	}
+	if (region_no != 0) {
+		if (AXI_REGION_SIZE < (2ULL << num_pass_bits))
+		return -EINVAL;
+	}
+	aw_base = pp->apb_base + PCIE_CORE_AXI_CONF_BASE;
+	aw_base += (region_no << OB_REG_SIZE_SHIFT);
+
+	ob_addr_0 = (ob_addr_0 &
+		     ~0x0000003fU) | (num_pass_bits &
+		     0x0000003fU);
+	ob_addr_0 = (ob_addr_0 &
+		     ~0xffffff00U) | (lower_addr & 0xffffff00U);
+	ob_addr_1 = upper_addr;
+	ob_desc_0 = (1 << 23 | type);
+
+	writel(ob_addr_0, aw_base);
+	writel(ob_addr_1, aw_base + PCIE_CORE_OB_REGION_ADDR1);
+	writel(ob_desc_0, aw_base + PCIE_CORE_OB_REGION_DESC0);
+	writel(ob_desc_1, aw_base + PCIE_CORE_OB_REGION_DESC1);
+
+	return 0;
+}
+
+static int rockchip_pcie_prog_ib_atu(struct rockchip_pcie_port *pp,
+				     int region_no,
+				     u8 num_pass_bits,
+				     u32 lower_addr,
+				     u32 upper_addr)
+{
+	u32 ib_addr_0 = 0;
+	u32 ib_addr_1 = 0;
+	void __iomem *aw_base;
+
+	if (!pp)
+		return -EINVAL;
+	if (region_no > MAX_AXI_IB_ROOTPORT_REGION_NUM)
+		return -EINVAL;
+	if ((num_pass_bits + 1) < MIN_AXI_ADDR_BITS_PASSED)
+		return -EINVAL;
+	if (num_pass_bits > 63)
+		return -EINVAL;
+	aw_base = pp->apb_base + PCIE_CORE_AXI_INBOUND_BASE;
+	aw_base += (region_no << IB_ROOT_PORT_REG_SIZE_SHIFT);
+	ib_addr_0 = (ib_addr_0 &
+		     ~0x0000003fU) | (num_pass_bits &
+		     0x0000003fU);
+
+	ib_addr_0 = (ib_addr_0 & ~0xffffff00U) |
+		     ((lower_addr << 8) & 0xffffff00U);
+	ib_addr_1 = upper_addr;
+
+	writel(ib_addr_0, aw_base);
+	writel(ib_addr_1, aw_base + PCIE_RP_IB_ADDR_TRANS);
+
+	return 0;
+}
+
+static int rockchip_pcie_probe(struct platform_device *pdev)
+{
+	struct rockchip_pcie_port *port;
+	struct device *dev = &pdev->dev;
+	struct pci_bus *bus, *child;
+	struct resource_entry *win;
+	resource_size_t io_base;
+	struct resource	*busn = NULL;
+	struct resource	*mem = NULL;
+	struct resource	*io = NULL;
+	phys_addr_t io_bus_addr = 0;
+	u32 io_size = 0;
+	phys_addr_t mem_bus_addr = 0;
+	u32 mem_size = 0;
+	int reg_no = 0;
+	int err = 0;
+	int offset = 0;
+
+	LIST_HEAD(res);
+
+	if (!dev->of_node)
+		return -ENODEV;
+
+	port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	port->dev = dev;
+
+	err = rockchip_pcie_parse_dt(port);
+	if (err)
+		return err;
+
+	err = clk_prepare_enable(port->aclk_pcie);
+	if (err) {
+		dev_err(dev, "Unable to enable aclk_pcie clock.\n");
+		goto err_aclk_pcie;
+	}
+
+	err = clk_prepare_enable(port->aclk_perf_pcie);
+	if (err) {
+		dev_err(dev, "Unable to enable aclk_perf_pcie clock.\n");
+		goto err_aclk_perf_pcie;
+	}
+
+	err = clk_prepare_enable(port->hclk_pcie);
+	if (err) {
+		dev_err(dev, "Unable to enable hclk_pcie clock.\n");
+		goto err_hclk_pcie;
+	}
+
+	err = clk_prepare_enable(port->clk_pcie_pm);
+	if (err) {
+		dev_err(dev, "Unable to enable hclk_pcie clock.\n");
+		goto err_pcie_pm;
+	}
+
+	err = rockchip_pcie_set_vpcie(port);
+	if (err) {
+		dev_err(port->dev, "Fail to set vpcie regulator.\n");
+		goto err_set_vpcie;
+	}
+
+	err = rockchip_pcie_init_port(port);
+	if (err)
+		goto err_vpcie;
+
+	platform_set_drvdata(pdev, port);
+
+	rockchip_pcie_enable_interrupts(port);
+
+	err = rockchip_pcie_init_irq_domain(port);
+	if (err < 0)
+		goto err_vpcie;
+
+	err = of_pci_get_host_bridge_resources(dev->of_node, 0, 0xff,
+					       &res, &io_base);
+	if (err)
+		goto err_vpcie;
+
+	/* Get the I/O and memory ranges from DT */
+	resource_list_for_each_entry(win, &res) {
+		switch (resource_type(win->res)) {
+		case IORESOURCE_IO:
+			io = win->res;
+			io->name = "I/O";
+			io_size = resource_size(io);
+			io_bus_addr = io->start - win->offset;
+			err = pci_remap_iospace(io, io_base);
+			if (err) {
+				dev_warn(port->dev, "error %d: failed to map resource %pR\n",
+					 err, io);
+				continue;
+			}
+			break;
+		case IORESOURCE_MEM:
+			mem = win->res;
+			mem->name = "MEM";
+			mem_size = resource_size(mem);
+			mem_bus_addr = mem->start - win->offset;
+			break;
+		case IORESOURCE_BUS:
+			busn = win->res;
+			break;
+		default:
+			continue;
+		}
+	}
+
+	if (mem_size)
+		for (reg_no = 0; reg_no < (mem_size >> 20); reg_no++) {
+			err = rockchip_pcie_prog_ob_atu(port, reg_no + 1,
+							AXI_WRAPPER_MEM_WRITE,
+							20 - 1,
+							mem_bus_addr +
+							(reg_no << 20),
+							0);
+			if (err) {
+				dev_err(dev, "Program RC mem outbound atu failed\n");
+				goto err_vpcie;
+			}
+		}
+
+	err = rockchip_pcie_prog_ib_atu(port, 2, 32 - 1, 0x0, 0);
+	if (err) {
+		dev_err(dev, "Program RC mem inbound atu failed\n");
+		goto err_vpcie;
+	}
+
+	offset = mem_size >> 20;
+
+	if (io_size)
+		for (reg_no = 0; reg_no < (io_size >> 20); reg_no++) {
+			err = rockchip_pcie_prog_ob_atu(port,
+							reg_no + 1 + offset,
+							AXI_WRAPPER_IO_WRITE,
+							20 - 1,
+							io_bus_addr +
+							(reg_no << 20),
+							0);
+			if (err) {
+				dev_err(dev, "Program RC io outbound atu failed\n");
+				goto err_vpcie;
+			}
+		}
+
+	port->root_bus_nr = busn->start;
+
+	bus = pci_scan_root_bus(&pdev->dev, 0,
+				&rockchip_pcie_ops, port, &res);
+
+	if (!bus) {
+		err = -ENOMEM;
+		goto err_vpcie;
+	}
+
+	pci_bus_size_bridges(bus);
+	pci_bus_assign_resources(bus);
+	list_for_each_entry(child, &bus->children, node)
+		pcie_bus_configure_settings(child);
+
+	pci_bus_add_devices(bus);
+
+	return err;
+
+err_vpcie:
+	if (!IS_ERR(port->vpcie3v3))
+		regulator_disable(port->vpcie3v3);
+	if (!IS_ERR(port->vpcie1v8))
+		regulator_disable(port->vpcie1v8);
+	if (!IS_ERR(port->vpcie0v9))
+		regulator_disable(port->vpcie0v9);
+err_set_vpcie:
+	clk_disable_unprepare(port->clk_pcie_pm);
+err_pcie_pm:
+	clk_disable_unprepare(port->hclk_pcie);
+err_hclk_pcie:
+	clk_disable_unprepare(port->aclk_perf_pcie);
+err_aclk_perf_pcie:
+	clk_disable_unprepare(port->aclk_pcie);
+err_aclk_pcie:
+	return err;
+}
+
+static const struct of_device_id rockchip_pcie_of_match[] = {
+	{ .compatible = "rockchip,rk3399-pcie", },
+	{}
+};
+
+static struct platform_driver rockchip_pcie_driver = {
+	.driver = {
+		.name = "rockchip-pcie",
+		.of_match_table = rockchip_pcie_of_match,
+	},
+	.probe = rockchip_pcie_probe,
+
+};
+module_platform_driver(rockchip_pcie_driver);
+
+MODULE_AUTHOR("Rockchip Inc");
+MODULE_DESCRIPTION("Rockchip AXI PCIe driver");
+MODULE_LICENSE("GPL v2");
-- 
2.3.7




From w.egorov at phytec.de  Wed Jul  6 01:03:46 2016
From: w.egorov at phytec.de (Wadim Egorov)
Date: Wed, 6 Jul 2016 10:03:46 +0200
Subject: [RESEND PATCH v5 1/5] mfd: RK808: Add RK818 support
In-Reply-To: <577C77DF.9060706@rock-chips.com>
References: <1464850228-17244-1-git-send-email-w.egorov@phytec.de>
 <20160608141735.GE14888@dell> <5759277D.7090101@phytec.de>
 <577C77DF.9060706@rock-chips.com>
Message-ID: <577CBB62.8080806@phytec.de>

Hi Andy,

On 06.07.2016 05:15, Andy Yan wrote:
> Hi Wadim:
>
> On 2016?06?09? 16:23, Wadim Egorov wrote:
>> Hi,
>>
>> On 08.06.2016 16:17, Lee Jones wrote:
>>> On Thu, 02 Jun 2016, Wadim Egorov wrote:
>>>
>>>> The RK818 chip is a power management IC for multimedia and handheld
>>> "Power Management IC (PMIC)"
>>>
>>>> devices. It contains the following components:
>>>>
>>>> - Regulators
>>>> - RTC
>>>> - Clkout
>>> Clocking
>>>
>>>> - battery support
>>> Battery support
>>>
>>>> Both chips RK808 and RK818 are using a similar register map.
>>> "Both RK808 ad RK818 chips"
>>>
>>>> So we can reuse the RTC and Clkout functionality.
>>> Swap '.' for ','.
>>>
>>>> Signed-off-by: Wadim Egorov <w.egorov at phytec.de>
>>>> ---
>>>>   drivers/mfd/Kconfig       |   4 +-
>>>>   drivers/mfd/rk808.c       | 231
>>>> ++++++++++++++++++++++++++++++++++++++--------
>>>>   include/linux/mfd/rk808.h | 162 ++++++++++++++++++++++++++++++--
>>>>   3 files changed, 350 insertions(+), 47 deletions(-)
>>>>
>>>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>>>> index 1bcf601..7ba464b 100644
>>>> --- a/drivers/mfd/Kconfig
>>>> +++ b/drivers/mfd/Kconfig
>>>> @@ -839,13 +839,13 @@ config MFD_RC5T583
>>>>         different functionality of the device.
>>>>     config MFD_RK808
>>>> -    tristate "Rockchip RK808 Power Management chip"
>>>> +    tristate "Rockchip RK808/RK818 Power Management chip"
>>> "Chip"
>>>
>>>>       depends on I2C && OF
>>>>       select MFD_CORE
>>>>       select REGMAP_I2C
>>>>       select REGMAP_IRQ
>>>>       help
>>>> -      If you say yes here you get support for the RK808
>>>> +      If you say yes here you get support for the RK808 and RK818
>>>>         Power Management chips.
>>>>         This driver provides common support for accessing the device
>>>>         through I2C interface. The device supports multiple
>>>> sub-devices
>>>> diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c
>>>> index 49d7f62..3cf9724 100644
>>>> --- a/drivers/mfd/rk808.c
>>>> +++ b/drivers/mfd/rk808.c
>>>> @@ -1,11 +1,15 @@
>>>>   /*
>>>> - * MFD core driver for Rockchip RK808
>>>> + * MFD core driver for Rockchip RK808/RK818
>>>>    *
>>>>    * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
>>>>    *
>>>>    * Author: Chris Zhong <zyw at rock-chips.com>
>>>>    * Author: Zhang Qing <zhangqing at rock-chips.com>
>>>>    *
>>>> + * Copyright (C) 2016 PHYTEC Messtechnik GmbH
>>>> + *
>>>> + * Author: Wadim Egorov <w.egorov at phytec.de>
>>>> + *
>>>>    * This program is free software; you can redistribute it and/or
>>>> modify it
>>>>    * under the terms and conditions of the GNU General Public License,
>>>>    * version 2, as published by the Free Software Foundation.
>>>> @@ -22,12 +26,7 @@
>>>>   #include <linux/mfd/core.h>
>>>>   #include <linux/module.h>
>>>>   #include <linux/regmap.h>
>>>> -
>>>> -struct rk808_reg_data {
>>>> -    int addr;
>>>> -    int mask;
>>>> -    int value;
>>>> -};
>>> Why are you moving this to the header?
>> It is now part of the rk808 struct.
>>
>>>> +#include <linux/of_device.h>
>>>>     static bool rk808_is_volatile_reg(struct device *dev, unsigned
>>>> int reg)
>>>>   {
>>>> @@ -57,6 +56,14 @@ static bool rk808_is_volatile_reg(struct device
>>>> *dev, unsigned int reg)
>>>>       return false;
>>>>   }
>>>>   +static const struct regmap_config rk818_regmap_config = {
>>>> +    .reg_bits = 8,
>>>> +    .val_bits = 8,
>>>> +    .max_register = RK818_USB_CTRL_REG,
>>>> +    .cache_type = REGCACHE_RBTREE,
>>>> +    .volatile_reg = rk808_is_volatile_reg,
>>>> +};
>>>> +
>>>>   static const struct regmap_config rk808_regmap_config = {
>>>>       .reg_bits = 8,
>>>>       .val_bits = 8,
>>>> @@ -83,7 +90,17 @@ static const struct mfd_cell rk808s[] = {
>>>>       },
>>>>   };
>>>>   -static const struct rk808_reg_data pre_init_reg[] = {
>>>> +static const struct mfd_cell rk818s[] = {
>>>> +    { .name = "rk808-clkout", },
>>> How does this differ to a normal -clock driver?
>> I don't know. It is a normal clock driver.
>>
>>>> +    { .name = "rk808-regulator", },
>>>> +    {
>>>> +        .name = "rk808-rtc",
>>>> +        .num_resources = ARRAY_SIZE(rtc_resources),
>>>> +        .resources = &rtc_resources[0],
>>> .resources = rtc_resources,  ?
>>>
>>>> +    },
>>>> +};
>>>> +
>>>> +static const struct rk8xx_reg_data rk808_pre_init_reg[] = {
>>>>       { RK808_BUCK3_CONFIG_REG, BUCK_ILMIN_MASK,  BUCK_ILMIN_150MA },
>>>>       { RK808_BUCK4_CONFIG_REG, BUCK_ILMIN_MASK,  BUCK_ILMIN_200MA },
>>>>       { RK808_BOOST_CONFIG_REG, BOOST_ILMIN_MASK, BOOST_ILMIN_100MA },
>>>> @@ -94,6 +111,22 @@ static const struct rk808_reg_data
>>>> pre_init_reg[] = {
>>>>                               VB_LO_SEL_3500MV },
>>>>   };
>>>>   +static const struct rk8xx_reg_data rk818_pre_init_reg[] = {
>>>> +    { RK818_USB_CTRL_REG,    RK818_USB_ILIM_SEL_MASK,
>>>> +                        RK818_USB_ILMIN_2000MA },
>>>> +    /* close charger when usb lower then 3.4V */
>>>> +    { RK818_USB_CTRL_REG,    RK818_USB_CHG_SD_VSEL_MASK, (0x7 <<
>>>> 4) },
>>>> +    /* no action when vref */
>>>> +    { RK818_H5V_EN_REG,    BIT(1),        RK818_REF_RDY_CTRL },
>>>> +    /* enable HDMI 5V */
>>>> +    { RK818_H5V_EN_REG,    BIT(0),        RK818_H5V_EN },
>>>> +    /* improve efficiency */
>>>> +    { RK818_BUCK2_CONFIG_REG, BUCK2_RATE_MASK,    BUCK_ILMIN_250MA },
>>>> +    { RK818_BUCK4_CONFIG_REG, BUCK_ILMIN_MASK,    BUCK_ILMIN_250MA },
>>>> +    { RK818_BOOST_CONFIG_REG, BOOST_ILMIN_MASK,   
>>>> BOOST_ILMIN_100MA },
>>>> +    { RK808_VB_MON_REG,    MASK_ALL,    VB_LO_ACT |
>>>> VB_LO_SEL_3500MV },
>>>> +};
>>> The alignment here looks odd.
>> I will fix it in the next version.
>>
>>>>   static const struct regmap_irq rk808_irqs[] = {
>>>>       /* INT_STS */
>>>>       [RK808_IRQ_VOUT_LO] = {
>>>> @@ -136,6 +169,76 @@ static const struct regmap_irq rk808_irqs[] = {
>>>>       },
>>>>   };
>>>>   +static const struct regmap_irq rk818_irqs[] = {
>>>> +    /* INT_STS */
>>>> +    [RK818_IRQ_VOUT_LO] = {
>>>> +        .mask = RK818_IRQ_VOUT_LO_MSK,
>>>> +        .reg_offset = 0,
>>>> +    },
>>>> +    [RK818_IRQ_VB_LO] = {
>>>> +        .mask = RK818_IRQ_VB_LO_MSK,
>>>> +        .reg_offset = 0,
>>>> +    },
>>>> +    [RK818_IRQ_PWRON] = {
>>>> +        .mask = RK818_IRQ_PWRON_MSK,
>>>> +        .reg_offset = 0,
>>>> +    },
>>>> +    [RK818_IRQ_PWRON_LP] = {
>>>> +        .mask = RK818_IRQ_PWRON_LP_MSK,
>>>> +        .reg_offset = 0,
>>>> +    },
>>>> +    [RK818_IRQ_HOTDIE] = {
>>>> +        .mask = RK818_IRQ_HOTDIE_MSK,
>>>> +        .reg_offset = 0,
>>>> +    },
>>>> +    [RK818_IRQ_RTC_ALARM] = {
>>>> +        .mask = RK818_IRQ_RTC_ALARM_MSK,
>>>> +        .reg_offset = 0,
>>>> +    },
>>>> +    [RK818_IRQ_RTC_PERIOD] = {
>>>> +        .mask = RK818_IRQ_RTC_PERIOD_MSK,
>>>> +        .reg_offset = 0,
>>>> +    },
>>>> +    [RK818_IRQ_USB_OV] = {
>>>> +        .mask = RK818_IRQ_USB_OV_MSK,
>>>> +        .reg_offset = 0,
>>>> +    },
>>>> +
>>>> +    /* INT_STS2 */
>>>> +    [RK818_IRQ_PLUG_IN] = {
>>>> +        .mask = RK818_IRQ_PLUG_IN_MSK,
>>>> +        .reg_offset = 1,
>>>> +    },
>>>> +    [RK818_IRQ_PLUG_OUT] = {
>>>> +        .mask = RK818_IRQ_PLUG_OUT_MSK,
>>>> +        .reg_offset = 1,
>>>> +    },
>>>> +    [RK818_IRQ_CHG_OK] = {
>>>> +        .mask = RK818_IRQ_CHG_OK_MSK,
>>>> +        .reg_offset = 1,
>>>> +    },
>>>> +    [RK818_IRQ_CHG_TE] = {
>>>> +        .mask = RK818_IRQ_CHG_TE_MSK,
>>>> +        .reg_offset = 1,
>>>> +    },
>>>> +    [RK818_IRQ_CHG_TS1] = {
>>>> +        .mask = RK818_IRQ_CHG_TS1_MSK,
>>>> +        .reg_offset = 1,
>>>> +    },
>>>> +    [RK818_IRQ_TS2] = {
>>>> +        .mask = RK818_IRQ_TS2_MSK,
>>>> +        .reg_offset = 1,
>>>> +    },
>>>> +    [RK818_IRQ_CHG_CVTLIM] = {
>>>> +        .mask = RK818_IRQ_CHG_CVTLIM_MSK,
>>>> +        .reg_offset = 1,
>>>> +    },
>>>> +    [RK818_IRQ_DISCHG_ILIM] = {
>>>> +        .mask = RK818_IRQ_DISCHG_ILIM_MSK,
>>>> +        .reg_offset = 1,
>>>> +    },
>>>> +};
>>>> +
>>>>   static struct regmap_irq_chip rk808_irq_chip = {
>>>>       .name = "rk808",
>>>>       .irqs = rk808_irqs,
>>>> @@ -148,6 +251,18 @@ static struct regmap_irq_chip rk808_irq_chip = {
>>>>       .init_ack_masked = true,
>>>>   };
>>>>   +static struct regmap_irq_chip rk818_irq_chip = {
>>>> +    .name = "rk818",
>>>> +    .irqs = rk818_irqs,
>>>> +    .num_irqs = ARRAY_SIZE(rk818_irqs),
>>>> +    .num_regs = 2,
>>>> +    .irq_reg_stride = 2,
>>>> +    .status_base = RK818_INT_STS_REG1,
>>>> +    .mask_base = RK818_INT_STS_MSK_REG1,
>>>> +    .ack_base = RK818_INT_STS_REG1,
>>>> +    .init_ack_masked = true,
>>>> +};
>>>> +
>>>>   static struct i2c_client *rk808_i2c_client;
>>>>   static void rk808_device_shutdown(void)
>>>>   {
>>>> @@ -167,6 +282,48 @@ static void rk808_device_shutdown(void)
>>>>           dev_err(&rk808_i2c_client->dev, "power off error!\n");
>>>>   }
>>>>   +static const struct of_device_id rk808_of_match[] = {
>>>> +    { .compatible = "rockchip,rk808", .data = (void *) RK808_ID },
>>>> +    { .compatible = "rockchip,rk818", .data = (void *) RK818_ID },
>>>> +    { },
>>>> +};
>>>> +MODULE_DEVICE_TABLE(of, rk808_of_match);
>>>> +
>>>> +static int rk8xx_match_device(struct rk808 *rk808, struct device
>>>> *dev)
>>> If you're going to do it this way, please switch these parameters.
>>>
>>> It's more common to return the pointer however.
>> If it's more common I will return a pointer to rk808.
>>
>>>> +{
>>>> +    const struct of_device_id *of_id;
>>>> +
>>>> +    of_id = of_match_device(rk808_of_match, dev);
>>>> +    if (!of_id) {
>>>> +        dev_err(dev, "Unable to match OF ID\n");
>>>> +        return -ENODEV;
>>>> +    }
>>>> +    rk808->variant = (long) of_id->data;
>>>> +
>>>> +    switch (rk808->variant) {
>>>> +    case RK808_ID:
>>>> +        rk808->nr_cells = ARRAY_SIZE(rk808s);
>>>> +        rk808->cells = rk808s;
>>>> +        rk808->regmap_cfg = &rk808_regmap_config;
>>>> +        rk808->regmap_irq_chip = &rk808_irq_chip;
>>>> +        rk808->pre_init_reg = rk808_pre_init_reg;
>>>> +        rk808->nr_pre_init_regs = ARRAY_SIZE(rk808_pre_init_reg);
>>>> +        break;
>>>> +    case RK818_ID:
>>>> +        rk808->nr_cells = ARRAY_SIZE(rk818s);
>>>> +        rk808->cells = rk818s;
>>>> +        rk808->regmap_cfg = &rk818_regmap_config;
>>>> +        rk808->regmap_irq_chip = &rk818_irq_chip;
>>>> +        rk808->pre_init_reg = rk818_pre_init_reg;
>>>> +        rk808->nr_pre_init_regs = ARRAY_SIZE(rk818_pre_init_reg);
>>>> +        break;
>>>> +    default:
>>>> +        dev_err(dev, "unsupported RK8XX ID %lu\n", rk808->variant);
>>>> +        return -EINVAL;
>>>> +    }
>>>> +
>>>> +    return 0;
>>>> +}
>>>>   static int rk808_probe(struct i2c_client *client,
>>>>                  const struct i2c_device_id *id)
>>>>   {
>>>> @@ -176,46 +333,52 @@ static int rk808_probe(struct i2c_client
>>>> *client,
>>>>       int ret;
>>>>       int i;
>>>>   -    if (!client->irq) {
>>>> -        dev_err(&client->dev, "No interrupt support, no core IRQ\n");
>>>> -        return -EINVAL;
>>>> -    }
>>>> -
>>>>       rk808 = devm_kzalloc(&client->dev, sizeof(*rk808), GFP_KERNEL);
>>>>       if (!rk808)
>>>>           return -ENOMEM;
>>>>   -    rk808->regmap = devm_regmap_init_i2c(client,
>>>> &rk808_regmap_config);
>>>> +    ret = rk8xx_match_device(rk808, &client->dev);
>>> Is there a way to dynamically probe the device?  No device ID you can
>>> read directly from the silicon?
>> AFAIK there is no device ID register. At least it is not documented in
>> the manual.
>
> register 0x17 and 0x18 is the ID register, 0x17 is the MSB, 0x18 is LSB
>
> for RK818, register 0x17 is 0x81, 0x18 is 0x81

thank you for sharing this information. I have no RK808 PMIC device
here, so I would also need the IDs for RK808.

Lee, you have already applied this series. But I can't find the patches
in your kernel tree.
I would like to read the device ID from the register in the probe function.
Do you want me to base my changes on top of this series or send a new
version?


From wxt at rock-chips.com  Wed Jul  6 01:05:55 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Wed,  6 Jul 2016 16:05:55 +0800
Subject: [PATCH v2 0/2] arm64: dts: rockchip: Support PMU for rk3399 SoCs
Message-ID: <1467792357-9749-1-git-send-email-wxt@rock-chips.com>

Hello Heiko, Marc & ARM guys

When Jay first submitted the rk3399.dtsi upstream
<https://patchwork.kernel.org/patch/8885821/> he had the PMU node in there,
but then took it out because the upstream binding wasn't done yet.
It looks as if the upstream stuff has landed, since in linux/master I see:
287e9357abcc DT/arm,gic-v3: Documment PPI partition support
e3825ba1af3a irqchip/gic-v3: Add support for partitioned PPIs
9e2c986cb460 irqchip: Add per-cpu interrupt partitioning library
222df54fd8b7 genirq: Allow the affinity of a percpu interrupt to be set/retrieved
651e8b54abde irqdomain: Allow domain matching on irq_fwspec

This series patches add to support the rk3399 SoCs PMU.
I pick up the https://patchwork.kernel.org/patch/9209369/.

As do some tests with ChromeOs for my rk3399 board.
Tested with linus master 4.7-rc6 kernel on rk3399 board.
https://github.com/Caesar-github/rockchip/tree/rk3399/pmu-upstream

localhost / # perf list

List of pre-defined events (to be used in -e):
cpu-cycles OR cycles [Hardware event]
instructions [Hardware event]
cache-references [Hardware event]
cache-misses [Hardware event]
branch-instructions OR branches [Hardware event]
branch-misses [Hardware event]
bus-cycles [Hardware event]
...

perf stat --cpu 0/1/2/3..... to minitor
e.g. cpu0;

localhost / # perf stat --cpu 0

Performance counter stats for 'CPU(s) 0':

3374.917571 task-clock (msec) # 1.001 CPUs utilized [100.00%]
20 context-switches # 0.006 K/sec [100.00%]
2 cpu-migrations # 0.001 K/sec [100.00%]
55 page-faults # 0.016 K/sec
7151843 cycles # 0.002 GHz [100.00%]
<not supported> stalled-cycles-frontend
<not supported> stalled-cycles-backend
4272536 instructions # 0.60 insns per cycle [100.00%]
568406 branches # 0.168 M/sec [100.00%]
65652 branch-misses # 11.55% of all branches

Also, 'perf top' to monitor the PMU interrupts from cpus

-Caesar


Changes in v2:
- AS Mark comments on https://patchwork.kernel.org/patch/9209369/
  remove the interrupt-affinity property, we need depend on Marc' perf
  code on https://patchwork.kernel.org/patch/9209369/.

Caesar Wang (2):
  arm64: dts: rockchip: change all interrupts cells for 4 on rk3399 SoCs
  arm64: dts: rockchip: support the pmu node for rk3399

 arch/arm64/boot/dts/rockchip/rk3399.dtsi | 118 ++++++++++++++++++-------------
 1 file changed, 69 insertions(+), 49 deletions(-)

-- 
1.9.1



From wxt at rock-chips.com  Wed Jul  6 01:05:56 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Wed,  6 Jul 2016 16:05:56 +0800
Subject: [PATCH v2 1/2] arm64: dts: rockchip: change all interrupts cells for
 4 on rk3399 SoCs
In-Reply-To: <1467792357-9749-1-git-send-email-wxt@rock-chips.com>
References: <1467792357-9749-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1467792357-9749-2-git-send-email-wxt@rock-chips.com>

Add the interrupts cells value for 4, and the 4th cell is zero.

Due to the doc[0] said:" the system requires describing PPI affinity,
then the value must be at least 4"
The 4th cell is a phandle to a node describing a set of CPUs this
interrupt is affine to. The interrupt must be a PPI, and the node
pointed must be a subnode of the "ppi-partitions" subnode. For
interrupt types other than PPI or PPIs that are not partitionned,
this cell must be zero. See the "ppi-partitions" node description
below.

[0]:
Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.txt

Signed-off-by: Caesar Wang <wxt at rock-chips.com>
Cc: Heiko Stuebner <heiko at sntech.de>
Cc: Will Deacon <will.deacon at arm.com>
Cc: Marc Zyngier <marc.zyngier at arm.com>
CC: linux-arm-kernel at lists.infradead.org
---

Changes in v2: None

 arch/arm64/boot/dts/rockchip/rk3399.dtsi | 98 ++++++++++++++++----------------
 1 file changed, 49 insertions(+), 49 deletions(-)

diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index a6dd623..8f0a069 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -159,10 +159,10 @@
 
 	timer {
 		compatible = "arm,armv8-timer";
-		interrupts = <GIC_PPI 13 IRQ_TYPE_LEVEL_LOW>,
-			     <GIC_PPI 14 IRQ_TYPE_LEVEL_LOW>,
-			     <GIC_PPI 11 IRQ_TYPE_LEVEL_LOW>,
-			     <GIC_PPI 10 IRQ_TYPE_LEVEL_LOW>;
+		interrupts = <GIC_PPI 13 IRQ_TYPE_LEVEL_LOW 0>,
+			     <GIC_PPI 14 IRQ_TYPE_LEVEL_LOW 0>,
+			     <GIC_PPI 11 IRQ_TYPE_LEVEL_LOW 0>,
+			     <GIC_PPI 10 IRQ_TYPE_LEVEL_LOW 0>;
 	};
 
 	xin24m: xin24m {
@@ -181,8 +181,8 @@
 		dmac_bus: dma-controller at ff6d0000 {
 			compatible = "arm,pl330", "arm,primecell";
 			reg = <0x0 0xff6d0000 0x0 0x4000>;
-			interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>,
-				     <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
+			interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH 0>,
+				     <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH 0>;
 			#dma-cells = <1>;
 			clocks = <&cru ACLK_DMAC0_PERILP>;
 			clock-names = "apb_pclk";
@@ -191,8 +191,8 @@
 		dmac_peri: dma-controller at ff6e0000 {
 			compatible = "arm,pl330", "arm,primecell";
 			reg = <0x0 0xff6e0000 0x0 0x4000>;
-			interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>,
-				     <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
+			interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH 0>,
+				     <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH 0>;
 			#dma-cells = <1>;
 			clocks = <&cru ACLK_DMAC1_PERILP>;
 			clock-names = "apb_pclk";
@@ -203,7 +203,7 @@
 		compatible = "rockchip,rk3399-dw-mshc",
 			     "rockchip,rk3288-dw-mshc";
 		reg = <0x0 0xfe310000 0x0 0x4000>;
-		interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH 0>;
 		clock-freq-min-max = <400000 150000000>;
 		clocks = <&cru HCLK_SDIO>, <&cru SCLK_SDIO>,
 			 <&cru SCLK_SDIO_DRV>, <&cru SCLK_SDIO_SAMPLE>;
@@ -216,7 +216,7 @@
 		compatible = "rockchip,rk3399-dw-mshc",
 			     "rockchip,rk3288-dw-mshc";
 		reg = <0x0 0xfe320000 0x0 0x4000>;
-		interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH 0>;
 		clock-freq-min-max = <400000 150000000>;
 		clocks = <&cru HCLK_SDMMC>, <&cru SCLK_SDMMC>,
 			 <&cru SCLK_SDMMC_DRV>, <&cru SCLK_SDMMC_SAMPLE>;
@@ -228,7 +228,7 @@
 	sdhci: sdhci at fe330000 {
 		compatible = "rockchip,rk3399-sdhci-5.1", "arasan,sdhci-5.1";
 		reg = <0x0 0xfe330000 0x0 0x10000>;
-		interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH 0>;
 		arasan,soc-ctl-syscon = <&grf>;
 		assigned-clocks = <&cru SCLK_EMMC>;
 		assigned-clock-rates = <200000000>;
@@ -244,7 +244,7 @@
 	usb_host0_ehci: usb at fe380000 {
 		compatible = "generic-ehci";
 		reg = <0x0 0xfe380000 0x0 0x20000>;
-		interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH 0>;
 		clocks = <&cru HCLK_HOST0>, <&cru HCLK_HOST0_ARB>;
 		clock-names = "hclk_host0", "hclk_host0_arb";
 		status = "disabled";
@@ -253,7 +253,7 @@
 	usb_host0_ohci: usb at fe3a0000 {
 		compatible = "generic-ohci";
 		reg = <0x0 0xfe3a0000 0x0 0x20000>;
-		interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH 0>;
 		clocks = <&cru HCLK_HOST0>, <&cru HCLK_HOST0_ARB>;
 		clock-names = "hclk_host0", "hclk_host0_arb";
 		status = "disabled";
@@ -262,7 +262,7 @@
 	usb_host1_ehci: usb at fe3c0000 {
 		compatible = "generic-ehci";
 		reg = <0x0 0xfe3c0000 0x0 0x20000>;
-		interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH 0>;
 		clocks = <&cru HCLK_HOST1>, <&cru HCLK_HOST1_ARB>;
 		clock-names = "hclk_host1", "hclk_host1_arb";
 		status = "disabled";
@@ -271,7 +271,7 @@
 	usb_host1_ohci: usb at fe3e0000 {
 		compatible = "generic-ohci";
 		reg = <0x0 0xfe3e0000 0x0 0x20000>;
-		interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH 0>;
 		clocks = <&cru HCLK_HOST1>, <&cru HCLK_HOST1_ARB>;
 		clock-names = "hclk_host1", "hclk_host1_arb";
 		status = "disabled";
@@ -279,7 +279,7 @@
 
 	gic: interrupt-controller at fee00000 {
 		compatible = "arm,gic-v3";
-		#interrupt-cells = <3>;
+		#interrupt-cells = <4>;
 		#address-cells = <2>;
 		#size-cells = <2>;
 		ranges;
@@ -290,7 +290,7 @@
 		      <0x0 0xfff00000 0 0x10000>, /* GICC */
 		      <0x0 0xfff10000 0 0x10000>, /* GICH */
 		      <0x0 0xfff20000 0 0x10000>; /* GICV */
-		interrupts = <GIC_PPI 9 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_PPI 9 IRQ_TYPE_LEVEL_HIGH 0>;
 		its: interrupt-controller at fee20000 {
 			compatible = "arm,gic-v3-its";
 			msi-controller;
@@ -305,7 +305,7 @@
 		assigned-clock-rates = <200000000>;
 		clocks = <&cru SCLK_I2C1>, <&cru PCLK_I2C1>;
 		clock-names = "i2c", "pclk";
-		interrupts = <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH 0>;
 		pinctrl-names = "default";
 		pinctrl-0 = <&i2c1_xfer>;
 		#address-cells = <1>;
@@ -320,7 +320,7 @@
 		assigned-clock-rates = <200000000>;
 		clocks = <&cru SCLK_I2C2>, <&cru PCLK_I2C2>;
 		clock-names = "i2c", "pclk";
-		interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH 0>;
 		pinctrl-names = "default";
 		pinctrl-0 = <&i2c2_xfer>;
 		#address-cells = <1>;
@@ -335,7 +335,7 @@
 		assigned-clock-rates = <200000000>;
 		clocks = <&cru SCLK_I2C3>, <&cru PCLK_I2C3>;
 		clock-names = "i2c", "pclk";
-		interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH 0>;
 		pinctrl-names = "default";
 		pinctrl-0 = <&i2c3_xfer>;
 		#address-cells = <1>;
@@ -350,7 +350,7 @@
 		assigned-clock-rates = <200000000>;
 		clocks = <&cru SCLK_I2C5>, <&cru PCLK_I2C5>;
 		clock-names = "i2c", "pclk";
-		interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH 0>;
 		pinctrl-names = "default";
 		pinctrl-0 = <&i2c5_xfer>;
 		#address-cells = <1>;
@@ -365,7 +365,7 @@
 		assigned-clock-rates = <200000000>;
 		clocks = <&cru SCLK_I2C6>, <&cru PCLK_I2C6>;
 		clock-names = "i2c", "pclk";
-		interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH 0>;
 		pinctrl-names = "default";
 		pinctrl-0 = <&i2c6_xfer>;
 		#address-cells = <1>;
@@ -380,7 +380,7 @@
 		assigned-clock-rates = <200000000>;
 		clocks = <&cru SCLK_I2C7>, <&cru PCLK_I2C7>;
 		clock-names = "i2c", "pclk";
-		interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH 0>;
 		pinctrl-names = "default";
 		pinctrl-0 = <&i2c7_xfer>;
 		#address-cells = <1>;
@@ -393,7 +393,7 @@
 		reg = <0x0 0xff180000 0x0 0x100>;
 		clocks = <&cru SCLK_UART0>, <&cru PCLK_UART0>;
 		clock-names = "baudclk", "apb_pclk";
-		interrupts = <GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH 0>;
 		reg-shift = <2>;
 		reg-io-width = <4>;
 		pinctrl-names = "default";
@@ -406,7 +406,7 @@
 		reg = <0x0 0xff190000 0x0 0x100>;
 		clocks = <&cru SCLK_UART1>, <&cru PCLK_UART1>;
 		clock-names = "baudclk", "apb_pclk";
-		interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH 0>;
 		reg-shift = <2>;
 		reg-io-width = <4>;
 		pinctrl-names = "default";
@@ -419,7 +419,7 @@
 		reg = <0x0 0xff1a0000 0x0 0x100>;
 		clocks = <&cru SCLK_UART2>, <&cru PCLK_UART2>;
 		clock-names = "baudclk", "apb_pclk";
-		interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH 0>;
 		reg-shift = <2>;
 		reg-io-width = <4>;
 		pinctrl-names = "default";
@@ -432,7 +432,7 @@
 		reg = <0x0 0xff1b0000 0x0 0x100>;
 		clocks = <&cru SCLK_UART3>, <&cru PCLK_UART3>;
 		clock-names = "baudclk", "apb_pclk";
-		interrupts = <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH 0>;
 		reg-shift = <2>;
 		reg-io-width = <4>;
 		pinctrl-names = "default";
@@ -445,7 +445,7 @@
 		reg = <0x0 0xff1c0000 0x0 0x1000>;
 		clocks = <&cru SCLK_SPI0>, <&cru PCLK_SPI0>;
 		clock-names = "spiclk", "apb_pclk";
-		interrupts = <GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH 0>;
 		pinctrl-names = "default";
 		pinctrl-0 = <&spi0_clk &spi0_tx &spi0_rx &spi0_cs0>;
 		#address-cells = <1>;
@@ -458,7 +458,7 @@
 		reg = <0x0 0xff1d0000 0x0 0x1000>;
 		clocks = <&cru SCLK_SPI1>, <&cru PCLK_SPI1>;
 		clock-names = "spiclk", "apb_pclk";
-		interrupts = <GIC_SPI 53 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 53 IRQ_TYPE_LEVEL_HIGH 0>;
 		pinctrl-names = "default";
 		pinctrl-0 = <&spi1_clk &spi1_tx &spi1_rx &spi1_cs0>;
 		#address-cells = <1>;
@@ -471,7 +471,7 @@
 		reg = <0x0 0xff1e0000 0x0 0x1000>;
 		clocks = <&cru SCLK_SPI2>, <&cru PCLK_SPI2>;
 		clock-names = "spiclk", "apb_pclk";
-		interrupts = <GIC_SPI 52 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 52 IRQ_TYPE_LEVEL_HIGH 0>;
 		pinctrl-names = "default";
 		pinctrl-0 = <&spi2_clk &spi2_tx &spi2_rx &spi2_cs0>;
 		#address-cells = <1>;
@@ -484,7 +484,7 @@
 		reg = <0x0 0xff1f0000 0x0 0x1000>;
 		clocks = <&cru SCLK_SPI4>, <&cru PCLK_SPI4>;
 		clock-names = "spiclk", "apb_pclk";
-		interrupts = <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH 0>;
 		pinctrl-names = "default";
 		pinctrl-0 = <&spi4_clk &spi4_tx &spi4_rx &spi4_cs0>;
 		#address-cells = <1>;
@@ -497,7 +497,7 @@
 		reg = <0x0 0xff200000 0x0 0x1000>;
 		clocks = <&cru SCLK_SPI5>, <&cru PCLK_SPI5>;
 		clock-names = "spiclk", "apb_pclk";
-		interrupts = <GIC_SPI 132 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 132 IRQ_TYPE_LEVEL_HIGH 0>;
 		pinctrl-names = "default";
 		pinctrl-0 = <&spi5_clk &spi5_tx &spi5_rx &spi5_cs0>;
 		#address-cells = <1>;
@@ -577,7 +577,7 @@
 	tsadc: tsadc at ff260000 {
 		compatible = "rockchip,rk3399-tsadc";
 		reg = <0x0 0xff260000 0x0 0x100>;
-		interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH 0>;
 		assigned-clocks = <&cru SCLK_TSADC>;
 		assigned-clock-rates = <750000>;
 		clocks = <&cru SCLK_TSADC>, <&cru PCLK_TSADC>;
@@ -611,7 +611,7 @@
 		reg = <0x0 0xff350000 0x0 0x1000>;
 		clocks = <&pmucru SCLK_SPI3_PMU>, <&pmucru PCLK_SPI3_PMU>;
 		clock-names = "spiclk", "apb_pclk";
-		interrupts = <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH 0>;
 		pinctrl-names = "default";
 		pinctrl-0 = <&spi3_clk &spi3_tx &spi3_rx &spi3_cs0>;
 		#address-cells = <1>;
@@ -624,7 +624,7 @@
 		reg = <0x0 0xff370000 0x0 0x100>;
 		clocks = <&pmucru SCLK_UART4_PMU>, <&pmucru PCLK_UART4_PMU>;
 		clock-names = "baudclk", "apb_pclk";
-		interrupts = <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH 0>;
 		reg-shift = <2>;
 		reg-io-width = <4>;
 		pinctrl-names = "default";
@@ -639,7 +639,7 @@
 		assigned-clock-rates = <200000000>;
 		clocks = <&pmucru SCLK_I2C0_PMU>, <&pmucru PCLK_I2C0_PMU>;
 		clock-names = "i2c", "pclk";
-		interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH 0>;
 		pinctrl-names = "default";
 		pinctrl-0 = <&i2c0_xfer>;
 		#address-cells = <1>;
@@ -654,7 +654,7 @@
 		assigned-clock-rates = <200000000>;
 		clocks = <&pmucru SCLK_I2C4_PMU>, <&pmucru PCLK_I2C4_PMU>;
 		clock-names = "i2c", "pclk";
-		interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH 0>;
 		pinctrl-names = "default";
 		pinctrl-0 = <&i2c4_xfer>;
 		#address-cells = <1>;
@@ -669,7 +669,7 @@
 		assigned-clock-rates = <200000000>;
 		clocks = <&pmucru SCLK_I2C8_PMU>, <&pmucru PCLK_I2C8_PMU>;
 		clock-names = "i2c", "pclk";
-		interrupts = <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH 0>;
 		pinctrl-names = "default";
 		pinctrl-0 = <&i2c8_xfer>;
 		#address-cells = <1>;
@@ -778,13 +778,13 @@
 		compatible = "snps,dw-wdt";
 		reg = <0x0 0xff840000 0x0 0x100>;
 		clocks = <&cru PCLK_WDT>;
-		interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH 0>;
 	};
 
 	rktimer: rktimer at ff850000 {
 		compatible = "rockchip,rk3399-timer";
 		reg = <0x0 0xff850000 0x0 0x1000>;
-		interrupts = <GIC_SPI 81 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 81 IRQ_TYPE_LEVEL_HIGH 0>;
 		clocks = <&cru PCLK_TIMER0>, <&cru SCLK_TIMER00>;
 		clock-names = "pclk", "timer";
 	};
@@ -792,7 +792,7 @@
 	spdif: spdif at ff870000 {
 		compatible = "rockchip,rk3399-spdif";
 		reg = <0x0 0xff870000 0x0 0x1000>;
-		interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH 0>;
 		dmas = <&dmac_bus 7>;
 		dma-names = "tx";
 		clock-names = "mclk", "hclk";
@@ -806,7 +806,7 @@
 		compatible = "rockchip,rk3399-i2s", "rockchip,rk3066-i2s";
 		reg = <0x0 0xff880000 0x0 0x1000>;
 		rockchip,grf = <&grf>;
-		interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH 0>;
 		dmas = <&dmac_bus 0>, <&dmac_bus 1>;
 		dma-names = "tx", "rx";
 		clock-names = "i2s_clk", "i2s_hclk";
@@ -819,7 +819,7 @@
 	i2s1: i2s at ff890000 {
 		compatible = "rockchip,rk3399-i2s", "rockchip,rk3066-i2s";
 		reg = <0x0 0xff890000 0x0 0x1000>;
-		interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH 0>;
 		dmas = <&dmac_bus 2>, <&dmac_bus 3>;
 		dma-names = "tx", "rx";
 		clock-names = "i2s_clk", "i2s_hclk";
@@ -832,7 +832,7 @@
 	i2s2: i2s at ff8a0000 {
 		compatible = "rockchip,rk3399-i2s", "rockchip,rk3066-i2s";
 		reg = <0x0 0xff8a0000 0x0 0x1000>;
-		interrupts = <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
+		interrupts = <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH 0>;
 		dmas = <&dmac_bus 4>, <&dmac_bus 5>;
 		dma-names = "tx", "rx";
 		clock-names = "i2s_clk", "i2s_hclk";
@@ -852,7 +852,7 @@
 			compatible = "rockchip,gpio-bank";
 			reg = <0x0 0xff720000 0x0 0x100>;
 			clocks = <&pmucru PCLK_GPIO0_PMU>;
-			interrupts = <GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>;
+			interrupts = <GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH 0>;
 
 			gpio-controller;
 			#gpio-cells = <0x2>;
@@ -865,7 +865,7 @@
 			compatible = "rockchip,gpio-bank";
 			reg = <0x0 0xff730000 0x0 0x100>;
 			clocks = <&pmucru PCLK_GPIO1_PMU>;
-			interrupts = <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>;
+			interrupts = <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH 0>;
 
 			gpio-controller;
 			#gpio-cells = <0x2>;
@@ -878,7 +878,7 @@
 			compatible = "rockchip,gpio-bank";
 			reg = <0x0 0xff780000 0x0 0x100>;
 			clocks = <&cru PCLK_GPIO2>;
-			interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
+			interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH 0>;
 
 			gpio-controller;
 			#gpio-cells = <0x2>;
@@ -891,7 +891,7 @@
 			compatible = "rockchip,gpio-bank";
 			reg = <0x0 0xff788000 0x0 0x100>;
 			clocks = <&cru PCLK_GPIO3>;
-			interrupts = <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;
+			interrupts = <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH 0>;
 
 			gpio-controller;
 			#gpio-cells = <0x2>;
@@ -904,7 +904,7 @@
 			compatible = "rockchip,gpio-bank";
 			reg = <0x0 0xff790000 0x0 0x100>;
 			clocks = <&cru PCLK_GPIO4>;
-			interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>;
+			interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH 0>;
 
 			gpio-controller;
 			#gpio-cells = <0x2>;
-- 
1.9.1



From wxt at rock-chips.com  Wed Jul  6 01:05:57 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Wed,  6 Jul 2016 16:05:57 +0800
Subject: [PATCH v2 2/2] arm64: dts: rockchip: support the pmu node for rk3399
In-Reply-To: <1467792357-9749-1-git-send-email-wxt@rock-chips.com>
References: <1467792357-9749-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1467792357-9749-3-git-send-email-wxt@rock-chips.com>

This patch add to enable the ARM Performance Monitor Units for rk3399.
ARM cores often have a PMU for counting cpu and cache events like cache
misses and hits.

Also, as the Marc posted the patches [0] to support Partitioning per-cpu
interrupts. Let's add this patch to match it on rk3399 SoCs.

[0]:
https://lkml.org/lkml/2016/4/11/182
https://patchwork.kernel.org/patch/9209369/

Signed-off-by: Caesar Wang <wxt at rock-chips.com>
Cc: Heiko Stuebner <heiko at sntech.de>
Cc: Marc Zyngier <marc.zyngier at arm.com>
CC: linux-arm-kernel at lists.infradead.org

---

Changes in v2:
- AS Mark comments on https://patchwork.kernel.org/patch/9209369/
  remove the interrupt-affinity property, we need depend on Marc' perf
  code on https://patchwork.kernel.org/patch/9209369/.

 arch/arm64/boot/dts/rockchip/rk3399.dtsi | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index 8f0a069..4bcd02b 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -165,6 +165,16 @@
 			     <GIC_PPI 10 IRQ_TYPE_LEVEL_LOW 0>;
 	};
 
+	pmu_a53 {
+		compatible = "arm,cortex-a53-pmu";
+		interrupts = <GIC_PPI 7 IRQ_TYPE_LEVEL_LOW &part0>;
+	};
+
+	pmu_a72 {
+		compatible = "arm,cortex-a72-pmu";
+		interrupts = <GIC_PPI 7 IRQ_TYPE_LEVEL_LOW &part1>;
+	};
+
 	xin24m: xin24m {
 		compatible = "fixed-clock";
 		clock-frequency = <24000000>;
@@ -296,6 +306,16 @@
 			msi-controller;
 			reg = <0x0 0xfee20000 0x0 0x20000>;
 		};
+
+		ppi-partitions {
+			part0: interrupt-partition-0 {
+				affinity = <&cpu_l0 &cpu_l1 &cpu_l2 &cpu_l3>;
+			};
+
+			part1: interrupt-partition-1 {
+				affinity = <&cpu_b0 &cpu_b1>;
+			};
+		};
 	};
 
 	i2c1: i2c at ff110000 {
-- 
1.9.1



From wxt at rock-chips.com  Wed Jul  6 01:20:54 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Wed,  6 Jul 2016 16:20:54 +0800
Subject: [PATCH] arm64: dts: rockchip: Add idle-states to device tree for
 rk3399
Message-ID: <1467793254-10808-1-git-send-email-wxt@rock-chips.com>

As the rk3399 ATF had been supported on ARM github [0], so we can add
idle-states for rk3399.
This patch adds idle-states bindings data collected through tests
experiments (latency and energy consumption) on rk3399 evb2 board.

You can see detail idle-states definitions on document [1].

* arm,psci-suspend-param: power_state parameter to pass to the PSCI
  suspend call.
* entry-latency: Worst case latency required to enter the idle state. The
  exit-latency may be guaranteed only after entry-latency has passed.
* min-residency: Minimum period, including preparation and entry, for a
  given idle state to be worthwhile energywise
* min-residency: Minimum period, including preparation and entry, for a
  given idle state to be worthwhile energywise.

[0]:
https://github.com/ARM-software/arm-trusted-firmware
[1]:
Documentation/devicetree/bindings/arm/psci.txt
Documentation/devicetree/bindings/arm/idle-states.txt

Signed-off-by: Caesar Wang <wxt at rock-chips.com>
---

 arch/arm64/boot/dts/rockchip/rk3399.dtsi | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index a6dd623..12ce265 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -101,6 +101,18 @@
 			};
 		};
 
+		idle-states {
+			entry-method = "psci";
+			cpu_sleep: cpu-sleep-0 {
+				compatible = "arm,idle-state";
+				local-timer-stop;
+				arm,psci-suspend-param = <0x0010000>;
+				entry-latency-us = <350>;
+				exit-latency-us = <600>;
+				min-residency-us = <1150>;
+			};
+		};
+
 		cpu_l0: cpu at 0 {
 			device_type = "cpu";
 			compatible = "arm,cortex-a53", "arm,armv8";
@@ -108,6 +120,7 @@
 			enable-method = "psci";
 			#cooling-cells = <2>; /* min followed by max */
 			clocks = <&cru ARMCLKL>;
+			cpu-idle-states = <&cpu_sleep>;
 		};
 
 		cpu_l1: cpu at 1 {
@@ -116,6 +129,7 @@
 			reg = <0x0 0x1>;
 			enable-method = "psci";
 			clocks = <&cru ARMCLKL>;
+			cpu-idle-states = <&cpu_sleep>;
 		};
 
 		cpu_l2: cpu at 2 {
@@ -124,6 +138,7 @@
 			reg = <0x0 0x2>;
 			enable-method = "psci";
 			clocks = <&cru ARMCLKL>;
+			cpu-idle-states = <&cpu_sleep>;
 		};
 
 		cpu_l3: cpu at 3 {
@@ -132,6 +147,7 @@
 			reg = <0x0 0x3>;
 			enable-method = "psci";
 			clocks = <&cru ARMCLKL>;
+			cpu-idle-states = <&cpu_sleep>;
 		};
 
 		cpu_b0: cpu at 100 {
@@ -141,6 +157,7 @@
 			enable-method = "psci";
 			#cooling-cells = <2>; /* min followed by max */
 			clocks = <&cru ARMCLKB>;
+			cpu-idle-states = <&cpu_sleep>;
 		};
 
 		cpu_b1: cpu at 101 {
@@ -149,6 +166,7 @@
 			reg = <0x0 0x101>;
 			enable-method = "psci";
 			clocks = <&cru ARMCLKB>;
+			cpu-idle-states = <&cpu_sleep>;
 		};
 	};
 
-- 
1.9.1



From andy.yan at rock-chips.com  Wed Jul  6 01:56:48 2016
From: andy.yan at rock-chips.com (Andy Yan)
Date: Wed, 6 Jul 2016 16:56:48 +0800
Subject: [RESEND PATCH v5 1/5] mfd: RK808: Add RK818 support
In-Reply-To: <577CBB62.8080806@phytec.de>
References: <1464850228-17244-1-git-send-email-w.egorov@phytec.de>
 <20160608141735.GE14888@dell> <5759277D.7090101@phytec.de>
 <577C77DF.9060706@rock-chips.com> <577CBB62.8080806@phytec.de>
Message-ID: <577CC7D0.2040402@rock-chips.com>

Hi Wadim:

On 2016?07?06? 16:03, Wadim Egorov wrote:
> Hi Andy,
>
> On 06.07.2016 05:15, Andy Yan wrote:
>> Hi Wadim:
>>
>> On 2016?06?09? 16:23, Wadim Egorov wrote:
>>> Hi,
>>>
>>> On 08.06.2016 16:17, Lee Jones wrote:
>>>> On Thu, 02 Jun 2016, Wadim Egorov wrote:
>>>>
>>>>> The RK818 chip is a power management IC for multimedia and handheld
>>>> "Power Management IC (PMIC)"
>>>>
>>>>> devices. It contains the following components:
>>>>>
>>>>> - Regulators
>>>>> - RTC
>>>>> - Clkout
>>>> Clocking
>>>>
>>>>> - battery support
>>>> Battery support
>>>>
>>>>> Both chips RK808 and RK818 are using a similar register map.
>>>> "Both RK808 ad RK818 chips"
>>>>
>>>>> So we can reuse the RTC and Clkout functionality.
>>>> Swap '.' for ','.
>>>>
>>>>> Signed-off-by: Wadim Egorov <w.egorov at phytec.de>
>>>>> ---
>>>>>    drivers/mfd/Kconfig       |   4 +-
>>>>>    drivers/mfd/rk808.c       | 231
>>>>> ++++++++++++++++++++++++++++++++++++++--------
>>>>>    include/linux/mfd/rk808.h | 162 ++++++++++++++++++++++++++++++--
>>>>>    3 files changed, 350 insertions(+), 47 deletions(-)
>>>>>
>>>>> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
>>>>> index 1bcf601..7ba464b 100644
>>>>> --- a/drivers/mfd/Kconfig
>>>>> +++ b/drivers/mfd/Kconfig
>>>>> @@ -839,13 +839,13 @@ config MFD_RC5T583
>>>>>          different functionality of the device.
>>>>>      config MFD_RK808
>>>>> -    tristate "Rockchip RK808 Power Management chip"
>>>>> +    tristate "Rockchip RK808/RK818 Power Management chip"
>>>> "Chip"
>>>>
>>>>>        depends on I2C && OF
>>>>>        select MFD_CORE
>>>>>        select REGMAP_I2C
>>>>>        select REGMAP_IRQ
>>>>>        help
>>>>> -      If you say yes here you get support for the RK808
>>>>> +      If you say yes here you get support for the RK808 and RK818
>>>>>          Power Management chips.
>>>>>          This driver provides common support for accessing the device
>>>>>          through I2C interface. The device supports multiple
>>>>> sub-devices
>>>>> diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c
>>>>> index 49d7f62..3cf9724 100644
>>>>> --- a/drivers/mfd/rk808.c
>>>>> +++ b/drivers/mfd/rk808.c
>>>>> @@ -1,11 +1,15 @@
>>>>>    /*
>>>>> - * MFD core driver for Rockchip RK808
>>>>> + * MFD core driver for Rockchip RK808/RK818
>>>>>     *
>>>>>     * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
>>>>>     *
>>>>>     * Author: Chris Zhong <zyw at rock-chips.com>
>>>>>     * Author: Zhang Qing <zhangqing at rock-chips.com>
>>>>>     *
>>>>> + * Copyright (C) 2016 PHYTEC Messtechnik GmbH
>>>>> + *
>>>>> + * Author: Wadim Egorov <w.egorov at phytec.de>
>>>>> + *
>>>>>     * This program is free software; you can redistribute it and/or
>>>>> modify it
>>>>>     * under the terms and conditions of the GNU General Public License,
>>>>>     * version 2, as published by the Free Software Foundation.
>>>>> @@ -22,12 +26,7 @@
>>>>>    #include <linux/mfd/core.h>
>>>>>    #include <linux/module.h>
>>>>>    #include <linux/regmap.h>
>>>>> -
>>>>> -struct rk808_reg_data {
>>>>> -    int addr;
>>>>> -    int mask;
>>>>> -    int value;
>>>>> -};
>>>> Why are you moving this to the header?
>>> It is now part of the rk808 struct.
>>>
>>>>> +#include <linux/of_device.h>
>>>>>      static bool rk808_is_volatile_reg(struct device *dev, unsigned
>>>>> int reg)
>>>>>    {
>>>>> @@ -57,6 +56,14 @@ static bool rk808_is_volatile_reg(struct device
>>>>> *dev, unsigned int reg)
>>>>>        return false;
>>>>>    }
>>>>>    +static const struct regmap_config rk818_regmap_config = {
>>>>> +    .reg_bits = 8,
>>>>> +    .val_bits = 8,
>>>>> +    .max_register = RK818_USB_CTRL_REG,
>>>>> +    .cache_type = REGCACHE_RBTREE,
>>>>> +    .volatile_reg = rk808_is_volatile_reg,
>>>>> +};
>>>>> +
>>>>>    static const struct regmap_config rk808_regmap_config = {
>>>>>        .reg_bits = 8,
>>>>>        .val_bits = 8,
>>>>> @@ -83,7 +90,17 @@ static const struct mfd_cell rk808s[] = {
>>>>>        },
>>>>>    };
>>>>>    -static const struct rk808_reg_data pre_init_reg[] = {
>>>>> +static const struct mfd_cell rk818s[] = {
>>>>> +    { .name = "rk808-clkout", },
>>>> How does this differ to a normal -clock driver?
>>> I don't know. It is a normal clock driver.
>>>
>>>>> +    { .name = "rk808-regulator", },
>>>>> +    {
>>>>> +        .name = "rk808-rtc",
>>>>> +        .num_resources = ARRAY_SIZE(rtc_resources),
>>>>> +        .resources = &rtc_resources[0],
>>>> .resources = rtc_resources,  ?
>>>>
>>>>> +    },
>>>>> +};
>>>>> +
>>>>> +static const struct rk8xx_reg_data rk808_pre_init_reg[] = {
>>>>>        { RK808_BUCK3_CONFIG_REG, BUCK_ILMIN_MASK,  BUCK_ILMIN_150MA },
>>>>>        { RK808_BUCK4_CONFIG_REG, BUCK_ILMIN_MASK,  BUCK_ILMIN_200MA },
>>>>>        { RK808_BOOST_CONFIG_REG, BOOST_ILMIN_MASK, BOOST_ILMIN_100MA },
>>>>> @@ -94,6 +111,22 @@ static const struct rk808_reg_data
>>>>> pre_init_reg[] = {
>>>>>                                VB_LO_SEL_3500MV },
>>>>>    };
>>>>>    +static const struct rk8xx_reg_data rk818_pre_init_reg[] = {
>>>>> +    { RK818_USB_CTRL_REG,    RK818_USB_ILIM_SEL_MASK,
>>>>> +                        RK818_USB_ILMIN_2000MA },
>>>>> +    /* close charger when usb lower then 3.4V */
>>>>> +    { RK818_USB_CTRL_REG,    RK818_USB_CHG_SD_VSEL_MASK, (0x7 <<
>>>>> 4) },
>>>>> +    /* no action when vref */
>>>>> +    { RK818_H5V_EN_REG,    BIT(1),        RK818_REF_RDY_CTRL },
>>>>> +    /* enable HDMI 5V */
>>>>> +    { RK818_H5V_EN_REG,    BIT(0),        RK818_H5V_EN },
>>>>> +    /* improve efficiency */
>>>>> +    { RK818_BUCK2_CONFIG_REG, BUCK2_RATE_MASK,    BUCK_ILMIN_250MA },
>>>>> +    { RK818_BUCK4_CONFIG_REG, BUCK_ILMIN_MASK,    BUCK_ILMIN_250MA },
>>>>> +    { RK818_BOOST_CONFIG_REG, BOOST_ILMIN_MASK,
>>>>> BOOST_ILMIN_100MA },
>>>>> +    { RK808_VB_MON_REG,    MASK_ALL,    VB_LO_ACT |
>>>>> VB_LO_SEL_3500MV },
>>>>> +};
>>>> The alignment here looks odd.
>>> I will fix it in the next version.
>>>
>>>>>    static const struct regmap_irq rk808_irqs[] = {
>>>>>        /* INT_STS */
>>>>>        [RK808_IRQ_VOUT_LO] = {
>>>>> @@ -136,6 +169,76 @@ static const struct regmap_irq rk808_irqs[] = {
>>>>>        },
>>>>>    };
>>>>>    +static const struct regmap_irq rk818_irqs[] = {
>>>>> +    /* INT_STS */
>>>>> +    [RK818_IRQ_VOUT_LO] = {
>>>>> +        .mask = RK818_IRQ_VOUT_LO_MSK,
>>>>> +        .reg_offset = 0,
>>>>> +    },
>>>>> +    [RK818_IRQ_VB_LO] = {
>>>>> +        .mask = RK818_IRQ_VB_LO_MSK,
>>>>> +        .reg_offset = 0,
>>>>> +    },
>>>>> +    [RK818_IRQ_PWRON] = {
>>>>> +        .mask = RK818_IRQ_PWRON_MSK,
>>>>> +        .reg_offset = 0,
>>>>> +    },
>>>>> +    [RK818_IRQ_PWRON_LP] = {
>>>>> +        .mask = RK818_IRQ_PWRON_LP_MSK,
>>>>> +        .reg_offset = 0,
>>>>> +    },
>>>>> +    [RK818_IRQ_HOTDIE] = {
>>>>> +        .mask = RK818_IRQ_HOTDIE_MSK,
>>>>> +        .reg_offset = 0,
>>>>> +    },
>>>>> +    [RK818_IRQ_RTC_ALARM] = {
>>>>> +        .mask = RK818_IRQ_RTC_ALARM_MSK,
>>>>> +        .reg_offset = 0,
>>>>> +    },
>>>>> +    [RK818_IRQ_RTC_PERIOD] = {
>>>>> +        .mask = RK818_IRQ_RTC_PERIOD_MSK,
>>>>> +        .reg_offset = 0,
>>>>> +    },
>>>>> +    [RK818_IRQ_USB_OV] = {
>>>>> +        .mask = RK818_IRQ_USB_OV_MSK,
>>>>> +        .reg_offset = 0,
>>>>> +    },
>>>>> +
>>>>> +    /* INT_STS2 */
>>>>> +    [RK818_IRQ_PLUG_IN] = {
>>>>> +        .mask = RK818_IRQ_PLUG_IN_MSK,
>>>>> +        .reg_offset = 1,
>>>>> +    },
>>>>> +    [RK818_IRQ_PLUG_OUT] = {
>>>>> +        .mask = RK818_IRQ_PLUG_OUT_MSK,
>>>>> +        .reg_offset = 1,
>>>>> +    },
>>>>> +    [RK818_IRQ_CHG_OK] = {
>>>>> +        .mask = RK818_IRQ_CHG_OK_MSK,
>>>>> +        .reg_offset = 1,
>>>>> +    },
>>>>> +    [RK818_IRQ_CHG_TE] = {
>>>>> +        .mask = RK818_IRQ_CHG_TE_MSK,
>>>>> +        .reg_offset = 1,
>>>>> +    },
>>>>> +    [RK818_IRQ_CHG_TS1] = {
>>>>> +        .mask = RK818_IRQ_CHG_TS1_MSK,
>>>>> +        .reg_offset = 1,
>>>>> +    },
>>>>> +    [RK818_IRQ_TS2] = {
>>>>> +        .mask = RK818_IRQ_TS2_MSK,
>>>>> +        .reg_offset = 1,
>>>>> +    },
>>>>> +    [RK818_IRQ_CHG_CVTLIM] = {
>>>>> +        .mask = RK818_IRQ_CHG_CVTLIM_MSK,
>>>>> +        .reg_offset = 1,
>>>>> +    },
>>>>> +    [RK818_IRQ_DISCHG_ILIM] = {
>>>>> +        .mask = RK818_IRQ_DISCHG_ILIM_MSK,
>>>>> +        .reg_offset = 1,
>>>>> +    },
>>>>> +};
>>>>> +
>>>>>    static struct regmap_irq_chip rk808_irq_chip = {
>>>>>        .name = "rk808",
>>>>>        .irqs = rk808_irqs,
>>>>> @@ -148,6 +251,18 @@ static struct regmap_irq_chip rk808_irq_chip = {
>>>>>        .init_ack_masked = true,
>>>>>    };
>>>>>    +static struct regmap_irq_chip rk818_irq_chip = {
>>>>> +    .name = "rk818",
>>>>> +    .irqs = rk818_irqs,
>>>>> +    .num_irqs = ARRAY_SIZE(rk818_irqs),
>>>>> +    .num_regs = 2,
>>>>> +    .irq_reg_stride = 2,
>>>>> +    .status_base = RK818_INT_STS_REG1,
>>>>> +    .mask_base = RK818_INT_STS_MSK_REG1,
>>>>> +    .ack_base = RK818_INT_STS_REG1,
>>>>> +    .init_ack_masked = true,
>>>>> +};
>>>>> +
>>>>>    static struct i2c_client *rk808_i2c_client;
>>>>>    static void rk808_device_shutdown(void)
>>>>>    {
>>>>> @@ -167,6 +282,48 @@ static void rk808_device_shutdown(void)
>>>>>            dev_err(&rk808_i2c_client->dev, "power off error!\n");
>>>>>    }
>>>>>    +static const struct of_device_id rk808_of_match[] = {
>>>>> +    { .compatible = "rockchip,rk808", .data = (void *) RK808_ID },
>>>>> +    { .compatible = "rockchip,rk818", .data = (void *) RK818_ID },
>>>>> +    { },
>>>>> +};
>>>>> +MODULE_DEVICE_TABLE(of, rk808_of_match);
>>>>> +
>>>>> +static int rk8xx_match_device(struct rk808 *rk808, struct device
>>>>> *dev)
>>>> If you're going to do it this way, please switch these parameters.
>>>>
>>>> It's more common to return the pointer however.
>>> If it's more common I will return a pointer to rk808.
>>>
>>>>> +{
>>>>> +    const struct of_device_id *of_id;
>>>>> +
>>>>> +    of_id = of_match_device(rk808_of_match, dev);
>>>>> +    if (!of_id) {
>>>>> +        dev_err(dev, "Unable to match OF ID\n");
>>>>> +        return -ENODEV;
>>>>> +    }
>>>>> +    rk808->variant = (long) of_id->data;
>>>>> +
>>>>> +    switch (rk808->variant) {
>>>>> +    case RK808_ID:
>>>>> +        rk808->nr_cells = ARRAY_SIZE(rk808s);
>>>>> +        rk808->cells = rk808s;
>>>>> +        rk808->regmap_cfg = &rk808_regmap_config;
>>>>> +        rk808->regmap_irq_chip = &rk808_irq_chip;
>>>>> +        rk808->pre_init_reg = rk808_pre_init_reg;
>>>>> +        rk808->nr_pre_init_regs = ARRAY_SIZE(rk808_pre_init_reg);
>>>>> +        break;
>>>>> +    case RK818_ID:
>>>>> +        rk808->nr_cells = ARRAY_SIZE(rk818s);
>>>>> +        rk808->cells = rk818s;
>>>>> +        rk808->regmap_cfg = &rk818_regmap_config;
>>>>> +        rk808->regmap_irq_chip = &rk818_irq_chip;
>>>>> +        rk808->pre_init_reg = rk818_pre_init_reg;
>>>>> +        rk808->nr_pre_init_regs = ARRAY_SIZE(rk818_pre_init_reg);
>>>>> +        break;
>>>>> +    default:
>>>>> +        dev_err(dev, "unsupported RK8XX ID %lu\n", rk808->variant);
>>>>> +        return -EINVAL;
>>>>> +    }
>>>>> +
>>>>> +    return 0;
>>>>> +}
>>>>>    static int rk808_probe(struct i2c_client *client,
>>>>>                   const struct i2c_device_id *id)
>>>>>    {
>>>>> @@ -176,46 +333,52 @@ static int rk808_probe(struct i2c_client
>>>>> *client,
>>>>>        int ret;
>>>>>        int i;
>>>>>    -    if (!client->irq) {
>>>>> -        dev_err(&client->dev, "No interrupt support, no core IRQ\n");
>>>>> -        return -EINVAL;
>>>>> -    }
>>>>> -
>>>>>        rk808 = devm_kzalloc(&client->dev, sizeof(*rk808), GFP_KERNEL);
>>>>>        if (!rk808)
>>>>>            return -ENOMEM;
>>>>>    -    rk808->regmap = devm_regmap_init_i2c(client,
>>>>> &rk808_regmap_config);
>>>>> +    ret = rk8xx_match_device(rk808, &client->dev);
>>>> Is there a way to dynamically probe the device?  No device ID you can
>>>> read directly from the silicon?
>>> AFAIK there is no device ID register. At least it is not documented in
>>> the manual.
>> register 0x17 and 0x18 is the ID register, 0x17 is the MSB, 0x18 is LSB
>>
>> for RK818, register 0x17 is 0x81, 0x18 is 0x81
> thank you for sharing this information. I have no RK808 PMIC device
> here, so I would also need the IDs for RK808.

    I have checked with the RK808 IC designer, the values of register 
0x17 and 0x18 are both 0x00.
>
> Lee, you have already applied this series. But I can't find the patches
> in your kernel tree.
> I would like to read the device ID from the register in the probe function.
> Do you want me to base my changes on top of this series or send a new
> version?
>
>
>




From will.deacon at arm.com  Wed Jul  6 03:39:52 2016
From: will.deacon at arm.com (Will Deacon)
Date: Wed, 6 Jul 2016 11:39:52 +0100
Subject: [PATCH] drivers/perf: arm-pmu: Handle per-interrupt affinity mask
In-Reply-To: <1467379291-18413-1-git-send-email-marc.zyngier@arm.com>
References: <1467379291-18413-1-git-send-email-marc.zyngier@arm.com>
Message-ID: <20160706103951.GC17925@arm.com>

On Fri, Jul 01, 2016 at 02:21:31PM +0100, Marc Zyngier wrote:
> On a big-little system, PMUs can be wired to CPUs using per CPU
> interrups (PPI). In this case, it is important to make sure that
> the enable/disable do happen on the right set of CPUs.
> 
> So instead of relying on the interrupt-affinity property, we can
> use the actual percpu affinity that DT exposes as part of the
> interrupt specifier. The DT binding is also updated to reflect
> the fact that the interrupt-affinity property shouldn't be used
> in that case.

[...]

> -	/* If we didn't manage to parse anything, claim to support all CPUs */
> -	if (cpumask_weight(&pmu->supported_cpus) == 0)
> -		cpumask_setall(&pmu->supported_cpus);
> +	/* If we didn't manage to parse anything, try the interrupt affinity */
> +	if (cpumask_weight(&pmu->supported_cpus) == 0) {
> +		if (!using_spi) {
> +			/* If using PPIs, check the affinity of the partition */
> +			int irq = platform_get_irq(pdev, 0);
> +			irq_get_percpu_devid_partition(irq,
> +						       &pmu->supported_cpus);

Should we not at least propagate the failure if this returns -EINVAL?

Will


From roger.chen at rock-chips.com  Wed Jul  6 03:51:28 2016
From: roger.chen at rock-chips.com (Roger Chen)
Date: Wed,  6 Jul 2016 18:51:28 +0800
Subject: [PATCH 0/3] *** support GMAC for RK3399 & RK3366 ***
Message-ID: <1467802291-6487-1-git-send-email-roger.chen@rock-chips.com>

This series adds registers description for RK3366 & RK3399 GMAC.
And also DT nodes for RK3399 SoC and EVB.

Roger Chen (3):
  net: stmmac: dwmac-rk: add rk3366 & rk3399-specific data
  arm64: dts: rockchip: add GMAC dt nodes for rk3399 SoC
  arm64: dts: rockchip: add GMAC dt nodes for RK3399 evb

 .../devicetree/bindings/net/rockchip-dwmac.txt     |   3 +-
 arch/arm64/boot/dts/rockchip/rk3399-evb.dts        |  23 +++
 arch/arm64/boot/dts/rockchip/rk3399.dtsi           |  79 +++++++
 drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c     | 226 +++++++++++++++++++++
 4 files changed, 330 insertions(+), 1 deletion(-)

-- 
1.9.1




From roger.chen at rock-chips.com  Wed Jul  6 03:51:29 2016
From: roger.chen at rock-chips.com (Roger Chen)
Date: Wed,  6 Jul 2016 18:51:29 +0800
Subject: [PATCH 1/3] net: stmmac: dwmac-rk: add rk3366 & rk3399-specific data
In-Reply-To: <1467802291-6487-1-git-send-email-roger.chen@rock-chips.com>
References: <1467802291-6487-1-git-send-email-roger.chen@rock-chips.com>
Message-ID: <1467802291-6487-2-git-send-email-roger.chen@rock-chips.com>

Add constants and callback functions for the dwmac on rk3368 socs.
As can be seen, the base structure is the same, only registers and
the bits in them moved slightly.

Signed-off-by: Roger Chen <roger.chen at rock-chips.com>
---
 .../devicetree/bindings/net/rockchip-dwmac.txt     |   3 +-
 drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c     | 226 +++++++++++++++++++++
 2 files changed, 228 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/net/rockchip-dwmac.txt b/Documentation/devicetree/bindings/net/rockchip-dwmac.txt
index 93eac7c..8c066e6 100644
--- a/Documentation/devicetree/bindings/net/rockchip-dwmac.txt
+++ b/Documentation/devicetree/bindings/net/rockchip-dwmac.txt
@@ -3,7 +3,8 @@ Rockchip SoC RK3288 10/100/1000 Ethernet driver(GMAC)
 The device node has following properties.
 
 Required properties:
- - compatible: Can be one of "rockchip,rk3288-gmac", "rockchip,rk3368-gmac"
+ - compatible: Can be one of "rockchip,rk3288-gmac", "rockchip,rk3366-gmac",
+                             "rockchip,rk3368-gmac", "rockchip,rk3399-gmac"
  - reg: addresses and length of the register sets for the device.
  - interrupts: Should contain the GMAC interrupts.
  - interrupt-names: Should contain the interrupt names "macirq".
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
index 0cd3ecf..35145b2 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
@@ -184,6 +184,118 @@ static const struct rk_gmac_ops rk3288_ops = {
 	.set_rmii_speed = rk3288_set_rmii_speed,
 };
 
+#define RK3366_GRF_SOC_CON6	0x0418
+#define RK3366_GRF_SOC_CON7	0x041c
+
+/* RK3366_GRF_SOC_CON6 */
+#define RK3366_GMAC_PHY_INTF_SEL_RGMII	(GRF_BIT(9) | GRF_CLR_BIT(10) | \
+					 GRF_CLR_BIT(11))
+#define RK3366_GMAC_PHY_INTF_SEL_RMII	(GRF_CLR_BIT(9) | GRF_CLR_BIT(10) | \
+					 GRF_BIT(11))
+#define RK3366_GMAC_FLOW_CTRL		GRF_BIT(8)
+#define RK3366_GMAC_FLOW_CTRL_CLR	GRF_CLR_BIT(8)
+#define RK3366_GMAC_SPEED_10M		GRF_CLR_BIT(7)
+#define RK3366_GMAC_SPEED_100M		GRF_BIT(7)
+#define RK3366_GMAC_RMII_CLK_25M	GRF_BIT(3)
+#define RK3366_GMAC_RMII_CLK_2_5M	GRF_CLR_BIT(3)
+#define RK3366_GMAC_CLK_125M		(GRF_CLR_BIT(4) | GRF_CLR_BIT(5))
+#define RK3366_GMAC_CLK_25M		(GRF_BIT(4) | GRF_BIT(5))
+#define RK3366_GMAC_CLK_2_5M		(GRF_CLR_BIT(4) | GRF_BIT(5))
+#define RK3366_GMAC_RMII_MODE		GRF_BIT(6)
+#define RK3366_GMAC_RMII_MODE_CLR	GRF_CLR_BIT(6)
+
+/* RK3366_GRF_SOC_CON7 */
+#define RK3366_GMAC_TXCLK_DLY_ENABLE	GRF_BIT(7)
+#define RK3366_GMAC_TXCLK_DLY_DISABLE	GRF_CLR_BIT(7)
+#define RK3366_GMAC_RXCLK_DLY_ENABLE	GRF_BIT(15)
+#define RK3366_GMAC_RXCLK_DLY_DISABLE	GRF_CLR_BIT(15)
+#define RK3366_GMAC_CLK_RX_DL_CFG(val)	HIWORD_UPDATE(val, 0x7F, 8)
+#define RK3366_GMAC_CLK_TX_DL_CFG(val)	HIWORD_UPDATE(val, 0x7F, 0)
+
+static void rk3366_set_to_rgmii(struct rk_priv_data *bsp_priv,
+				int tx_delay, int rx_delay)
+{
+	struct device *dev = &bsp_priv->pdev->dev;
+
+	if (IS_ERR(bsp_priv->grf)) {
+		dev_err(dev, "%s: Missing rockchip,grf property\n", __func__);
+		return;
+	}
+
+	regmap_write(bsp_priv->grf, RK3366_GRF_SOC_CON6,
+		     RK3366_GMAC_PHY_INTF_SEL_RGMII |
+		     RK3366_GMAC_RMII_MODE_CLR);
+	regmap_write(bsp_priv->grf, RK3366_GRF_SOC_CON7,
+		     RK3366_GMAC_RXCLK_DLY_ENABLE |
+		     RK3366_GMAC_TXCLK_DLY_ENABLE |
+		     RK3366_GMAC_CLK_RX_DL_CFG(rx_delay) |
+		     RK3366_GMAC_CLK_TX_DL_CFG(tx_delay));
+}
+
+static void rk3366_set_to_rmii(struct rk_priv_data *bsp_priv)
+{
+	struct device *dev = &bsp_priv->pdev->dev;
+
+	if (IS_ERR(bsp_priv->grf)) {
+		dev_err(dev, "%s: Missing rockchip,grf property\n", __func__);
+		return;
+	}
+
+	regmap_write(bsp_priv->grf, RK3366_GRF_SOC_CON6,
+		     RK3366_GMAC_PHY_INTF_SEL_RMII | RK3366_GMAC_RMII_MODE);
+}
+
+static void rk3366_set_rgmii_speed(struct rk_priv_data *bsp_priv, int speed)
+{
+	struct device *dev = &bsp_priv->pdev->dev;
+
+	if (IS_ERR(bsp_priv->grf)) {
+		dev_err(dev, "%s: Missing rockchip,grf property\n", __func__);
+		return;
+	}
+
+	if (speed == 10)
+		regmap_write(bsp_priv->grf, RK3366_GRF_SOC_CON6,
+			     RK3366_GMAC_CLK_2_5M);
+	else if (speed == 100)
+		regmap_write(bsp_priv->grf, RK3366_GRF_SOC_CON6,
+			     RK3366_GMAC_CLK_25M);
+	else if (speed == 1000)
+		regmap_write(bsp_priv->grf, RK3366_GRF_SOC_CON6,
+			     RK3366_GMAC_CLK_125M);
+	else
+		dev_err(dev, "unknown speed value for RGMII! speed=%d", speed);
+}
+
+static void rk3366_set_rmii_speed(struct rk_priv_data *bsp_priv, int speed)
+{
+	struct device *dev = &bsp_priv->pdev->dev;
+
+	if (IS_ERR(bsp_priv->grf)) {
+		dev_err(dev, "%s: Missing rockchip,grf property\n", __func__);
+		return;
+	}
+
+	if (speed == 10) {
+		regmap_write(bsp_priv->grf, RK3366_GRF_SOC_CON6,
+			     RK3366_GMAC_RMII_CLK_2_5M |
+			     RK3366_GMAC_SPEED_10M);
+	} else if (speed == 100) {
+		regmap_write(bsp_priv->grf, RK3366_GRF_SOC_CON6,
+			     RK3366_GMAC_RMII_CLK_25M |
+			     RK3366_GMAC_SPEED_100M);
+	} else {
+		dev_err(dev, "unknown speed value for RMII! speed=%d", speed);
+	}
+}
+
+static const struct rk_gmac_ops rk3366_ops = {
+	.set_to_rgmii = rk3366_set_to_rgmii,
+	.set_to_rmii = rk3366_set_to_rmii,
+	.set_rgmii_speed = rk3366_set_rgmii_speed,
+	.set_rmii_speed = rk3366_set_rmii_speed,
+};
+
 #define RK3368_GRF_SOC_CON15	0x043c
 #define RK3368_GRF_SOC_CON16	0x0440
 
@@ -296,6 +408,118 @@ static const struct rk_gmac_ops rk3368_ops = {
 	.set_rmii_speed = rk3368_set_rmii_speed,
 };
 
+#define RK3399_GRF_SOC_CON5	0xc214
+#define RK3399_GRF_SOC_CON6	0xc218
+
+/* RK3399_GRF_SOC_CON5 */
+#define RK3399_GMAC_PHY_INTF_SEL_RGMII	(GRF_BIT(9) | GRF_CLR_BIT(10) | \
+					 GRF_CLR_BIT(11))
+#define RK3399_GMAC_PHY_INTF_SEL_RMII	(GRF_CLR_BIT(9) | GRF_CLR_BIT(10) | \
+					 GRF_BIT(11))
+#define RK3399_GMAC_FLOW_CTRL		GRF_BIT(8)
+#define RK3399_GMAC_FLOW_CTRL_CLR	GRF_CLR_BIT(8)
+#define RK3399_GMAC_SPEED_10M		GRF_CLR_BIT(7)
+#define RK3399_GMAC_SPEED_100M		GRF_BIT(7)
+#define RK3399_GMAC_RMII_CLK_25M	GRF_BIT(3)
+#define RK3399_GMAC_RMII_CLK_2_5M	GRF_CLR_BIT(3)
+#define RK3399_GMAC_CLK_125M		(GRF_CLR_BIT(4) | GRF_CLR_BIT(5))
+#define RK3399_GMAC_CLK_25M		(GRF_BIT(4) | GRF_BIT(5))
+#define RK3399_GMAC_CLK_2_5M		(GRF_CLR_BIT(4) | GRF_BIT(5))
+#define RK3399_GMAC_RMII_MODE		GRF_BIT(6)
+#define RK3399_GMAC_RMII_MODE_CLR	GRF_CLR_BIT(6)
+
+/* RK3399_GRF_SOC_CON6 */
+#define RK3399_GMAC_TXCLK_DLY_ENABLE	GRF_BIT(7)
+#define RK3399_GMAC_TXCLK_DLY_DISABLE	GRF_CLR_BIT(7)
+#define RK3399_GMAC_RXCLK_DLY_ENABLE	GRF_BIT(15)
+#define RK3399_GMAC_RXCLK_DLY_DISABLE	GRF_CLR_BIT(15)
+#define RK3399_GMAC_CLK_RX_DL_CFG(val)	HIWORD_UPDATE(val, 0x7F, 8)
+#define RK3399_GMAC_CLK_TX_DL_CFG(val)	HIWORD_UPDATE(val, 0x7F, 0)
+
+static void rk3399_set_to_rgmii(struct rk_priv_data *bsp_priv,
+				int tx_delay, int rx_delay)
+{
+	struct device *dev = &bsp_priv->pdev->dev;
+
+	if (IS_ERR(bsp_priv->grf)) {
+		dev_err(dev, "%s: Missing rockchip,grf property\n", __func__);
+		return;
+	}
+
+	regmap_write(bsp_priv->grf, RK3399_GRF_SOC_CON5,
+		     RK3399_GMAC_PHY_INTF_SEL_RGMII |
+		     RK3399_GMAC_RMII_MODE_CLR);
+	regmap_write(bsp_priv->grf, RK3399_GRF_SOC_CON6,
+		     RK3399_GMAC_RXCLK_DLY_ENABLE |
+		     RK3399_GMAC_TXCLK_DLY_ENABLE |
+		     RK3399_GMAC_CLK_RX_DL_CFG(rx_delay) |
+		     RK3399_GMAC_CLK_TX_DL_CFG(tx_delay));
+}
+
+static void rk3399_set_to_rmii(struct rk_priv_data *bsp_priv)
+{
+	struct device *dev = &bsp_priv->pdev->dev;
+
+	if (IS_ERR(bsp_priv->grf)) {
+		dev_err(dev, "%s: Missing rockchip,grf property\n", __func__);
+		return;
+	}
+
+	regmap_write(bsp_priv->grf, RK3399_GRF_SOC_CON5,
+		     RK3399_GMAC_PHY_INTF_SEL_RMII | RK3399_GMAC_RMII_MODE);
+}
+
+static void rk3399_set_rgmii_speed(struct rk_priv_data *bsp_priv, int speed)
+{
+	struct device *dev = &bsp_priv->pdev->dev;
+
+	if (IS_ERR(bsp_priv->grf)) {
+		dev_err(dev, "%s: Missing rockchip,grf property\n", __func__);
+		return;
+	}
+
+	if (speed == 10)
+		regmap_write(bsp_priv->grf, RK3399_GRF_SOC_CON5,
+			     RK3399_GMAC_CLK_2_5M);
+	else if (speed == 100)
+		regmap_write(bsp_priv->grf, RK3399_GRF_SOC_CON5,
+			     RK3399_GMAC_CLK_25M);
+	else if (speed == 1000)
+		regmap_write(bsp_priv->grf, RK3399_GRF_SOC_CON5,
+			     RK3399_GMAC_CLK_125M);
+	else
+		dev_err(dev, "unknown speed value for RGMII! speed=%d", speed);
+}
+
+static void rk3399_set_rmii_speed(struct rk_priv_data *bsp_priv, int speed)
+{
+	struct device *dev = &bsp_priv->pdev->dev;
+
+	if (IS_ERR(bsp_priv->grf)) {
+		dev_err(dev, "%s: Missing rockchip,grf property\n", __func__);
+		return;
+	}
+
+	if (speed == 10) {
+		regmap_write(bsp_priv->grf, RK3399_GRF_SOC_CON5,
+			     RK3399_GMAC_RMII_CLK_2_5M |
+			     RK3399_GMAC_SPEED_10M);
+	} else if (speed == 100) {
+		regmap_write(bsp_priv->grf, RK3399_GRF_SOC_CON5,
+			     RK3399_GMAC_RMII_CLK_25M |
+			     RK3399_GMAC_SPEED_100M);
+	} else {
+		dev_err(dev, "unknown speed value for RMII! speed=%d", speed);
+	}
+}
+
+static const struct rk_gmac_ops rk3399_ops = {
+	.set_to_rgmii = rk3399_set_to_rgmii,
+	.set_to_rmii = rk3399_set_to_rmii,
+	.set_rgmii_speed = rk3399_set_rgmii_speed,
+	.set_rmii_speed = rk3399_set_rmii_speed,
+};
+
 static int gmac_clk_init(struct rk_priv_data *bsp_priv)
 {
 	struct device *dev = &bsp_priv->pdev->dev;
@@ -605,7 +829,9 @@ static int rk_gmac_probe(struct platform_device *pdev)
 
 static const struct of_device_id rk_gmac_dwmac_match[] = {
 	{ .compatible = "rockchip,rk3288-gmac", .data = &rk3288_ops },
+	{ .compatible = "rockchip,rk3366-gmac", .data = &rk3366_ops },
 	{ .compatible = "rockchip,rk3368-gmac", .data = &rk3368_ops },
+	{ .compatible = "rockchip,rk3399-gmac", .data = &rk3399_ops },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, rk_gmac_dwmac_match);
-- 
1.9.1




From roger.chen at rock-chips.com  Wed Jul  6 03:51:30 2016
From: roger.chen at rock-chips.com (Roger Chen)
Date: Wed,  6 Jul 2016 18:51:30 +0800
Subject: [PATCH 2/3] arm64: dts: rockchip: add GMAC dt nodes for rk3399 SoC
In-Reply-To: <1467802291-6487-1-git-send-email-roger.chen@rock-chips.com>
References: <1467802291-6487-1-git-send-email-roger.chen@rock-chips.com>
Message-ID: <1467802291-6487-3-git-send-email-roger.chen@rock-chips.com>

This patch adds ethernet GMAC dt notes for Rockchip RK3399 SoC.

Signed-off-by: Roger Chen <roger.chen at rock-chips.com>
---
 arch/arm64/boot/dts/rockchip/rk3399.dtsi | 79 ++++++++++++++++++++++++++++++++
 1 file changed, 79 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index d7f8e06..cb0b17d 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -189,6 +189,25 @@
 		};
 	};
 
+	gmac: eth at fe300000 {
+		compatible = "rockchip,rk3399-gmac";
+		reg = <0x0 0xfe300000 0x0 0x10000>;
+		rockchip,grf = <&grf>;
+		interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>;
+		interrupt-names = "macirq";
+		clocks = <&cru SCLK_MAC>, <&cru SCLK_MAC_RX>,
+			 <&cru SCLK_MAC_TX>, <&cru SCLK_MACREF>,
+			 <&cru SCLK_MACREF_OUT>, <&cru ACLK_GMAC>,
+			 <&cru PCLK_GMAC>;
+		clock-names = "stmmaceth", "mac_clk_rx",
+			      "mac_clk_tx", "clk_mac_ref",
+			      "clk_mac_refout", "aclk_mac",
+			      "pclk_mac";
+		resets = <&cru SRST_A_GMAC>;
+		reset-names = "stmmaceth";
+		status = "disabled";
+	};
+
 	sdio0: dwmmc at fe310000 {
 		compatible = "rockchip,rk3399-dw-mshc",
 			     "rockchip,rk3288-dw-mshc";
@@ -658,6 +677,66 @@
 			drive-strength = <13>;
 		};
 
+		gmac {
+			rgmii_pins: rgmii-pins {
+				rockchip,pins =
+					/* mac_txclk */
+					<3 17 RK_FUNC_1 &pcfg_pull_none_13ma>,
+					/* mac_rxclk */
+					<3 14 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_mdio */
+					<3 13 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_txen */
+					<3 12 RK_FUNC_1 &pcfg_pull_none_13ma>,
+					/* mac_clk */
+					<3 11 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_rxdv */
+					<3 9 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_mdc */
+					<3 8 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_rxd1 */
+					<3 7 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_rxd0 */
+					<3 6 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_txd1 */
+					<3 5 RK_FUNC_1 &pcfg_pull_none_13ma>,
+					/* mac_txd0 */
+					<3 4 RK_FUNC_1 &pcfg_pull_none_13ma>,
+					/* mac_rxd3 */
+					<3 3 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_rxd2 */
+					<3 2 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_txd3 */
+					<3 1 RK_FUNC_1 &pcfg_pull_none_13ma>,
+					/* mac_txd2 */
+					<3 0 RK_FUNC_1 &pcfg_pull_none_13ma>;
+			};
+
+			rmii_pins: rmii-pins {
+				rockchip,pins =
+					/* mac_mdio */
+					<3 13 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_txen */
+					<3 12 RK_FUNC_1 &pcfg_pull_none_13ma>,
+					/* mac_clk */
+					<3 11 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_rxer */
+					<3 10 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_rxdv */
+					<3 9 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_mdc */
+					<3 8 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_rxd1 */
+					<3 7 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_rxd0 */
+					<3 6 RK_FUNC_1 &pcfg_pull_none>,
+					/* mac_txd1 */
+					<3 5 RK_FUNC_1 &pcfg_pull_none_13ma>,
+					/* mac_txd0 */
+					<3 4 RK_FUNC_1 &pcfg_pull_none_13ma>;
+			};
+		};
+
 		i2c0 {
 			i2c0_xfer: i2c0-xfer {
 				rockchip,pins =
-- 
1.9.1




From roger.chen at rock-chips.com  Wed Jul  6 03:51:31 2016
From: roger.chen at rock-chips.com (Roger Chen)
Date: Wed,  6 Jul 2016 18:51:31 +0800
Subject: [PATCH 3/3] arm64: dts: rockchip: add GMAC dt nodes for RK3399 evb
In-Reply-To: <1467802291-6487-1-git-send-email-roger.chen@rock-chips.com>
References: <1467802291-6487-1-git-send-email-roger.chen@rock-chips.com>
Message-ID: <1467802291-6487-4-git-send-email-roger.chen@rock-chips.com>

This patch add ethernet GMAC dt nodes for RK3399 evaluation board.

Signed-off-by: Roger Chen <roger.chen at rock-chips.com>
---
 arch/arm64/boot/dts/rockchip/rk3399-evb.dts | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3399-evb.dts b/arch/arm64/boot/dts/rockchip/rk3399-evb.dts
index 1a3eb14..337c939 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-evb.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-evb.dts
@@ -69,6 +69,13 @@
 		regulator-max-microvolt = <3300000>;
 	};
 
+	clkin_gmac: external-gmac-clock {
+		compatible = "fixed-clock";
+		clock-frequency = <125000000>;
+		clock-output-names = "clkin_gmac";
+		#clock-cells = <0>;
+	};
+
 	vcc_phy: vcc-phy-regulator {
 		compatible = "regulator-fixed";
 		regulator-name = "vcc_phy";
@@ -122,3 +129,19 @@
 		};
 	};
 };
+
+&gmac {
+	phy-supply = <&vcc_phy>;
+	phy-mode = "rgmii";
+	clock_in_out = "input";
+	snps,reset-gpio = <&gpio3 15 GPIO_ACTIVE_LOW>;
+	snps,reset-active-low;
+	snps,reset-delays-us = <0 10000 50000>;
+	assigned-clocks = <&cru SCLK_RMII_SRC>;
+	assigned-clock-parents = <&clkin_gmac>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&rgmii_pins>;
+	tx_delay = <0x28>;
+	rx_delay = <0x11>;
+	status = "okay";
+};
-- 
1.9.1




From mark.rutland at arm.com  Wed Jul  6 04:24:44 2016
From: mark.rutland at arm.com (Mark Rutland)
Date: Wed, 6 Jul 2016 12:24:44 +0100
Subject: [PATCH v2 0/2] arm64: dts: rockchip: Support PMU for rk3399 SoCs
In-Reply-To: <1467792357-9749-1-git-send-email-wxt@rock-chips.com>
References: <1467792357-9749-1-git-send-email-wxt@rock-chips.com>
Message-ID: <20160706112444.GB14657@leverpostej>

On Wed, Jul 06, 2016 at 04:05:55PM +0800, Caesar Wang wrote:
> Hello Heiko, Marc & ARM guys

Hi,

These patches themselves look fine, so FWIW:

Acked-by: Mark Rutland <mark.rutland at arm.com>

The prerequisite patch from Marc [1] also largely looks fine, but there
are some minor details to sort out [2], so at this point in time I'm
guesssing this is all v4.9 material.

Thanks,
Mark.

[1] http://lists.infradead.org/pipermail/linux-arm-kernel/2016-July/440647.html
[2] http://lists.infradead.org/pipermail/linux-arm-kernel/2016-July/441495.html

> When Jay first submitted the rk3399.dtsi upstream
> <https://patchwork.kernel.org/patch/8885821/> he had the PMU node in there,
> but then took it out because the upstream binding wasn't done yet.
> It looks as if the upstream stuff has landed, since in linux/master I see:
> 287e9357abcc DT/arm,gic-v3: Documment PPI partition support
> e3825ba1af3a irqchip/gic-v3: Add support for partitioned PPIs
> 9e2c986cb460 irqchip: Add per-cpu interrupt partitioning library
> 222df54fd8b7 genirq: Allow the affinity of a percpu interrupt to be set/retrieved
> 651e8b54abde irqdomain: Allow domain matching on irq_fwspec
> 
> This series patches add to support the rk3399 SoCs PMU.
> I pick up the https://patchwork.kernel.org/patch/9209369/.
> 
> As do some tests with ChromeOs for my rk3399 board.
> Tested with linus master 4.7-rc6 kernel on rk3399 board.
> https://github.com/Caesar-github/rockchip/tree/rk3399/pmu-upstream
> 
> localhost / # perf list
> 
> List of pre-defined events (to be used in -e):
> cpu-cycles OR cycles [Hardware event]
> instructions [Hardware event]
> cache-references [Hardware event]
> cache-misses [Hardware event]
> branch-instructions OR branches [Hardware event]
> branch-misses [Hardware event]
> bus-cycles [Hardware event]
> ...
> 
> perf stat --cpu 0/1/2/3..... to minitor
> e.g. cpu0;
> 
> localhost / # perf stat --cpu 0
> 
> Performance counter stats for 'CPU(s) 0':
> 
> 3374.917571 task-clock (msec) # 1.001 CPUs utilized [100.00%]
> 20 context-switches # 0.006 K/sec [100.00%]
> 2 cpu-migrations # 0.001 K/sec [100.00%]
> 55 page-faults # 0.016 K/sec
> 7151843 cycles # 0.002 GHz [100.00%]
> <not supported> stalled-cycles-frontend
> <not supported> stalled-cycles-backend
> 4272536 instructions # 0.60 insns per cycle [100.00%]
> 568406 branches # 0.168 M/sec [100.00%]
> 65652 branch-misses # 11.55% of all branches
> 
> Also, 'perf top' to monitor the PMU interrupts from cpus
> 
> -Caesar
> 
> 
> Changes in v2:
> - AS Mark comments on https://patchwork.kernel.org/patch/9209369/
>   remove the interrupt-affinity property, we need depend on Marc' perf
>   code on https://patchwork.kernel.org/patch/9209369/.
> 
> Caesar Wang (2):
>   arm64: dts: rockchip: change all interrupts cells for 4 on rk3399 SoCs
>   arm64: dts: rockchip: support the pmu node for rk3399
> 
>  arch/arm64/boot/dts/rockchip/rk3399.dtsi | 118 ++++++++++++++++++-------------
>  1 file changed, 69 insertions(+), 49 deletions(-)
> 
> -- 
> 1.9.1
> 


From sergei.shtylyov at cogentembedded.com  Wed Jul  6 05:23:53 2016
From: sergei.shtylyov at cogentembedded.com (Sergei Shtylyov)
Date: Wed, 6 Jul 2016 15:23:53 +0300
Subject: [PATCH 2/3] arm64: dts: rockchip: add GMAC dt nodes for rk3399 SoC
In-Reply-To: <1467802291-6487-3-git-send-email-roger.chen@rock-chips.com>
References: <1467802291-6487-1-git-send-email-roger.chen@rock-chips.com>
 <1467802291-6487-3-git-send-email-roger.chen@rock-chips.com>
Message-ID: <f1e3270b-4908-2d81-9784-21c1cb77e2d9@cogentembedded.com>

Hello.

On 7/6/2016 1:51 PM, Roger Chen wrote:

> This patch adds ethernet GMAC dt notes for Rockchip RK3399 SoC.
>
> Signed-off-by: Roger Chen <roger.chen at rock-chips.com>
> ---
>  arch/arm64/boot/dts/rockchip/rk3399.dtsi | 79 ++++++++++++++++++++++++++++++++
>  1 file changed, 79 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> index d7f8e06..cb0b17d 100644
> --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> @@ -189,6 +189,25 @@
>  		};
>  	};
>
> +	gmac: eth at fe300000 {

    The node should be named "ethernet at fe300000" to comply with the ePAPR 
standard.

[...]

MBR, Sergei



From boris.brezillon at free-electrons.com  Wed Jul  6 05:27:19 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Wed, 6 Jul 2016 14:27:19 +0200
Subject: [PATCH] regulator: pwm: Fix regulator ramp delay for continuous
 mode
In-Reply-To: <1467089591-7631-1-git-send-email-dianders@chromium.org>
References: <1467089591-7631-1-git-send-email-dianders@chromium.org>
Message-ID: <20160706142719.73713123@bbrezillon>

On Mon, 27 Jun 2016 21:53:11 -0700
Douglas Anderson <dianders at chromium.org> wrote:

> The original commit adding support for continuous voltage mode didn't
> handle the regulator ramp delay properly.  It treated the delay as a
> fixed delay in uS despite the property being defined as uV / uS.  Let's
> adjust it.  Luckily there appear to be no users of this ramp delay for
> PWM regulators (as per grepping through device trees in linuxnext).
> 
> Note also that the upper bound of usleep_range probably shouldn't be a
> full 1 ms longer than the lower bound since I've seen plenty of hardware
> with a ramp rate of ~5000 uS / uV and for small jumps the total delays
> are in the tens of uS.  1000 is way too much.  We'll try to be dynamic
> and use 10%

I realize I may have introduced another bug when adding the
->enable()/disable() methods: we are only waiting for the ramp up delay
when changing the voltage, but it should probably be applied when
enabling the PWM, and conditionally applied when changing the voltage
only if the PWM is enabled.

> 
> Signed-off-by: Douglas Anderson <dianders at chromium.org>
> ---
> Note that this patch is atop Boris's recent PWM regulator fixes.  If
> desired it wouldn't be too hard to write it atop the old code, though
> quite honestly anyone using a PWM regulator should probably be using his
> new code.
> 
>  drivers/regulator/pwm-regulator.c | 9 +++++++--
>  1 file changed, 7 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/regulator/pwm-regulator.c b/drivers/regulator/pwm-regulator.c
> index fa1c74c77bb0..de94d19f6e1f 100644
> --- a/drivers/regulator/pwm-regulator.c
> +++ b/drivers/regulator/pwm-regulator.c
> @@ -188,6 +188,7 @@ static int pwm_regulator_set_voltage(struct regulator_dev *rdev,
>  	struct pwm_state pstate;
>  	unsigned int diff_duty;
>  	unsigned int dutycycle;
> +	int old_uV = pwm_regulator_get_voltage(rdev);
>  	int ret;
>  
>  	pwm_init_state(drvdata->pwm, &pstate);
> @@ -219,8 +220,12 @@ static int pwm_regulator_set_voltage(struct regulator_dev *rdev,
>  		return ret;
>  	}
>  
> -	/* Delay required by PWM regulator to settle to the new voltage */
> -	usleep_range(ramp_delay, ramp_delay + 1000);
> +	if (ramp_delay == 0)
> +		return 0;
> +
> +	/* Ramp delay is in uV/uS. Adjust to uS and delay */
> +	ramp_delay = DIV_ROUND_UP(abs(req_min_uV - old_uV), ramp_delay);
> +	usleep_range(ramp_delay, ramp_delay + DIV_ROUND_UP(ramp_delay, 10));
>  
>  	return 0;
>  }



From marc.zyngier at arm.com  Wed Jul  6 07:11:17 2016
From: marc.zyngier at arm.com (Marc Zyngier)
Date: Wed, 6 Jul 2016 15:11:17 +0100
Subject: [PATCH] drivers/perf: arm-pmu: Handle per-interrupt affinity mask
In-Reply-To: <20160706103951.GC17925@arm.com>
References: <1467379291-18413-1-git-send-email-marc.zyngier@arm.com>
 <20160706103951.GC17925@arm.com>
Message-ID: <577D1185.4090801@arm.com>

On 06/07/16 11:39, Will Deacon wrote:
> On Fri, Jul 01, 2016 at 02:21:31PM +0100, Marc Zyngier wrote:
>> On a big-little system, PMUs can be wired to CPUs using per CPU
>> interrups (PPI). In this case, it is important to make sure that
>> the enable/disable do happen on the right set of CPUs.
>>
>> So instead of relying on the interrupt-affinity property, we can
>> use the actual percpu affinity that DT exposes as part of the
>> interrupt specifier. The DT binding is also updated to reflect
>> the fact that the interrupt-affinity property shouldn't be used
>> in that case.
> 
> [...]
> 
>> -	/* If we didn't manage to parse anything, claim to support all CPUs */
>> -	if (cpumask_weight(&pmu->supported_cpus) == 0)
>> -		cpumask_setall(&pmu->supported_cpus);
>> +	/* If we didn't manage to parse anything, try the interrupt affinity */
>> +	if (cpumask_weight(&pmu->supported_cpus) == 0) {
>> +		if (!using_spi) {
>> +			/* If using PPIs, check the affinity of the partition */
>> +			int irq = platform_get_irq(pdev, 0);
>> +			irq_get_percpu_devid_partition(irq,
>> +						       &pmu->supported_cpus);
> 
> Should we not at least propagate the failure if this returns -EINVAL?

Good point. I'll fix that and resend it.

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...


From marc.zyngier at arm.com  Wed Jul  6 07:33:47 2016
From: marc.zyngier at arm.com (Marc Zyngier)
Date: Wed,  6 Jul 2016 15:33:47 +0100
Subject: [PATCH v2] drivers/perf: arm-pmu: Handle per-interrupt affinity mask
Message-ID: <1467815627-4061-1-git-send-email-marc.zyngier@arm.com>

On a big-little system, PMUs can be wired to CPUs using per CPU
interrups (PPI). In this case, it is important to make sure that
the enable/disable do happen on the right set of CPUs.

So instead of relying on the interrupt-affinity property, we can
use the actual percpu affinity that DT exposes as part of the
interrupt specifier. The DT binding is also updated to reflect
the fact that the interrupt-affinity property shouldn't be used
in that case.

Signed-off-by: Marc Zyngier <marc.zyngier at arm.com>
---
* From v1:
  - propagate the error if irq_get_percpu_devid_partition fails

 Documentation/devicetree/bindings/arm/pmu.txt |  4 +++-
 drivers/perf/arm_pmu.c                        | 27 ++++++++++++++++++++++-----
 2 files changed, 25 insertions(+), 6 deletions(-)

diff --git a/Documentation/devicetree/bindings/arm/pmu.txt b/Documentation/devicetree/bindings/arm/pmu.txt
index 74d5417..61c8b46 100644
--- a/Documentation/devicetree/bindings/arm/pmu.txt
+++ b/Documentation/devicetree/bindings/arm/pmu.txt
@@ -39,7 +39,9 @@ Optional properties:
                        When using a PPI, specifies a list of phandles to CPU
 		       nodes corresponding to the set of CPUs which have
 		       a PMU of this type signalling the PPI listed in the
-		       interrupts property.
+		       interrupts property, unless this is already specified
+		       by the PPI interrupt specifier itself (in which case
+		       the interrupt-affinity property shouldn't be present).
 
                        This property should be present when there is more than
 		       a single SPI.
diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
index 140436a..8e4d7f5 100644
--- a/drivers/perf/arm_pmu.c
+++ b/drivers/perf/arm_pmu.c
@@ -603,7 +603,8 @@ static void cpu_pmu_free_irq(struct arm_pmu *cpu_pmu)
 
 	irq = platform_get_irq(pmu_device, 0);
 	if (irq >= 0 && irq_is_percpu(irq)) {
-		on_each_cpu(cpu_pmu_disable_percpu_irq, &irq, 1);
+		on_each_cpu_mask(&cpu_pmu->supported_cpus,
+				 cpu_pmu_disable_percpu_irq, &irq, 1);
 		free_percpu_irq(irq, &hw_events->percpu_pmu);
 	} else {
 		for (i = 0; i < irqs; ++i) {
@@ -645,7 +646,9 @@ static int cpu_pmu_request_irq(struct arm_pmu *cpu_pmu, irq_handler_t handler)
 				irq);
 			return err;
 		}
-		on_each_cpu(cpu_pmu_enable_percpu_irq, &irq, 1);
+
+		on_each_cpu_mask(&cpu_pmu->supported_cpus,
+				 cpu_pmu_enable_percpu_irq, &irq, 1);
 	} else {
 		for (i = 0; i < irqs; ++i) {
 			int cpu = i;
@@ -961,9 +964,23 @@ static int of_pmu_irq_cfg(struct arm_pmu *pmu)
 		i++;
 	} while (1);
 
-	/* If we didn't manage to parse anything, claim to support all CPUs */
-	if (cpumask_weight(&pmu->supported_cpus) == 0)
-		cpumask_setall(&pmu->supported_cpus);
+	/* If we didn't manage to parse anything, try the interrupt affinity */
+	if (cpumask_weight(&pmu->supported_cpus) == 0) {
+		if (!using_spi) {
+			/* If using PPIs, check the affinity of the partition */
+			int ret, irq;
+
+			irq = platform_get_irq(pdev, 0);
+			ret = irq_get_percpu_devid_partition(irq, &pmu->supported_cpus);
+			if (ret) {
+				kfree(irqs);
+				return ret;
+			}
+		} else {
+			/* Otherwise default to all CPUs */
+			cpumask_setall(&pmu->supported_cpus);
+		}
+	}
 
 	/* If we matched up the IRQ affinities, use them to route the SPIs */
 	if (using_spi && i == pdev->num_resources)
-- 
2.1.4



From ayaka at soulik.info  Wed Jul  6 08:25:22 2016
From: ayaka at soulik.info (ayaka)
Date: Wed,  6 Jul 2016 23:25:22 +0800
Subject: [PATCH 1/3] ARM: dts: add label leds for rk3288-firefly.dtsi
In-Reply-To: <1467818724-21500-1-git-send-email-ayaka@soulik.info>
References: <1467818724-21500-1-git-send-email-ayaka@soulik.info>
Message-ID: <1467818724-21500-2-git-send-email-ayaka@soulik.info>

The connected leds are different in firefly and firefly reload.
Adding this label making it is possible to add more leds.

Signed-off-by: ayaka <ayaka at soulik.info>
---
 arch/arm/boot/dts/rk3288-firefly.dtsi | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/rk3288-firefly.dtsi b/arch/arm/boot/dts/rk3288-firefly.dtsi
index d6cf9ad..1d7f03c 100644
--- a/arch/arm/boot/dts/rk3288-firefly.dtsi
+++ b/arch/arm/boot/dts/rk3288-firefly.dtsi
@@ -99,7 +99,7 @@
 		};
 	};
 
-	leds {
+	leds: leds {
 		compatible = "gpio-leds";
 
 		work {
-- 
2.7.4



From ayaka at soulik.info  Wed Jul  6 08:25:21 2016
From: ayaka at soulik.info (ayaka)
Date: Wed,  6 Jul 2016 23:25:21 +0800
Subject: [PATCH 0/3] ARM: dts: the dts support for rk3288 firefly reload
Message-ID: <1467818724-21500-1-git-send-email-ayaka@soulik.info>

The rk3288 firefly reload  is a Rockchip RK3288 based board be found by
core board and main board. The regulators are connected in a different
way to the previous version of firefly boards, it is necessary to
move some common code to uncommon place.

I only tested the ethernet and confirmed that works.

ayaka (3):
  ARM: dts: add label leds for rk3288-firefly.dtsi
  ARM: dts: move regulator settings from common to specifics
  ARM: dts: add rk3288-firefly-reload

 arch/arm/boot/dts/Makefile                  |   1 +
 arch/arm/boot/dts/rk3288-firefly-beta.dts   |  30 ++++++++
 arch/arm/boot/dts/rk3288-firefly-reload.dts | 109 ++++++++++++++++++++++++++++
 arch/arm/boot/dts/rk3288-firefly.dts        |  30 ++++++++
 arch/arm/boot/dts/rk3288-firefly.dtsi       |  16 ++--
 5 files changed, 178 insertions(+), 8 deletions(-)
 create mode 100644 arch/arm/boot/dts/rk3288-firefly-reload.dts

-- 
2.7.4



From ayaka at soulik.info  Wed Jul  6 08:25:23 2016
From: ayaka at soulik.info (ayaka)
Date: Wed,  6 Jul 2016 23:25:23 +0800
Subject: [PATCH 2/3] ARM: dts: move regulator settings from common to specifics
In-Reply-To: <1467818724-21500-1-git-send-email-ayaka@soulik.info>
References: <1467818724-21500-1-git-send-email-ayaka@soulik.info>
Message-ID: <1467818724-21500-3-git-send-email-ayaka@soulik.info>

The regulator settings are the same in the firefly beta and
firefly. But the firefly reload is different. I remove
the difference part from the common header dtsi, but remained those
common one to keep references.

Signed-off-by: ayaka <ayaka at soulik.info>
---
 arch/arm/boot/dts/rk3288-firefly-beta.dts | 30 ++++++++++++++++++++++++++++++
 arch/arm/boot/dts/rk3288-firefly.dts      | 30 ++++++++++++++++++++++++++++++
 arch/arm/boot/dts/rk3288-firefly.dtsi     | 14 +++++++-------
 3 files changed, 67 insertions(+), 7 deletions(-)

diff --git a/arch/arm/boot/dts/rk3288-firefly-beta.dts b/arch/arm/boot/dts/rk3288-firefly-beta.dts
index 75d77e3..6c08dae2 100644
--- a/arch/arm/boot/dts/rk3288-firefly-beta.dts
+++ b/arch/arm/boot/dts/rk3288-firefly-beta.dts
@@ -69,3 +69,33 @@
 &pwm0 {
 	status = "okay";
 };
+
+&gmac {
+	phy-supply = <&vcc_lan>;
+};
+
+&io_domains {
+		audio-supply = <&vcca_33>;
+		flash1-supply = <&vcc_lan>;
+};
+
+&regulators {
+	vcca_18: REG7  {
+		regulator-name = "vcca_18";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+	};
+
+	vcca_33: REG8 {
+		regulator-name = "vcca_33";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-always-on;
+	};
+
+	vcc_lan: REG9 {
+		regulator-name = "vcc_lan";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+	};
+};
diff --git a/arch/arm/boot/dts/rk3288-firefly.dts b/arch/arm/boot/dts/rk3288-firefly.dts
index c07fe92..1964b6f 100644
--- a/arch/arm/boot/dts/rk3288-firefly.dts
+++ b/arch/arm/boot/dts/rk3288-firefly.dts
@@ -69,3 +69,33 @@
 &pwm1 {
 	status = "okay";
 };
+
+&gmac {
+	phy-supply = <&vcc_lan>;
+};
+
+&io_domains {
+		audio-supply = <&vcca_33>;
+		flash1-supply = <&vcc_lan>;
+};
+
+&regulators {
+	vcca_18: REG7  {
+		regulator-name = "vcca_18";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+	};
+
+	vcca_33: REG8 {
+		regulator-name = "vcca_33";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-always-on;
+	};
+
+	vcc_lan: REG9 {
+		regulator-name = "vcc_lan";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+	};
+};
diff --git a/arch/arm/boot/dts/rk3288-firefly.dtsi b/arch/arm/boot/dts/rk3288-firefly.dtsi
index 1d7f03c..205348f 100644
--- a/arch/arm/boot/dts/rk3288-firefly.dtsi
+++ b/arch/arm/boot/dts/rk3288-firefly.dtsi
@@ -68,11 +68,9 @@
 		compatible = "rockchip,rk3288-io-voltage-domain";
 		rockchip,grf = <&grf>;
 
-		audio-supply = <&vcca_33>;
 		bb-supply = <&vcc_io>;
 		dvp-supply = <&dovdd_1v8>;
 		flash0-supply = <&vcc_flash>;
-		flash1-supply = <&vcc_lan>;
 		gpio30-supply = <&vcc_io>;
 		gpio1830-supply = <&vcc_io>;
 		lcdc-supply = <&vcc_io>;
@@ -225,7 +223,6 @@
 	clock_in_out = "input";
 	pinctrl-names = "default";
 	pinctrl-0 = <&rgmii_pins>, <&phy_rst>, <&phy_pmeb>, <&phy_int>;
-	phy-supply = <&vcc_lan>;
 	phy-mode = "rgmii";
 	snps,reset-active-low;
 	snps,reset-delays-us = <0 10000 1000000>;
@@ -296,7 +293,7 @@
 		inl2-supply = <&vcc_sys>;
 		inl3-supply = <&vcc_20>;
 
-		regulators {
+		regulators: regulators {
 			vcc_ddr: REG1 {
 				regulator-name = "vcc_ddr";
 				regulator-min-microvolt = <1200000>;
@@ -339,24 +336,27 @@
 				regulator-always-on;
 			};
 
-			vcca_18: REG7 {
+			REG7  {
 				regulator-name = "vcca_18";
 				regulator-min-microvolt = <1800000>;
 				regulator-max-microvolt = <1800000>;
 			};
 
-			vcca_33: REG8 {
+
+			REG8 {
 				regulator-name = "vcca_33";
 				regulator-min-microvolt = <3300000>;
 				regulator-max-microvolt = <3300000>;
+				regulator-always-on;
 			};
 
-			vcc_lan: REG9 {
+			REG9 {
 				regulator-name = "vcc_lan";
 				regulator-min-microvolt = <3300000>;
 				regulator-max-microvolt = <3300000>;
 			};
 
+
 			vdd_10: REG10 {
 				regulator-name = "vdd_10";
 				regulator-min-microvolt = <1000000>;
-- 
2.7.4



From ayaka at soulik.info  Wed Jul  6 08:25:24 2016
From: ayaka at soulik.info (ayaka)
Date: Wed,  6 Jul 2016 23:25:24 +0800
Subject: [PATCH 3/3] ARM: dts: add rk3288-firefly-reload
In-Reply-To: <1467818724-21500-1-git-send-email-ayaka@soulik.info>
References: <1467818724-21500-1-git-send-email-ayaka@soulik.info>
Message-ID: <1467818724-21500-4-git-send-email-ayaka@soulik.info>

The firefly rk3288 reload is a Rockchip rk3288 based develop board.

Signed-off-by: ayaka <ayaka at soulik.info>
---
 arch/arm/boot/dts/Makefile                  |   1 +
 arch/arm/boot/dts/rk3288-firefly-reload.dts | 109 ++++++++++++++++++++++++++++
 2 files changed, 110 insertions(+)
 create mode 100644 arch/arm/boot/dts/rk3288-firefly-reload.dts

diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index 06b6c2d..19a6049 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -606,6 +606,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += \
 	rk3288-evb-rk808.dtb \
 	rk3288-firefly-beta.dtb \
 	rk3288-firefly.dtb \
+	rk3288-firefly-reload.dtb \
 	rk3288-miqi.dtb \
 	rk3288-popmetal.dtb \
 	rk3288-r89.dtb \
diff --git a/arch/arm/boot/dts/rk3288-firefly-reload.dts b/arch/arm/boot/dts/rk3288-firefly-reload.dts
new file mode 100644
index 0000000..9805cf2
--- /dev/null
+++ b/arch/arm/boot/dts/rk3288-firefly-reload.dts
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2016 Randy Li <ayaka at soulik.info>
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of the
+ *     License, or (at your option) any later version.
+ *
+ *     This file 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.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/dts-v1/;
+#include "rk3288-firefly.dtsi"
+
+/ {
+	model = "Firefly-RK3288-reload";
+	compatible = "firefly,firefly-rk3288-reload", "rockchip,rk3288";
+
+};
+
+&leds {
+	power {
+		gpios = <&gpio8 2 GPIO_ACTIVE_LOW>;
+		label = "firefly:blue:power";
+		linux,default-trigger = "default-on";
+		pinctrl-names = "default";
+		pinctrl-0 = <&power_led>;
+	};
+};
+
+&gmac {
+	phy-supply = <&vcc_lan>;
+};
+
+&pinctrl {
+	act8846 {
+		pmic_vsel: pmic-vsel {
+			rockchip,pins = <7 14 RK_FUNC_GPIO &pcfg_output_low>;
+		};
+	};
+
+	ir {
+		ir_int: ir-int {
+			rockchip,pins = <7 0 RK_FUNC_GPIO &pcfg_pull_up>;
+		};
+	};
+
+};
+
+&pwm1 {
+	status = "disabled";
+};
+
+&io_domains {
+		audio-supply = <&vcca_33>;
+		flash1-supply = <&vcc_lan>;
+};
+
+&regulators {
+	vcca_33: REG7 {
+		regulator-name = "vcca_33";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+	};
+
+	vcc_lan: REG8 {
+		regulator-name = "vcc_lan";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+	};
+
+	vccio_pmu: REG9 {
+		regulator-name = "vccio_pmu";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-always-on;
+	};
+};
-- 
2.7.4



From will.deacon at arm.com  Wed Jul  6 09:05:25 2016
From: will.deacon at arm.com (Will Deacon)
Date: Wed, 6 Jul 2016 17:05:25 +0100
Subject: [PATCH v2] drivers/perf: arm-pmu: Handle per-interrupt affinity
 mask
In-Reply-To: <1467815627-4061-1-git-send-email-marc.zyngier@arm.com>
References: <1467815627-4061-1-git-send-email-marc.zyngier@arm.com>
Message-ID: <20160706160525.GA31910@arm.com>

On Wed, Jul 06, 2016 at 03:33:47PM +0100, Marc Zyngier wrote:
> On a big-little system, PMUs can be wired to CPUs using per CPU
> interrups (PPI). In this case, it is important to make sure that
> the enable/disable do happen on the right set of CPUs.
> 
> So instead of relying on the interrupt-affinity property, we can
> use the actual percpu affinity that DT exposes as part of the
> interrupt specifier. The DT binding is also updated to reflect
> the fact that the interrupt-affinity property shouldn't be used
> in that case.
> 
> Signed-off-by: Marc Zyngier <marc.zyngier at arm.com>
> ---
> * From v1:
>   - propagate the error if irq_get_percpu_devid_partition fails

Thanks, I'll queue this.

Will


From heiko at sntech.de  Wed Jul  6 09:13:06 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Wed, 06 Jul 2016 18:13:06 +0200
Subject: [PATCH 2/3] ARM: dts: move regulator settings from common to
 specifics
In-Reply-To: <1467818724-21500-3-git-send-email-ayaka@soulik.info>
References: <1467818724-21500-1-git-send-email-ayaka@soulik.info>
 <1467818724-21500-3-git-send-email-ayaka@soulik.info>
Message-ID: <3015470.z4L7UQJKa9@phil>

Hi,

Am Mittwoch, 6. Juli 2016, 23:25:23 schrieb ayaka:
> The regulator settings are the same in the firefly beta and
> firefly. But the firefly reload is different. I remove
> the difference part from the common header dtsi, but remained those
> common one to keep references.
> 
> Signed-off-by: ayaka <ayaka at soulik.info>

applies to all 3 patches: we require real names both in the patch/mail from 
as well as the Signed-off-by line, see [0] at number 11. To my untrained eye 
the name given doesn't look like a real name, please correct me if I'm 
wrong.


[0] https://www.kernel.org/doc/Documentation/SubmittingPatches


> ---
>  arch/arm/boot/dts/rk3288-firefly-beta.dts | 30
> ++++++++++++++++++++++++++++++ arch/arm/boot/dts/rk3288-firefly.dts     
> | 30 ++++++++++++++++++++++++++++++ arch/arm/boot/dts/rk3288-firefly.dtsi
>     | 14 +++++++-------
>  3 files changed, 67 insertions(+), 7 deletions(-)
> 
> diff --git a/arch/arm/boot/dts/rk3288-firefly-beta.dts
> b/arch/arm/boot/dts/rk3288-firefly-beta.dts index 75d77e3..6c08dae2
> 100644
> --- a/arch/arm/boot/dts/rk3288-firefly-beta.dts
> +++ b/arch/arm/boot/dts/rk3288-firefly-beta.dts
> @@ -69,3 +69,33 @@
>  &pwm0 {
>  	status = "okay";
>  };
> +
> +&gmac {
> +	phy-supply = <&vcc_lan>;
> +};

Not 100% sure, but I guess this does not need to be duplicated.
The supply is named vcc_lan in all board revisions only the supplying 
regulator seems different, so it should be enough to point the vcc_lan 
phandle to the correct regulator in the relevant file, but the gmac 
assignment could stay in the main file?

> +
> +&io_domains {
> +		audio-supply = <&vcca_33>;
> +		flash1-supply = <&vcc_lan>;
> +};

same here

> +
> +&regulators {
> +	vcca_18: REG7  {
> +		regulator-name = "vcca_18";
> +		regulator-min-microvolt = <1800000>;
> +		regulator-max-microvolt = <1800000>;
> +	};
> +
> +	vcca_33: REG8 {
> +		regulator-name = "vcca_33";
> +		regulator-min-microvolt = <3300000>;
> +		regulator-max-microvolt = <3300000>;
> +		regulator-always-on;
> +	};
> +
> +	vcc_lan: REG9 {
> +		regulator-name = "vcc_lan";
> +		regulator-min-microvolt = <3300000>;
> +		regulator-max-microvolt = <3300000>;
> +	};
> +};
> diff --git a/arch/arm/boot/dts/rk3288-firefly.dts
> b/arch/arm/boot/dts/rk3288-firefly.dts index c07fe92..1964b6f 100644
> --- a/arch/arm/boot/dts/rk3288-firefly.dts
> +++ b/arch/arm/boot/dts/rk3288-firefly.dts
> @@ -69,3 +69,33 @@
>  &pwm1 {
>  	status = "okay";
>  };
> +
> +&gmac {
> +	phy-supply = <&vcc_lan>;
> +};
> +
> +&io_domains {
> +		audio-supply = <&vcca_33>;
> +		flash1-supply = <&vcc_lan>;
> +};
> +
> +&regulators {
> +	vcca_18: REG7  {
> +		regulator-name = "vcca_18";
> +		regulator-min-microvolt = <1800000>;
> +		regulator-max-microvolt = <1800000>;
> +	};
> +
> +	vcca_33: REG8 {
> +		regulator-name = "vcca_33";
> +		regulator-min-microvolt = <3300000>;
> +		regulator-max-microvolt = <3300000>;
> +		regulator-always-on;
> +	};
> +
> +	vcc_lan: REG9 {
> +		regulator-name = "vcc_lan";
> +		regulator-min-microvolt = <3300000>;
> +		regulator-max-microvolt = <3300000>;
> +	};
> +};
> diff --git a/arch/arm/boot/dts/rk3288-firefly.dtsi
> b/arch/arm/boot/dts/rk3288-firefly.dtsi index 1d7f03c..205348f 100644
> --- a/arch/arm/boot/dts/rk3288-firefly.dtsi
> +++ b/arch/arm/boot/dts/rk3288-firefly.dtsi
> @@ -68,11 +68,9 @@
>  		compatible = "rockchip,rk3288-io-voltage-domain";
>  		rockchip,grf = <&grf>;
> 
> -		audio-supply = <&vcca_33>;
>  		bb-supply = <&vcc_io>;
>  		dvp-supply = <&dovdd_1v8>;
>  		flash0-supply = <&vcc_flash>;
> -		flash1-supply = <&vcc_lan>;
>  		gpio30-supply = <&vcc_io>;
>  		gpio1830-supply = <&vcc_io>;
>  		lcdc-supply = <&vcc_io>;
> @@ -225,7 +223,6 @@
>  	clock_in_out = "input";
>  	pinctrl-names = "default";
>  	pinctrl-0 = <&rgmii_pins>, <&phy_rst>, <&phy_pmeb>, <&phy_int>;
> -	phy-supply = <&vcc_lan>;
>  	phy-mode = "rgmii";
>  	snps,reset-active-low;
>  	snps,reset-delays-us = <0 10000 1000000>;
> @@ -296,7 +293,7 @@
>  		inl2-supply = <&vcc_sys>;
>  		inl3-supply = <&vcc_20>;
> 
> -		regulators {
> +		regulators: regulators {

the phandle name is to generic, as this is part of the act8846 and not 
something generic. So please name the phandle act8846_regulators / 
act8846_regs or so.

>  			vcc_ddr: REG1 {
>  				regulator-name = "vcc_ddr";
>  				regulator-min-microvolt = <1200000>;
> @@ -339,24 +336,27 @@
>  				regulator-always-on;
>  			};
> 
> -			vcca_18: REG7 {
> +			REG7  {
>  				regulator-name = "vcca_18";
>  				regulator-min-microvolt = <1800000>;
>  				regulator-max-microvolt = <1800000>;
>  			};
> 
> -			vcca_33: REG8 {
> +

unecessary new empty line

> +			REG8 {
>  				regulator-name = "vcca_33";
>  				regulator-min-microvolt = <3300000>;
>  				regulator-max-microvolt = <3300000>;
> +				regulator-always-on;
>  			};
> 
> -			vcc_lan: REG9 {
> +			REG9 {
>  				regulator-name = "vcc_lan";
>  				regulator-min-microvolt = <3300000>;
>  				regulator-max-microvolt = <3300000>;
>  			};
> 
> +

unecessary new empty line

>  			vdd_10: REG10 {
>  				regulator-name = "vdd_10";
>  				regulator-min-microvolt = <1000000>;
> --
> 2.7.4


Heiko


From ayaka at soulik.info  Wed Jul  6 10:34:04 2016
From: ayaka at soulik.info (Randy Li)
Date: Thu,  7 Jul 2016 01:34:04 +0800
Subject: [PATCH 0/3] Re: [PATCH 2/3] ARM: dts: move regulator settings from
 common to specifics
In-Reply-To: <3015470.z4L7UQJKa9@phil>
References: <3015470.z4L7UQJKa9@phil>
Message-ID: <1467826447-24159-1-git-send-email-ayaka@soulik.info>

I am sorry to make this mistake. I have re-sent my patches.
Thank you for reminding me that.

I hope that I make it correct this time.

ayaka (3):
  ARM: dts: add label leds for rk3288-firefly.dtsi
  ARM: dts: move regulator settings from common to specifics
  ARM: dts: add rk3288-firefly-reload

 arch/arm/boot/dts/Makefile                  |   1 +
 arch/arm/boot/dts/rk3288-firefly-beta.dts   |  30 ++++++++
 arch/arm/boot/dts/rk3288-firefly-reload.dts | 109 ++++++++++++++++++++++++++++
 arch/arm/boot/dts/rk3288-firefly.dts        |  30 ++++++++
 arch/arm/boot/dts/rk3288-firefly.dtsi       |  16 ++--
 5 files changed, 178 insertions(+), 8 deletions(-)
 create mode 100644 arch/arm/boot/dts/rk3288-firefly-reload.dts

-- 
2.7.4



From ayaka at soulik.info  Wed Jul  6 10:34:05 2016
From: ayaka at soulik.info (Randy Li)
Date: Thu,  7 Jul 2016 01:34:05 +0800
Subject: [PATCH 1/3] ARM: dts: add label leds for rk3288-firefly.dtsi
In-Reply-To: <1467826447-24159-1-git-send-email-ayaka@soulik.info>
References: <3015470.z4L7UQJKa9@phil>
 <1467826447-24159-1-git-send-email-ayaka@soulik.info>
Message-ID: <1467826447-24159-2-git-send-email-ayaka@soulik.info>

From: ayaka <ayaka at soulik.info>

The connected leds are different in firefly release version
and firefly reload.
Adding this label making it is possible to add more leds.

Signed-off-by: Randy Li <ayaka at soulik.info>
---
 arch/arm/boot/dts/rk3288-firefly.dtsi | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/rk3288-firefly.dtsi b/arch/arm/boot/dts/rk3288-firefly.dtsi
index d6cf9ad..1d7f03c 100644
--- a/arch/arm/boot/dts/rk3288-firefly.dtsi
+++ b/arch/arm/boot/dts/rk3288-firefly.dtsi
@@ -99,7 +99,7 @@
 		};
 	};
 
-	leds {
+	leds: leds {
 		compatible = "gpio-leds";
 
 		work {
-- 
2.7.4



From ayaka at soulik.info  Wed Jul  6 10:34:06 2016
From: ayaka at soulik.info (Randy Li)
Date: Thu,  7 Jul 2016 01:34:06 +0800
Subject: [PATCH 2/3] ARM: dts: move regulator settings from common to specifics
In-Reply-To: <1467826447-24159-1-git-send-email-ayaka@soulik.info>
References: <3015470.z4L7UQJKa9@phil>
 <1467826447-24159-1-git-send-email-ayaka@soulik.info>
Message-ID: <1467826447-24159-3-git-send-email-ayaka@soulik.info>

From: ayaka <ayaka at soulik.info>

The regulator settings are the same in the firefly beta and
firefly release version. But the firefly reload is different.
I remove the different part from the common header dtsi file,
but remained those common one to keep references.

Signed-off-by: Randy Li <ayaka at soulik.info>
---
 arch/arm/boot/dts/rk3288-firefly-beta.dts | 30 ++++++++++++++++++++++++++++++
 arch/arm/boot/dts/rk3288-firefly.dts      | 30 ++++++++++++++++++++++++++++++
 arch/arm/boot/dts/rk3288-firefly.dtsi     | 14 +++++++-------
 3 files changed, 67 insertions(+), 7 deletions(-)

diff --git a/arch/arm/boot/dts/rk3288-firefly-beta.dts b/arch/arm/boot/dts/rk3288-firefly-beta.dts
index 75d77e3..6c08dae2 100644
--- a/arch/arm/boot/dts/rk3288-firefly-beta.dts
+++ b/arch/arm/boot/dts/rk3288-firefly-beta.dts
@@ -69,3 +69,33 @@
 &pwm0 {
 	status = "okay";
 };
+
+&gmac {
+	phy-supply = <&vcc_lan>;
+};
+
+&io_domains {
+		audio-supply = <&vcca_33>;
+		flash1-supply = <&vcc_lan>;
+};
+
+&regulators {
+	vcca_18: REG7  {
+		regulator-name = "vcca_18";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+	};
+
+	vcca_33: REG8 {
+		regulator-name = "vcca_33";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-always-on;
+	};
+
+	vcc_lan: REG9 {
+		regulator-name = "vcc_lan";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+	};
+};
diff --git a/arch/arm/boot/dts/rk3288-firefly.dts b/arch/arm/boot/dts/rk3288-firefly.dts
index c07fe92..1964b6f 100644
--- a/arch/arm/boot/dts/rk3288-firefly.dts
+++ b/arch/arm/boot/dts/rk3288-firefly.dts
@@ -69,3 +69,33 @@
 &pwm1 {
 	status = "okay";
 };
+
+&gmac {
+	phy-supply = <&vcc_lan>;
+};
+
+&io_domains {
+		audio-supply = <&vcca_33>;
+		flash1-supply = <&vcc_lan>;
+};
+
+&regulators {
+	vcca_18: REG7  {
+		regulator-name = "vcca_18";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+	};
+
+	vcca_33: REG8 {
+		regulator-name = "vcca_33";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-always-on;
+	};
+
+	vcc_lan: REG9 {
+		regulator-name = "vcc_lan";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+	};
+};
diff --git a/arch/arm/boot/dts/rk3288-firefly.dtsi b/arch/arm/boot/dts/rk3288-firefly.dtsi
index 1d7f03c..205348f 100644
--- a/arch/arm/boot/dts/rk3288-firefly.dtsi
+++ b/arch/arm/boot/dts/rk3288-firefly.dtsi
@@ -68,11 +68,9 @@
 		compatible = "rockchip,rk3288-io-voltage-domain";
 		rockchip,grf = <&grf>;
 
-		audio-supply = <&vcca_33>;
 		bb-supply = <&vcc_io>;
 		dvp-supply = <&dovdd_1v8>;
 		flash0-supply = <&vcc_flash>;
-		flash1-supply = <&vcc_lan>;
 		gpio30-supply = <&vcc_io>;
 		gpio1830-supply = <&vcc_io>;
 		lcdc-supply = <&vcc_io>;
@@ -225,7 +223,6 @@
 	clock_in_out = "input";
 	pinctrl-names = "default";
 	pinctrl-0 = <&rgmii_pins>, <&phy_rst>, <&phy_pmeb>, <&phy_int>;
-	phy-supply = <&vcc_lan>;
 	phy-mode = "rgmii";
 	snps,reset-active-low;
 	snps,reset-delays-us = <0 10000 1000000>;
@@ -296,7 +293,7 @@
 		inl2-supply = <&vcc_sys>;
 		inl3-supply = <&vcc_20>;
 
-		regulators {
+		regulators: regulators {
 			vcc_ddr: REG1 {
 				regulator-name = "vcc_ddr";
 				regulator-min-microvolt = <1200000>;
@@ -339,24 +336,27 @@
 				regulator-always-on;
 			};
 
-			vcca_18: REG7 {
+			REG7  {
 				regulator-name = "vcca_18";
 				regulator-min-microvolt = <1800000>;
 				regulator-max-microvolt = <1800000>;
 			};
 
-			vcca_33: REG8 {
+
+			REG8 {
 				regulator-name = "vcca_33";
 				regulator-min-microvolt = <3300000>;
 				regulator-max-microvolt = <3300000>;
+				regulator-always-on;
 			};
 
-			vcc_lan: REG9 {
+			REG9 {
 				regulator-name = "vcc_lan";
 				regulator-min-microvolt = <3300000>;
 				regulator-max-microvolt = <3300000>;
 			};
 
+
 			vdd_10: REG10 {
 				regulator-name = "vdd_10";
 				regulator-min-microvolt = <1000000>;
-- 
2.7.4



From ayaka at soulik.info  Wed Jul  6 10:34:07 2016
From: ayaka at soulik.info (Randy Li)
Date: Thu,  7 Jul 2016 01:34:07 +0800
Subject: [PATCH 3/3] ARM: dts: add rk3288-firefly-reload
In-Reply-To: <1467826447-24159-1-git-send-email-ayaka@soulik.info>
References: <3015470.z4L7UQJKa9@phil>
 <1467826447-24159-1-git-send-email-ayaka@soulik.info>
Message-ID: <1467826447-24159-4-git-send-email-ayaka@soulik.info>

From: ayaka <ayaka at soulik.info>

The firefly rk3288 reload is a Rockchip rk3288 based develop board.

Signed-off-by: Randy Li <ayaka at soulik.info>
---
 arch/arm/boot/dts/Makefile                  |   1 +
 arch/arm/boot/dts/rk3288-firefly-reload.dts | 109 ++++++++++++++++++++++++++++
 2 files changed, 110 insertions(+)
 create mode 100644 arch/arm/boot/dts/rk3288-firefly-reload.dts

diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index 06b6c2d..19a6049 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -606,6 +606,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += \
 	rk3288-evb-rk808.dtb \
 	rk3288-firefly-beta.dtb \
 	rk3288-firefly.dtb \
+	rk3288-firefly-reload.dtb \
 	rk3288-miqi.dtb \
 	rk3288-popmetal.dtb \
 	rk3288-r89.dtb \
diff --git a/arch/arm/boot/dts/rk3288-firefly-reload.dts b/arch/arm/boot/dts/rk3288-firefly-reload.dts
new file mode 100644
index 0000000..9805cf2
--- /dev/null
+++ b/arch/arm/boot/dts/rk3288-firefly-reload.dts
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2016 Randy Li <ayaka at soulik.info>
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of the
+ *     License, or (at your option) any later version.
+ *
+ *     This file 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.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/dts-v1/;
+#include "rk3288-firefly.dtsi"
+
+/ {
+	model = "Firefly-RK3288-reload";
+	compatible = "firefly,firefly-rk3288-reload", "rockchip,rk3288";
+
+};
+
+&leds {
+	power {
+		gpios = <&gpio8 2 GPIO_ACTIVE_LOW>;
+		label = "firefly:blue:power";
+		linux,default-trigger = "default-on";
+		pinctrl-names = "default";
+		pinctrl-0 = <&power_led>;
+	};
+};
+
+&gmac {
+	phy-supply = <&vcc_lan>;
+};
+
+&pinctrl {
+	act8846 {
+		pmic_vsel: pmic-vsel {
+			rockchip,pins = <7 14 RK_FUNC_GPIO &pcfg_output_low>;
+		};
+	};
+
+	ir {
+		ir_int: ir-int {
+			rockchip,pins = <7 0 RK_FUNC_GPIO &pcfg_pull_up>;
+		};
+	};
+
+};
+
+&pwm1 {
+	status = "disabled";
+};
+
+&io_domains {
+		audio-supply = <&vcca_33>;
+		flash1-supply = <&vcc_lan>;
+};
+
+&regulators {
+	vcca_33: REG7 {
+		regulator-name = "vcca_33";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+	};
+
+	vcc_lan: REG8 {
+		regulator-name = "vcc_lan";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+	};
+
+	vccio_pmu: REG9 {
+		regulator-name = "vccio_pmu";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-always-on;
+	};
+};
-- 
2.7.4



From ayaka at soulik.info  Wed Jul  6 10:52:41 2016
From: ayaka at soulik.info (Randy Li)
Date: Thu,  7 Jul 2016 01:52:41 +0800
Subject: [PATCH 0/3] Re:[PATCH 2/3] ARM: dts: move regulator settings from
 common to specifics
In-Reply-To: <3015470.z4L7UQJKa9@phil>
References: <3015470.z4L7UQJKa9@phil>
Message-ID: <1467827564-28007-1-git-send-email-ayaka@soulik.info>

I am very sorry very sorry, the last time I sent are still wrong.
I forget to reset the author. Please drop the below ones,
and have a review on this time.

I am sorry to waste your time.

Randy Li (3):
  ARM: dts: add label leds for rk3288-firefly.dtsi
  ARM: dts: move regulator settings from common to specifics
  ARM: dts: add rk3288-firefly-reload

 arch/arm/boot/dts/Makefile                  |   1 +
 arch/arm/boot/dts/rk3288-firefly-beta.dts   |  30 ++++++++
 arch/arm/boot/dts/rk3288-firefly-reload.dts | 109 ++++++++++++++++++++++++++++
 arch/arm/boot/dts/rk3288-firefly.dts        |  30 ++++++++
 arch/arm/boot/dts/rk3288-firefly.dtsi       |  16 ++--
 5 files changed, 178 insertions(+), 8 deletions(-)
 create mode 100644 arch/arm/boot/dts/rk3288-firefly-reload.dts

-- 
2.7.4



From ayaka at soulik.info  Wed Jul  6 10:52:42 2016
From: ayaka at soulik.info (Randy Li)
Date: Thu,  7 Jul 2016 01:52:42 +0800
Subject: [PATCH 1/3] ARM: dts: add label leds for rk3288-firefly.dtsi
In-Reply-To: <1467827564-28007-1-git-send-email-ayaka@soulik.info>
References: <3015470.z4L7UQJKa9@phil>
 <1467827564-28007-1-git-send-email-ayaka@soulik.info>
Message-ID: <1467827564-28007-2-git-send-email-ayaka@soulik.info>

The connected leds are different in firefly release version
and firefly reload.
Adding this label making it is possible to add more leds.

Signed-off-by: Randy Li <ayaka at soulik.info>
---
 arch/arm/boot/dts/rk3288-firefly.dtsi | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/rk3288-firefly.dtsi b/arch/arm/boot/dts/rk3288-firefly.dtsi
index d6cf9ad..1d7f03c 100644
--- a/arch/arm/boot/dts/rk3288-firefly.dtsi
+++ b/arch/arm/boot/dts/rk3288-firefly.dtsi
@@ -99,7 +99,7 @@
 		};
 	};
 
-	leds {
+	leds: leds {
 		compatible = "gpio-leds";
 
 		work {
-- 
2.7.4



From ayaka at soulik.info  Wed Jul  6 10:52:43 2016
From: ayaka at soulik.info (Randy Li)
Date: Thu,  7 Jul 2016 01:52:43 +0800
Subject: [PATCH 2/3] ARM: dts: move regulator settings from common to specifics
In-Reply-To: <1467827564-28007-1-git-send-email-ayaka@soulik.info>
References: <3015470.z4L7UQJKa9@phil>
 <1467827564-28007-1-git-send-email-ayaka@soulik.info>
Message-ID: <1467827564-28007-3-git-send-email-ayaka@soulik.info>

The regulator settings are the same in the firefly beta and
firefly release version. But the firefly reload is different.
I remove the different part from the common header dtsi file,
but remained those common one to keep references.

Signed-off-by: Randy Li <ayaka at soulik.info>
---
 arch/arm/boot/dts/rk3288-firefly-beta.dts | 30 ++++++++++++++++++++++++++++++
 arch/arm/boot/dts/rk3288-firefly.dts      | 30 ++++++++++++++++++++++++++++++
 arch/arm/boot/dts/rk3288-firefly.dtsi     | 14 +++++++-------
 3 files changed, 67 insertions(+), 7 deletions(-)

diff --git a/arch/arm/boot/dts/rk3288-firefly-beta.dts b/arch/arm/boot/dts/rk3288-firefly-beta.dts
index 75d77e3..6c08dae2 100644
--- a/arch/arm/boot/dts/rk3288-firefly-beta.dts
+++ b/arch/arm/boot/dts/rk3288-firefly-beta.dts
@@ -69,3 +69,33 @@
 &pwm0 {
 	status = "okay";
 };
+
+&gmac {
+	phy-supply = <&vcc_lan>;
+};
+
+&io_domains {
+		audio-supply = <&vcca_33>;
+		flash1-supply = <&vcc_lan>;
+};
+
+&regulators {
+	vcca_18: REG7  {
+		regulator-name = "vcca_18";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+	};
+
+	vcca_33: REG8 {
+		regulator-name = "vcca_33";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-always-on;
+	};
+
+	vcc_lan: REG9 {
+		regulator-name = "vcc_lan";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+	};
+};
diff --git a/arch/arm/boot/dts/rk3288-firefly.dts b/arch/arm/boot/dts/rk3288-firefly.dts
index c07fe92..1964b6f 100644
--- a/arch/arm/boot/dts/rk3288-firefly.dts
+++ b/arch/arm/boot/dts/rk3288-firefly.dts
@@ -69,3 +69,33 @@
 &pwm1 {
 	status = "okay";
 };
+
+&gmac {
+	phy-supply = <&vcc_lan>;
+};
+
+&io_domains {
+		audio-supply = <&vcca_33>;
+		flash1-supply = <&vcc_lan>;
+};
+
+&regulators {
+	vcca_18: REG7  {
+		regulator-name = "vcca_18";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+	};
+
+	vcca_33: REG8 {
+		regulator-name = "vcca_33";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-always-on;
+	};
+
+	vcc_lan: REG9 {
+		regulator-name = "vcc_lan";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+	};
+};
diff --git a/arch/arm/boot/dts/rk3288-firefly.dtsi b/arch/arm/boot/dts/rk3288-firefly.dtsi
index 1d7f03c..205348f 100644
--- a/arch/arm/boot/dts/rk3288-firefly.dtsi
+++ b/arch/arm/boot/dts/rk3288-firefly.dtsi
@@ -68,11 +68,9 @@
 		compatible = "rockchip,rk3288-io-voltage-domain";
 		rockchip,grf = <&grf>;
 
-		audio-supply = <&vcca_33>;
 		bb-supply = <&vcc_io>;
 		dvp-supply = <&dovdd_1v8>;
 		flash0-supply = <&vcc_flash>;
-		flash1-supply = <&vcc_lan>;
 		gpio30-supply = <&vcc_io>;
 		gpio1830-supply = <&vcc_io>;
 		lcdc-supply = <&vcc_io>;
@@ -225,7 +223,6 @@
 	clock_in_out = "input";
 	pinctrl-names = "default";
 	pinctrl-0 = <&rgmii_pins>, <&phy_rst>, <&phy_pmeb>, <&phy_int>;
-	phy-supply = <&vcc_lan>;
 	phy-mode = "rgmii";
 	snps,reset-active-low;
 	snps,reset-delays-us = <0 10000 1000000>;
@@ -296,7 +293,7 @@
 		inl2-supply = <&vcc_sys>;
 		inl3-supply = <&vcc_20>;
 
-		regulators {
+		regulators: regulators {
 			vcc_ddr: REG1 {
 				regulator-name = "vcc_ddr";
 				regulator-min-microvolt = <1200000>;
@@ -339,24 +336,27 @@
 				regulator-always-on;
 			};
 
-			vcca_18: REG7 {
+			REG7  {
 				regulator-name = "vcca_18";
 				regulator-min-microvolt = <1800000>;
 				regulator-max-microvolt = <1800000>;
 			};
 
-			vcca_33: REG8 {
+
+			REG8 {
 				regulator-name = "vcca_33";
 				regulator-min-microvolt = <3300000>;
 				regulator-max-microvolt = <3300000>;
+				regulator-always-on;
 			};
 
-			vcc_lan: REG9 {
+			REG9 {
 				regulator-name = "vcc_lan";
 				regulator-min-microvolt = <3300000>;
 				regulator-max-microvolt = <3300000>;
 			};
 
+
 			vdd_10: REG10 {
 				regulator-name = "vdd_10";
 				regulator-min-microvolt = <1000000>;
-- 
2.7.4



From ayaka at soulik.info  Wed Jul  6 10:52:44 2016
From: ayaka at soulik.info (Randy Li)
Date: Thu,  7 Jul 2016 01:52:44 +0800
Subject: [PATCH 3/3] ARM: dts: add rk3288-firefly-reload
In-Reply-To: <1467827564-28007-1-git-send-email-ayaka@soulik.info>
References: <3015470.z4L7UQJKa9@phil>
 <1467827564-28007-1-git-send-email-ayaka@soulik.info>
Message-ID: <1467827564-28007-4-git-send-email-ayaka@soulik.info>

The firefly rk3288 reload is a Rockchip rk3288 based develop board.

Signed-off-by: Randy Li <ayaka at soulik.info>
---
 arch/arm/boot/dts/Makefile                  |   1 +
 arch/arm/boot/dts/rk3288-firefly-reload.dts | 109 ++++++++++++++++++++++++++++
 2 files changed, 110 insertions(+)
 create mode 100644 arch/arm/boot/dts/rk3288-firefly-reload.dts

diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index 06b6c2d..19a6049 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -606,6 +606,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += \
 	rk3288-evb-rk808.dtb \
 	rk3288-firefly-beta.dtb \
 	rk3288-firefly.dtb \
+	rk3288-firefly-reload.dtb \
 	rk3288-miqi.dtb \
 	rk3288-popmetal.dtb \
 	rk3288-r89.dtb \
diff --git a/arch/arm/boot/dts/rk3288-firefly-reload.dts b/arch/arm/boot/dts/rk3288-firefly-reload.dts
new file mode 100644
index 0000000..9805cf2
--- /dev/null
+++ b/arch/arm/boot/dts/rk3288-firefly-reload.dts
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2016 Randy Li <ayaka at soulik.info>
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of the
+ *     License, or (at your option) any later version.
+ *
+ *     This file 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.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/dts-v1/;
+#include "rk3288-firefly.dtsi"
+
+/ {
+	model = "Firefly-RK3288-reload";
+	compatible = "firefly,firefly-rk3288-reload", "rockchip,rk3288";
+
+};
+
+&leds {
+	power {
+		gpios = <&gpio8 2 GPIO_ACTIVE_LOW>;
+		label = "firefly:blue:power";
+		linux,default-trigger = "default-on";
+		pinctrl-names = "default";
+		pinctrl-0 = <&power_led>;
+	};
+};
+
+&gmac {
+	phy-supply = <&vcc_lan>;
+};
+
+&pinctrl {
+	act8846 {
+		pmic_vsel: pmic-vsel {
+			rockchip,pins = <7 14 RK_FUNC_GPIO &pcfg_output_low>;
+		};
+	};
+
+	ir {
+		ir_int: ir-int {
+			rockchip,pins = <7 0 RK_FUNC_GPIO &pcfg_pull_up>;
+		};
+	};
+
+};
+
+&pwm1 {
+	status = "disabled";
+};
+
+&io_domains {
+		audio-supply = <&vcca_33>;
+		flash1-supply = <&vcc_lan>;
+};
+
+&regulators {
+	vcca_33: REG7 {
+		regulator-name = "vcca_33";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+	};
+
+	vcc_lan: REG8 {
+		regulator-name = "vcc_lan";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+	};
+
+	vccio_pmu: REG9 {
+		regulator-name = "vccio_pmu";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-always-on;
+	};
+};
-- 
2.7.4



From ayaka at soulik.info  Wed Jul  6 11:06:23 2016
From: ayaka at soulik.info (Randy Li)
Date: Thu, 7 Jul 2016 02:06:23 +0800
Subject: [PATCH 2/3] ARM: dts: move regulator settings from common to
 specifics
In-Reply-To: <3015470.z4L7UQJKa9@phil>
References: <1467818724-21500-1-git-send-email-ayaka@soulik.info>
 <1467818724-21500-3-git-send-email-ayaka@soulik.info>
 <3015470.z4L7UQJKa9@phil>
Message-ID: <d2f0c905-d81f-efbe-7c44-b2ba240bd04c@soulik.info>



at July, 7th,  Heiko Stuebner Wrote:
> Hi,
>
> Am Mittwoch, 6. Juli 2016, 23:25:23 schrieb ayaka:
>> The regulator settings are the same in the firefly beta and
>> firefly. But the firefly reload is different. I remove
>> the difference part from the common header dtsi, but remained those
>> common one to keep references.
>>
>> Signed-off-by: ayaka <ayaka at soulik.info>
> applies to all 3 patches: we require real names both in the patch/mail from
> as well as the Signed-off-by line, see [0] at number 11. To my untrained eye
> the name given doesn't look like a real name, please correct me if I'm
> wrong.
>
>
> [0] https://www.kernel.org/doc/Documentation/SubmittingPatches
>
>
>> ---
>>   arch/arm/boot/dts/rk3288-firefly-beta.dts | 30
>> ++++++++++++++++++++++++++++++ arch/arm/boot/dts/rk3288-firefly.dts
>> | 30 ++++++++++++++++++++++++++++++ arch/arm/boot/dts/rk3288-firefly.dtsi
>>      | 14 +++++++-------
>>   3 files changed, 67 insertions(+), 7 deletions(-)
>>
>> diff --git a/arch/arm/boot/dts/rk3288-firefly-beta.dts
>> b/arch/arm/boot/dts/rk3288-firefly-beta.dts index 75d77e3..6c08dae2
>> 100644
>> --- a/arch/arm/boot/dts/rk3288-firefly-beta.dts
>> +++ b/arch/arm/boot/dts/rk3288-firefly-beta.dts
>> @@ -69,3 +69,33 @@
>>   &pwm0 {
>>   	status = "okay";
>>   };
>> +
>> +&gmac {
>> +	phy-supply = <&vcc_lan>;
>> +};
> Not 100% sure, but I guess this does not need to be duplicated.
> The supply is named vcc_lan in all board revisions only the supplying
> regulator seems different, so it should be enough to point the vcc_lan
> phandle to the correct regulator in the relevant file, but the gmac
> assignment could stay in the main file?
>
But I can't have two label in two different place.
So I have no choice but to do so. If the dts compiler in kernel support
the overwrite now and unresolved symbols now, I would glad to know that.
>> +
>> +&io_domains {
>> +		audio-supply = <&vcca_33>;
>> +		flash1-supply = <&vcc_lan>;
>> +};
> same here
>
>> +
>> +&regulators {
>> +	vcca_18: REG7  {
>> +		regulator-name = "vcca_18";
>> +		regulator-min-microvolt = <1800000>;
>> +		regulator-max-microvolt = <1800000>;
>> +	};
>> +
>> +	vcca_33: REG8 {
>> +		regulator-name = "vcca_33";
>> +		regulator-min-microvolt = <3300000>;
>> +		regulator-max-microvolt = <3300000>;
>> +		regulator-always-on;
>> +	};
>> +
>> +	vcc_lan: REG9 {
>> +		regulator-name = "vcc_lan";
>> +		regulator-min-microvolt = <3300000>;
>> +		regulator-max-microvolt = <3300000>;
>> +	};
>> +};
>> diff --git a/arch/arm/boot/dts/rk3288-firefly.dts
>> b/arch/arm/boot/dts/rk3288-firefly.dts index c07fe92..1964b6f 100644
>> --- a/arch/arm/boot/dts/rk3288-firefly.dts
>> +++ b/arch/arm/boot/dts/rk3288-firefly.dts
>> @@ -69,3 +69,33 @@
>>   &pwm1 {
>>   	status = "okay";
>>   };
>> +
>> +&gmac {
>> +	phy-supply = <&vcc_lan>;
>> +};
>> +
>> +&io_domains {
>> +		audio-supply = <&vcca_33>;
>> +		flash1-supply = <&vcc_lan>;
>> +};
>> +
>> +&regulators {
>> +	vcca_18: REG7  {
>> +		regulator-name = "vcca_18";
>> +		regulator-min-microvolt = <1800000>;
>> +		regulator-max-microvolt = <1800000>;
>> +	};
>> +
>> +	vcca_33: REG8 {
>> +		regulator-name = "vcca_33";
>> +		regulator-min-microvolt = <3300000>;
>> +		regulator-max-microvolt = <3300000>;
>> +		regulator-always-on;
>> +	};
>> +
>> +	vcc_lan: REG9 {
>> +		regulator-name = "vcc_lan";
>> +		regulator-min-microvolt = <3300000>;
>> +		regulator-max-microvolt = <3300000>;
>> +	};
>> +};
>> diff --git a/arch/arm/boot/dts/rk3288-firefly.dtsi
>> b/arch/arm/boot/dts/rk3288-firefly.dtsi index 1d7f03c..205348f 100644
>> --- a/arch/arm/boot/dts/rk3288-firefly.dtsi
>> +++ b/arch/arm/boot/dts/rk3288-firefly.dtsi
>> @@ -68,11 +68,9 @@
>>   		compatible = "rockchip,rk3288-io-voltage-domain";
>>   		rockchip,grf = <&grf>;
>>
>> -		audio-supply = <&vcca_33>;
>>   		bb-supply = <&vcc_io>;
>>   		dvp-supply = <&dovdd_1v8>;
>>   		flash0-supply = <&vcc_flash>;
>> -		flash1-supply = <&vcc_lan>;
>>   		gpio30-supply = <&vcc_io>;
>>   		gpio1830-supply = <&vcc_io>;
>>   		lcdc-supply = <&vcc_io>;
>> @@ -225,7 +223,6 @@
>>   	clock_in_out = "input";
>>   	pinctrl-names = "default";
>>   	pinctrl-0 = <&rgmii_pins>, <&phy_rst>, <&phy_pmeb>, <&phy_int>;
>> -	phy-supply = <&vcc_lan>;
>>   	phy-mode = "rgmii";
>>   	snps,reset-active-low;
>>   	snps,reset-delays-us = <0 10000 1000000>;
>> @@ -296,7 +293,7 @@
>>   		inl2-supply = <&vcc_sys>;
>>   		inl3-supply = <&vcc_20>;
>>
>> -		regulators {
>> +		regulators: regulators {
> the phandle name is to generic, as this is part of the act8846 and not
> something generic. So please name the phandle act8846_regulators /
> act8846_regs or so.
I forget that, I would re-send new patches. I am very sorry to re-send 
the same patches three times.
I should not be such hurry.
>
>>   			vcc_ddr: REG1 {
>>   				regulator-name = "vcc_ddr";
>>   				regulator-min-microvolt = <1200000>;
>> @@ -339,24 +336,27 @@
>>   				regulator-always-on;
>>   			};
>>
>> -			vcca_18: REG7 {
>> +			REG7  {
>>   				regulator-name = "vcca_18";
>>   				regulator-min-microvolt = <1800000>;
>>   				regulator-max-microvolt = <1800000>;
>>   			};
>>
>> -			vcca_33: REG8 {
>> +
> unecessary new empty line
It would be removed
>> +			REG8 {
>>   				regulator-name = "vcca_33";
>>   				regulator-min-microvolt = <3300000>;
>>   				regulator-max-microvolt = <3300000>;
>> +				regulator-always-on;
>>   			};
>>
>> -			vcc_lan: REG9 {
>> +			REG9 {
>>   				regulator-name = "vcc_lan";
>>   				regulator-min-microvolt = <3300000>;
>>   				regulator-max-microvolt = <3300000>;
>>   			};
>>
>> +
> unecessary new empty line
It would be removed
>
>>   			vdd_10: REG10 {
>>   				regulator-name = "vdd_10";
>>   				regulator-min-microvolt = <1000000>;
>> --
>> 2.7.4
>
> Heiko



From ayaka at soulik.info  Wed Jul  6 11:22:57 2016
From: ayaka at soulik.info (Randy Li)
Date: Thu,  7 Jul 2016 02:22:57 +0800
Subject: [PATCH 0/3] ARM: dts: the dts support for rk3288 firefly reload
In-Reply-To: <3015470.z4L7UQJKa9@phil>
References: <3015470.z4L7UQJKa9@phil>
Message-ID: <1467829380-16242-1-git-send-email-ayaka@soulik.info>

The rk3288 firefly reload  is a Rockchip RK3288 based board be found by
core board and main board. The regulators are connected in a different
way to the previous version of firefly boards, it is necessary to
move some common code to uncommon place.

I only tested the ethernet and confirmed that works.
The usb in this board won't caused by the bugs in the driver.

This version follow the suggests from Heiko Stuebner,
except the duplicated supply name problem, I don't think
it could be fixed in that way.

Randy Li (3):
  ARM: dts: add label leds for rk3288-firefly.dtsi
  ARM: dts: move regulator settings from common to specifics
  ARM: dts: add rk3288-firefly-reload

 arch/arm/boot/dts/Makefile                  |   1 +
 arch/arm/boot/dts/rk3288-firefly-beta.dts   |  30 ++++++++
 arch/arm/boot/dts/rk3288-firefly-reload.dts | 109 ++++++++++++++++++++++++++++
 arch/arm/boot/dts/rk3288-firefly.dts        |  30 ++++++++
 arch/arm/boot/dts/rk3288-firefly.dtsi       |  16 ++--
 5 files changed, 178 insertions(+), 8 deletions(-)
 create mode 100644 arch/arm/boot/dts/rk3288-firefly-reload.dts

-- 
2.7.4



From ayaka at soulik.info  Wed Jul  6 11:22:58 2016
From: ayaka at soulik.info (Randy Li)
Date: Thu,  7 Jul 2016 02:22:58 +0800
Subject: [PATCH 1/3] ARM: dts: add label leds for rk3288-firefly.dtsi
In-Reply-To: <1467829380-16242-1-git-send-email-ayaka@soulik.info>
References: <3015470.z4L7UQJKa9@phil>
 <1467829380-16242-1-git-send-email-ayaka@soulik.info>
Message-ID: <1467829380-16242-2-git-send-email-ayaka@soulik.info>

The connected leds are different in firefly release version
and firefly reload.
Adding this label making it is possible to add more leds.

Signed-off-by: Randy Li <ayaka at soulik.info>
---
 arch/arm/boot/dts/rk3288-firefly.dtsi | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/rk3288-firefly.dtsi b/arch/arm/boot/dts/rk3288-firefly.dtsi
index d6cf9ad..1d7f03c 100644
--- a/arch/arm/boot/dts/rk3288-firefly.dtsi
+++ b/arch/arm/boot/dts/rk3288-firefly.dtsi
@@ -99,7 +99,7 @@
 		};
 	};
 
-	leds {
+	leds: leds {
 		compatible = "gpio-leds";
 
 		work {
-- 
2.7.4



From ayaka at soulik.info  Wed Jul  6 11:22:59 2016
From: ayaka at soulik.info (Randy Li)
Date: Thu,  7 Jul 2016 02:22:59 +0800
Subject: [PATCH 2/3] ARM: dts: move regulator settings from common to specifics
In-Reply-To: <1467829380-16242-1-git-send-email-ayaka@soulik.info>
References: <3015470.z4L7UQJKa9@phil>
 <1467829380-16242-1-git-send-email-ayaka@soulik.info>
Message-ID: <1467829380-16242-3-git-send-email-ayaka@soulik.info>

The regulator settings are the same in the firefly beta and
firefly release version. But the firefly reload is different.
I remove the different part from the common header dtsi file,
but remained those common one to keep references.

Signed-off-by: Randy Li <ayaka at soulik.info>
---
 arch/arm/boot/dts/rk3288-firefly-beta.dts | 30 ++++++++++++++++++++++++++++++
 arch/arm/boot/dts/rk3288-firefly.dts      | 30 ++++++++++++++++++++++++++++++
 arch/arm/boot/dts/rk3288-firefly.dtsi     | 12 +++++-------
 3 files changed, 65 insertions(+), 7 deletions(-)

diff --git a/arch/arm/boot/dts/rk3288-firefly-beta.dts b/arch/arm/boot/dts/rk3288-firefly-beta.dts
index 75d77e3..375c5d8 100644
--- a/arch/arm/boot/dts/rk3288-firefly-beta.dts
+++ b/arch/arm/boot/dts/rk3288-firefly-beta.dts
@@ -69,3 +69,33 @@
 &pwm0 {
 	status = "okay";
 };
+
+&gmac {
+	phy-supply = <&vcc_lan>;
+};
+
+&io_domains {
+		audio-supply = <&vcca_33>;
+		flash1-supply = <&vcc_lan>;
+};
+
+&act8846_regs {
+	vcca_18: REG7  {
+		regulator-name = "vcca_18";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+	};
+
+	vcca_33: REG8 {
+		regulator-name = "vcca_33";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-always-on;
+	};
+
+	vcc_lan: REG9 {
+		regulator-name = "vcc_lan";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+	};
+};
diff --git a/arch/arm/boot/dts/rk3288-firefly.dts b/arch/arm/boot/dts/rk3288-firefly.dts
index c07fe92..e89e575 100644
--- a/arch/arm/boot/dts/rk3288-firefly.dts
+++ b/arch/arm/boot/dts/rk3288-firefly.dts
@@ -69,3 +69,33 @@
 &pwm1 {
 	status = "okay";
 };
+
+&gmac {
+	phy-supply = <&vcc_lan>;
+};
+
+&io_domains {
+		audio-supply = <&vcca_33>;
+		flash1-supply = <&vcc_lan>;
+};
+
+&act8846_regs {
+	vcca_18: REG7  {
+		regulator-name = "vcca_18";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+	};
+
+	vcca_33: REG8 {
+		regulator-name = "vcca_33";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-always-on;
+	};
+
+	vcc_lan: REG9 {
+		regulator-name = "vcc_lan";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+	};
+};
diff --git a/arch/arm/boot/dts/rk3288-firefly.dtsi b/arch/arm/boot/dts/rk3288-firefly.dtsi
index 1d7f03c..194e05a 100644
--- a/arch/arm/boot/dts/rk3288-firefly.dtsi
+++ b/arch/arm/boot/dts/rk3288-firefly.dtsi
@@ -68,11 +68,9 @@
 		compatible = "rockchip,rk3288-io-voltage-domain";
 		rockchip,grf = <&grf>;
 
-		audio-supply = <&vcca_33>;
 		bb-supply = <&vcc_io>;
 		dvp-supply = <&dovdd_1v8>;
 		flash0-supply = <&vcc_flash>;
-		flash1-supply = <&vcc_lan>;
 		gpio30-supply = <&vcc_io>;
 		gpio1830-supply = <&vcc_io>;
 		lcdc-supply = <&vcc_io>;
@@ -225,7 +223,6 @@
 	clock_in_out = "input";
 	pinctrl-names = "default";
 	pinctrl-0 = <&rgmii_pins>, <&phy_rst>, <&phy_pmeb>, <&phy_int>;
-	phy-supply = <&vcc_lan>;
 	phy-mode = "rgmii";
 	snps,reset-active-low;
 	snps,reset-delays-us = <0 10000 1000000>;
@@ -296,7 +293,7 @@
 		inl2-supply = <&vcc_sys>;
 		inl3-supply = <&vcc_20>;
 
-		regulators {
+		act8846_regs: regulators {
 			vcc_ddr: REG1 {
 				regulator-name = "vcc_ddr";
 				regulator-min-microvolt = <1200000>;
@@ -339,19 +336,20 @@
 				regulator-always-on;
 			};
 
-			vcca_18: REG7 {
+			REG7  {
 				regulator-name = "vcca_18";
 				regulator-min-microvolt = <1800000>;
 				regulator-max-microvolt = <1800000>;
 			};
 
-			vcca_33: REG8 {
+			REG8 {
 				regulator-name = "vcca_33";
 				regulator-min-microvolt = <3300000>;
 				regulator-max-microvolt = <3300000>;
+				regulator-always-on;
 			};
 
-			vcc_lan: REG9 {
+			REG9 {
 				regulator-name = "vcc_lan";
 				regulator-min-microvolt = <3300000>;
 				regulator-max-microvolt = <3300000>;
-- 
2.7.4



From ayaka at soulik.info  Wed Jul  6 11:23:00 2016
From: ayaka at soulik.info (Randy Li)
Date: Thu,  7 Jul 2016 02:23:00 +0800
Subject: [PATCH 3/3] ARM: dts: add rk3288-firefly-reload
In-Reply-To: <1467829380-16242-1-git-send-email-ayaka@soulik.info>
References: <3015470.z4L7UQJKa9@phil>
 <1467829380-16242-1-git-send-email-ayaka@soulik.info>
Message-ID: <1467829380-16242-4-git-send-email-ayaka@soulik.info>

The firefly rk3288 reload is a Rockchip rk3288 based develop board.

Signed-off-by: Randy Li <ayaka at soulik.info>
---
 arch/arm/boot/dts/Makefile                  |   1 +
 arch/arm/boot/dts/rk3288-firefly-reload.dts | 109 ++++++++++++++++++++++++++++
 2 files changed, 110 insertions(+)
 create mode 100644 arch/arm/boot/dts/rk3288-firefly-reload.dts

diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index 06b6c2d..19a6049 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -606,6 +606,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += \
 	rk3288-evb-rk808.dtb \
 	rk3288-firefly-beta.dtb \
 	rk3288-firefly.dtb \
+	rk3288-firefly-reload.dtb \
 	rk3288-miqi.dtb \
 	rk3288-popmetal.dtb \
 	rk3288-r89.dtb \
diff --git a/arch/arm/boot/dts/rk3288-firefly-reload.dts b/arch/arm/boot/dts/rk3288-firefly-reload.dts
new file mode 100644
index 0000000..9d02509
--- /dev/null
+++ b/arch/arm/boot/dts/rk3288-firefly-reload.dts
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2016 Randy Li <ayaka at soulik.info>
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of the
+ *     License, or (at your option) any later version.
+ *
+ *     This file 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.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/dts-v1/;
+#include "rk3288-firefly.dtsi"
+
+/ {
+	model = "Firefly-RK3288-reload";
+	compatible = "firefly,firefly-rk3288-reload", "rockchip,rk3288";
+
+};
+
+&leds {
+	power {
+		gpios = <&gpio8 2 GPIO_ACTIVE_LOW>;
+		label = "firefly:blue:power";
+		linux,default-trigger = "default-on";
+		pinctrl-names = "default";
+		pinctrl-0 = <&power_led>;
+	};
+};
+
+&gmac {
+	phy-supply = <&vcc_lan>;
+};
+
+&pinctrl {
+	act8846 {
+		pmic_vsel: pmic-vsel {
+			rockchip,pins = <7 14 RK_FUNC_GPIO &pcfg_output_low>;
+		};
+	};
+
+	ir {
+		ir_int: ir-int {
+			rockchip,pins = <7 0 RK_FUNC_GPIO &pcfg_pull_up>;
+		};
+	};
+
+};
+
+&pwm1 {
+	status = "disabled";
+};
+
+&io_domains {
+		audio-supply = <&vcca_33>;
+		flash1-supply = <&vcc_lan>;
+};
+
+&act8846_regs {
+	vcca_33: REG7 {
+		regulator-name = "vcca_33";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+	};
+
+	vcc_lan: REG8 {
+		regulator-name = "vcc_lan";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+	};
+
+	vccio_pmu: REG9 {
+		regulator-name = "vccio_pmu";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-always-on;
+	};
+};
-- 
2.7.4



From dianders at chromium.org  Wed Jul  6 11:30:59 2016
From: dianders at chromium.org (Doug Anderson)
Date: Wed, 6 Jul 2016 11:30:59 -0700
Subject: [PATCH] regulator: pwm: Fix regulator ramp delay for continuous
 mode
In-Reply-To: <20160702075939.GD6247@sirena.org.uk>
References: <1467089591-7631-1-git-send-email-dianders@chromium.org>
 <20160701080614.GA9196@sirena.org.uk>
 <CAD=FV=WKR_Fq6o34886RCY-T-6AKdRqFz9H1aTiRq7Zu=o8TWA@mail.gmail.com>
 <20160702075939.GD6247@sirena.org.uk>
Message-ID: <CAD=FV=W2JDP3EAdz9CWUKbcoCz34Mm7oCF2QCD4W45tyBOaP+A@mail.gmail.com>

Mark,

On Sat, Jul 2, 2016 at 12:59 AM, Mark Brown <broonie at kernel.org> wrote:
> On Fri, Jul 01, 2016 at 01:17:48PM -0700, Doug Anderson wrote:
>
>> Anyway, let me know if you want me to post the untested, rebased
>> patch...  Personally I'd prefer to wait for Boris's patches and the
>> land the tested version.
>
> We've got some boards in kernelci.org using PWM regulators I think, or
> perhaps someone else can test.  It does seem like a real fix so...

OK, post coming up then.  Sorry for the delay--I was on holiday the
last two days.

Note: which boards are you thinking would test this?  My grep showed
zero boards using regulator-ramp-delay with PWM regulator.  While this
is an important fix for anyone using the ramp delay, I'm not convinced
that anyone in mainline is using it.

-Doug


From dianders at chromium.org  Wed Jul  6 11:31:45 2016
From: dianders at chromium.org (Doug Anderson)
Date: Wed, 6 Jul 2016 11:31:45 -0700
Subject: [PATCH] regulator: pwm: Fix regulator ramp delay for continuous
 mode
In-Reply-To: <20160706142719.73713123@bbrezillon>
References: <1467089591-7631-1-git-send-email-dianders@chromium.org>
 <20160706142719.73713123@bbrezillon>
Message-ID: <CAD=FV=W7kjMsXR3aRogCpEZWgvt2UZ7rX0B2Zg9yZOW7oGwepw@mail.gmail.com>

Boris,

On Wed, Jul 6, 2016 at 5:27 AM, Boris Brezillon
<boris.brezillon at free-electrons.com> wrote:
> On Mon, 27 Jun 2016 21:53:11 -0700
> Douglas Anderson <dianders at chromium.org> wrote:
>
>> The original commit adding support for continuous voltage mode didn't
>> handle the regulator ramp delay properly.  It treated the delay as a
>> fixed delay in uS despite the property being defined as uV / uS.  Let's
>> adjust it.  Luckily there appear to be no users of this ramp delay for
>> PWM regulators (as per grepping through device trees in linuxnext).
>>
>> Note also that the upper bound of usleep_range probably shouldn't be a
>> full 1 ms longer than the lower bound since I've seen plenty of hardware
>> with a ramp rate of ~5000 uS / uV and for small jumps the total delays
>> are in the tens of uS.  1000 is way too much.  We'll try to be dynamic
>> and use 10%
>
> I realize I may have introduced another bug when adding the
> ->enable()/disable() methods: we are only waiting for the ramp up delay
> when changing the voltage, but it should probably be applied when
> enabling the PWM, and conditionally applied when changing the voltage
> only if the PWM is enabled.

I'll certainly let Mark comment here, but:

* For enabling the PWM, I think you want want "regulator-enable-ramp-delay".

* Right, we probably shouldn't be delaying if the regulator is off.
I'll add that to my patch.

-Doug


From dianders at chromium.org  Wed Jul  6 11:42:01 2016
From: dianders at chromium.org (Douglas Anderson)
Date: Wed,  6 Jul 2016 11:42:01 -0700
Subject: [PATCH v2] regulator: pwm: Fix regulator ramp delay for continuous
 mode
Message-ID: <1467830521-15300-1-git-send-email-dianders@chromium.org>

The original commit adding support for continuous voltage mode didn't
handle the regulator ramp delay properly.  It treated the delay as a
fixed delay in uS despite the property being defined as uV / uS.  Let's
adjust it.  Luckily there appear to be no users of this ramp delay for
PWM regulators (as per grepping through device trees in linuxnext).

Note also that the upper bound of usleep_range probably shouldn't be a
full 1 ms longer than the lower bound since I've seen plenty of hardware
with a ramp rate of ~5000 uS / uV and for small jumps the total delays
are in the tens of uS.  1000 is way too much.  We'll try to be dynamic
and use 10%.

NOTE: This commit doesn't add support for regulator-enable-ramp-delay.
That could be done in a future patch when someone has a user of that
featre.

Though this patch is shows as "fixing" a bug, there are no actual known
users of continuous mode PWM regulator w/ ramp delay in mainline and so
this likely won't have any effect on anyone unless they are working
out-of-tree with private patches.  For anyone in this state, it is
highly encouraged to also pick Boris Brezillon's WIP patches to get
yourself a reliable and glitch-free regulator.

Fixes: 4773be185a0f ("regulator: pwm-regulator: Add support for continuous-voltage")
Signed-off-by: Douglas Anderson <dianders at chromium.org>
---
Note: I don't have a board that works against mainline and can use the
PWM regulator in continuous mode without Boris's patches.  That means
that this patch has only been compile tested.  The previous version of
the patches (atop Boris's changes) was tested more thoroughly.  YMMV.

Changes in v2:
- No longer atop Boris PWM regulator fixes (Mark).
- Don't delay if the regulator is not enabled (Boris).

 drivers/regulator/pwm-regulator.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/drivers/regulator/pwm-regulator.c b/drivers/regulator/pwm-regulator.c
index ab3cc0235843..75d9f9b1ad62 100644
--- a/drivers/regulator/pwm-regulator.c
+++ b/drivers/regulator/pwm-regulator.c
@@ -132,6 +132,7 @@ static int pwm_regulator_set_voltage(struct regulator_dev *rdev,
 	unsigned int duty_pulse;
 	u64 req_period;
 	u32 rem;
+	int old_uV = pwm_regulator_get_voltage(rdev);
 	int ret;
 
 	pwm_get_args(drvdata->pwm, &pargs);
@@ -161,8 +162,12 @@ static int pwm_regulator_set_voltage(struct regulator_dev *rdev,
 
 	drvdata->volt_uV = min_uV;
 
-	/* Delay required by PWM regulator to settle to the new voltage */
-	usleep_range(ramp_delay, ramp_delay + 1000);
+	if ((ramp_delay == 0) || !pwm_regulator_is_enabled(rdev))
+		return 0;
+
+	/* Ramp delay is in uV/uS. Adjust to uS and delay */
+	ramp_delay = DIV_ROUND_UP(abs(min_uV - old_uV), ramp_delay);
+	usleep_range(ramp_delay, ramp_delay + DIV_ROUND_UP(ramp_delay, 10));
 
 	return 0;
 }
-- 
2.8.0.rc3.226.g39d4020



From dianders at chromium.org  Wed Jul  6 12:47:44 2016
From: dianders at chromium.org (Doug Anderson)
Date: Wed, 6 Jul 2016 12:47:44 -0700
Subject: [PATCH v3] arm64: dts: rockchip: add the power domain node for
 rk3399
In-Reply-To: <1904131.irFlfKsH8m@phil>
References: <1467635644-16798-1-git-send-email-wxt@rock-chips.com>
 <1904131.irFlfKsH8m@phil>
Message-ID: <CAD=FV=W8-48ODWJN=4M54nYBAzH_Bproc13kKakStL_-aLUHcA@mail.gmail.com>

Hi,

On Mon, Jul 4, 2016 at 5:24 PM, Heiko Stuebner <heiko at sntech.de> wrote:
> Am Montag, 4. Juli 2016, 20:34:04 schrieb Caesar Wang:
>> From: Elaine Zhang <zhangqing at rock-chips.com>
>>
>> In order to meet low power requirements, a power management unit (PMU) is
>> designed for controlling power resources in RK3399. The RK3399 PMU is
>> dedicated for managing the power of the whole chip.
>>
>> 1. add pd node for RK3399 Soc
>> 2. create power domain tree
>> 3. add qos node for domain
>>
>> From the DT/binds and driver can get more detail information:
>> The driver:
>>         drivers/soc/rockchip/pm_domains.c
>> The document:
>>         Documentation/devicetree/bindings/soc/rockchip/power_domain.txt
>>
>> Note:
>> As the TRM lists many voltage domains and power domains, then this patch
>> adds some domains for driver. Due to some domains
>> (e.g. emmc, usb, core)...We can't turned off it on
>> bootup, or says some device driver can't handle the power domain enough.
>> Maybe We will add more other domains in the future or later.
>
> I talked with Caesar about that today and for example the gmac domain seems
> to cause problems right now when only using the generic power-domain
> handling the kernel provides on probe and remove.
>
> So while this gets investigated, I'm probably ok with only using the stable
> power-domains, but will give the us-people a chance to also respond on
> wednesday :-) .

Sure, that's fine with me.  Walk before we run.  ;)

-Doug


From dianders at chromium.org  Wed Jul  6 13:20:32 2016
From: dianders at chromium.org (Doug Anderson)
Date: Wed, 6 Jul 2016 13:20:32 -0700
Subject: [PATCH v3] arm64: dts: rockchip: add the power domain node for
 rk3399
In-Reply-To: <1467635644-16798-1-git-send-email-wxt@rock-chips.com>
References: <1467635644-16798-1-git-send-email-wxt@rock-chips.com>
Message-ID: <CAD=FV=VOzqkxH=XxnHadtYM_g7h1qS140uOwC=KXDwSeSET3gQ@mail.gmail.com>

Hi,

On Mon, Jul 4, 2016 at 5:34 AM, Caesar Wang <wxt at rock-chips.com> wrote:
> From: Elaine Zhang <zhangqing at rock-chips.com>
>
> In order to meet low power requirements, a power management unit (PMU) is
> designed for controlling power resources in RK3399. The RK3399 PMU is
> dedicated for managing the power of the whole chip.
>
> 1. add pd node for RK3399 Soc
> 2. create power domain tree
> 3. add qos node for domain
>
> From the DT/binds and driver can get more detail information:
> The driver:
>         drivers/soc/rockchip/pm_domains.c
> The document:
>         Documentation/devicetree/bindings/soc/rockchip/power_domain.txt
>
> Note:
> As the TRM lists many voltage domains and power domains, then this patch
> adds some domains for driver. Due to some domains
> (e.g. emmc, usb, core)...We can't turned off it on
> bootup, or says some device driver can't handle the power domain enough.
> Maybe We will add more other domains in the future or later.
>
> Signed-off-by: Elaine Zhang <zhangqing at rock-chips.com>
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
> Cc: linux-arm-kernel at lists.infradead.org
> Cc: linux-rockchip at lists.infradead.org
> Cc: Heiko Stuebner <heiko at sntech.de>
>
> ---
>
> Changes in v3:
> - As some commnets on https://patchwork.kernel.org/patch/9209205/
> - according to the alphabetically sort
> - %s/RK3399_PD_VOP/RK3399_PD_VOPL, %s/RK3399_PD_IE/RK3399_PD_IEP
>
> Changes in v2:
> - As Doug/Heiko commnets on https://patchwork.kernel.org/patch/9206415/.
>   drop the debugfs-dump and Add the comments for alphabetical order.
>
>  arch/arm64/boot/dts/rockchip/rk3399.dtsi | 179 +++++++++++++++++++++++++++++++
>  1 file changed, 179 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> index a6dd623..4559c04 100644
> --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> @@ -45,6 +45,7 @@
>  #include <dt-bindings/interrupt-controller/arm-gic.h>
>  #include <dt-bindings/interrupt-controller/irq.h>
>  #include <dt-bindings/pinctrl/rockchip.h>
> +#include <dt-bindings/power/rk3399-power.h>
>  #include <dt-bindings/thermal/thermal.h>
>
>  / {
> @@ -594,6 +595,184 @@
>                 status = "disabled";
>         };
>
> +       qos_hdcp: qos_hdcp at ffa90000 {
> +               compatible = "syscon";
> +               reg = <0x0 0xffa90000 0x0 0x20>;
> +       };
> +
> +       qos_iep: qos_iep at ffa98000 {
> +               compatible = "syscon";
> +               reg = <0x0 0xffa98000 0x0 0x20>;
> +       };
> +
> +       qos_isp0_m0: qos_isp0_m0 at ffaa0000 {
> +               compatible = "syscon";
> +               reg = <0x0 0xffaa0000 0x0 0x20>;
> +       };
> +
> +       qos_isp0_m1: qos_isp0_m1 at ffaa0080 {
> +               compatible = "syscon";
> +               reg = <0x0 0xffaa0080 0x0 0x20>;
> +       };
> +
> +       qos_isp1_m0: qos_isp1_m0 at ffaa8000 {
> +               compatible = "syscon";
> +               reg = <0x0 0xffaa8000 0x0 0x20>;
> +       };
> +
> +       qos_isp1_m1: qos_isp1_m1 at ffaa8080 {
> +               compatible = "syscon";
> +               reg = <0x0 0xffaa8080 0x0 0x20>;
> +       };
> +
> +       qos_rga_r: qos_rga_r at ffab0000 {
> +               compatible = "syscon";
> +               reg = <0x0 0xffab0000 0x0 0x20>;
> +       };
> +
> +       qos_rga_w: qos_rga_w at ffab0080 {
> +               compatible = "syscon";
> +               reg = <0x0 0xffab0080 0x0 0x20>;
> +       };
> +
> +       qos_video_m0: qos_video_m0 at ffab8000 {
> +               compatible = "syscon";
> +               reg = <0x0 0xffab8000 0x0 0x20>;
> +       };
> +
> +       qos_video_m1_r: qos_video_m1_r at ffac0000 {
> +               compatible = "syscon";
> +               reg = <0x0 0xffac0000 0x0 0x20>;
> +       };
> +
> +       qos_video_m1_w: qos_video_m1_w at ffac0080 {
> +               compatible = "syscon";
> +               reg = <0x0 0xffac0080 0x0 0x20>;
> +       };
> +
> +       qos_vop_big_r: qos_vop_big_r at ffac8000 {
> +               compatible = "syscon";
> +               reg = <0x0 0xffac8000 0x0 0x20>;
> +       };
> +
> +       qos_vop_big_w: qos_vop_big_w at ffac8080 {
> +               compatible = "syscon";
> +               reg = <0x0 0xffac8080 0x0 0x20>;
> +       };
> +
> +       qos_vop_little: qos_vop_little at ffad0000 {
> +               compatible = "syscon";
> +               reg = <0x0 0xffad0000 0x0 0x20>;
> +       };
> +
> +       qos_gpu: qos_gpu at ffae0000 {
> +               compatible = "syscon";
> +               reg = <0x0 0xffae0000 0x0 0x20>;
> +       };
> +
> +       pmu: power-management at ff310000 {
> +               compatible = "rockchip,rk3399-pmu", "syscon", "simple-mfd";
> +               reg = <0x0 0xff310000 0x0 0x1000>;
> +
> +               /*
> +                * Note: RK3399 supports 6 voltage domains including VD_CORE_L,
> +                * VD_CORE_B, VD_CENTER, VD_GPU, VD_LOGIC and VD_PMU.
> +                * Some of the power domains are grouped together for every
> +                * voltage domain.
> +                * The detail contents as below.
> +                */
> +               power: power-controller {
> +                       status = "okay";

In an offline conversation, Heiko pointed out to me that this 'status
= "okay";' is not needed...

> +                       compatible = "rockchip,rk3399-power-controller";
> +                       #power-domain-cells = <1>;
> +                       #address-cells = <1>;
> +                       #size-cells = <0>;
> +
> +                       /* These power domains are grouped by VD_CENTER */
> +                       pd_iep at RK3399_PD_IEP {
> +                               reg = <RK3399_PD_IEP>;
> +                               clocks = <&cru ACLK_IEP>,
> +                                        <&cru HCLK_IEP>;
> +                               pm_qos = <&qos_iep>;
> +                       };
> +                       pd_vcodec at RK3399_PD_VCODEC {
> +                               reg = <RK3399_PD_VCODEC>;
> +                               clocks = <&cru ACLK_VCODEC>,
> +                                        <&cru HCLK_VCODEC>;
> +                               pm_qos = <&qos_video_m0>;
> +                       };
> +                       pd_vdu at RK3399_PD_VDU {
> +                               reg = <RK3399_PD_VDU>;
> +                               clocks = <&cru ACLK_VDU>,
> +                                        <&cru HCLK_VDU>;
> +                               pm_qos = <&qos_video_m1_r>,
> +                                        <&qos_video_m1_w>;
> +                       };
> +                       pd_rga at RK3399_PD_RGA {
> +                               reg = <RK3399_PD_RGA>;
> +                               clocks = <&cru ACLK_RGA>,
> +                                        <&cru HCLK_RGA>;
> +                               pm_qos = <&qos_rga_r>,
> +                                        <&qos_rga_w>;
> +                       };

rga is still sorted incorrectly.

> +
> +                       /* These power domains are grouped by VD_GPU */
> +                       pd_gpu at RK3399_PD_GPU {
> +                               reg = <RK3399_PD_GPU>;
> +                               clocks = <&cru ACLK_GPU>;
> +                               pm_qos = <&qos_gpu>;
> +                       };
> +
> +                       /* These power domains are grouped by VD_LOGIC */
> +                       pd_vio at RK3399_PD_VIO {
> +                               reg = <RK3399_PD_VIO>;
> +                               #address-cells = <1>;
> +                               #size-cells = <0>;
> +
> +                               pd_hdcp at RK3399_PD_HDCP {
> +                                       reg = <RK3399_PD_HDCP>;
> +                                       clocks = <&cru ACLK_HDCP>,
> +                                                <&cru HCLK_HDCP>,
> +                                                <&cru PCLK_HDCP>;
> +                                       pm_qos = <&qos_hdcp>;
> +                               };
> +                               pd_isp0 at RK3399_PD_ISP0 {
> +                                       reg = <RK3399_PD_ISP0>;
> +                                       clocks = <&cru ACLK_ISP0>,
> +                                                <&cru HCLK_ISP0>;
> +                                       pm_qos = <&qos_isp0_m0>,
> +                                                <&qos_isp0_m1>;
> +                               };
> +                               pd_isp1 at RK3399_PD_ISP1 {
> +                                       reg = <RK3399_PD_ISP1>;
> +                                       clocks = <&cru ACLK_ISP1>,
> +                                                <&cru HCLK_ISP1>;
> +                                       pm_qos = <&qos_isp1_m0>,
> +                                                <&qos_isp1_m1>;
> +                               };
> +                               pd_vo at RK3399_PD_VO {
> +                                       reg = <RK3399_PD_VO>;
> +                                       #address-cells = <1>;
> +                                       #size-cells = <0>;
> +
> +                                       pd_vopb at RK3399_PD_VOPB {
> +                                               reg = <RK3399_PD_VOPB>;
> +                                               clocks = <&cru ACLK_VOP0>,
> +                                                        <&cru HCLK_VOP0>;
> +                                               pm_qos = <&qos_vop_big_r>,
> +                                                        <&qos_vop_big_w>;
> +                                       };
> +                                       pd_vopl at RK3399_PD_VOPL {
> +                                               reg = <RK3399_PD_VOPL>;
> +                                               clocks = <&cru ACLK_VOP1>,
> +                                                        <&cru HCLK_VOP1>;
> +                                               pm_qos = <&qos_vop_little>;
> +                                       };
> +                               };
> +                       };
> +               };
> +       };
> +

I think Heiko said he'll handle the nits when applying.  ...so given that:

Reviewed-by: Douglas Anderson <dianders at chromium.org>
Tested-by: Douglas Anderson <dianders at chromium.org>


From groeck at google.com  Wed Jul  6 13:22:27 2016
From: groeck at google.com (Guenter Roeck)
Date: Wed, 6 Jul 2016 13:22:27 -0700
Subject: [v3 PATCH 1/5] extcon: Add Type-C and DP support
In-Reply-To: <CAGTfZH1ciu33btOAkwwA+8pUV=5-XDus-Q_wNiv_hwRoLK5dPw@mail.gmail.com>
References: <1466686264-6744-1-git-send-email-zyw@rock-chips.com>
 <1466686264-6744-2-git-send-email-zyw@rock-chips.com>
 <CAGTfZH1aKUQH2VH+biyh9pFfU93pV9HH3h5s9zO0rrvXJYCMdA@mail.gmail.com>
 <CABXOdTdHhWTSF6H2D5ubaZsu65dnGsof9NWAFNeWjCir=H91Ag@mail.gmail.com>
 <CAGTfZH1ciu33btOAkwwA+8pUV=5-XDus-Q_wNiv_hwRoLK5dPw@mail.gmail.com>
Message-ID: <CABXOdTc27oW3xyeBZbks2r19qZRNDDAVcdOVJWgMff+59vpBPg@mail.gmail.com>

Hi,

On Tue, Jun 28, 2016 at 6:40 PM, Chanwoo Choi <cwchoi00 at gmail.com> wrote:
> Hi Guenter,
>
> 2016? 6? 29? ???, Guenter Roeck<groeck at google.com>?? ??? ???:
>>
>> On Tue, Jun 28, 2016 at 5:26 AM, Chanwoo Choi <cwchoi00 at gmail.com> wrote:
>> > Hi Chris,
>> >
>> > I agree to add the new EXTCON_DISP_DP connector.
>> > But, other new definition should be discussed.
>> > - EXTCON_DISP_DP_ALT
>> > - EXTCON_TYPEC_POLARITY
>> > - EXTCON_TYPEC_PIN_ASSIGN
>> >
>> > I think that TYPEC_POLARITY and TYPEC_PIN_ASSING are not appropriate
>> > as the new external connector definition. These are the property or
>> > attribute of
>> > USB connector with C-type.
>> >
>> > Also, EXTCON_DISP_DP_ALT is not a new type of connector.
>> > It is just one of the mode for DP connector.
>> >
>> > As I knew, DP alternative mode use the USB connector with C-type.
>> > So, DP alternative mode can be expressed on following:
>> >  = EXTCON_DISP_DP + EXTCON_USB + some property of USB c-type
>> >
>>
>> Problem is that extcon doesn't support exchanging cable properties
>> between cable providers (extcon drivers) and consumers, other than
>> cable states. In order to exchange properties such as polarity and pin
>> assignments, we would need a separate infrastructure. But then the
>> question would be why to use extcon in the first place.
>>
>> If you have a solution for that puzzle, please let us know.
>>
>
> You're right.
> Current extcon don't support the cable properties.
> The requirement about cables properties occur such as USB ID and VBUS pin.
> So, I'll support the cable properties in extcon framework and send the
> patches within this week.
>

Did you have time to look into this ? Any idea if/when you will send
the patches ?

Thanks,
Guenter


From heiko at sntech.de  Wed Jul  6 13:56:34 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Wed, 06 Jul 2016 22:56:34 +0200
Subject: [PATCH v3] arm64: dts: rockchip: add the power domain node for
 rk3399
In-Reply-To: <1467635644-16798-1-git-send-email-wxt@rock-chips.com>
References: <1467635644-16798-1-git-send-email-wxt@rock-chips.com>
Message-ID: <4362000.pLfftZqEvu@phil>

Am Montag, 4. Juli 2016, 20:34:04 schrieb Caesar Wang:
> From: Elaine Zhang <zhangqing at rock-chips.com>
> 
> In order to meet low power requirements, a power management unit (PMU) is
> designed for controlling power resources in RK3399. The RK3399 PMU is
> dedicated for managing the power of the whole chip.
> 
> 1. add pd node for RK3399 Soc
> 2. create power domain tree
> 3. add qos node for domain
> 
> From the DT/binds and driver can get more detail information:
> The driver:
>         drivers/soc/rockchip/pm_domains.c
> The document:
>         Documentation/devicetree/bindings/soc/rockchip/power_domain.txt
> 
> Note:
> As the TRM lists many voltage domains and power domains, then this patch
> adds some domains for driver. Due to some domains
> (e.g. emmc, usb, core)...We can't turned off it on
> bootup, or says some device driver can't handle the power domain enough.
> Maybe We will add more other domains in the future or later.
> 
> Signed-off-by: Elaine Zhang <zhangqing at rock-chips.com>
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
> Cc: linux-arm-kernel at lists.infradead.org
> Cc: linux-rockchip at lists.infradead.org
> Cc: Heiko Stuebner <heiko at sntech.de>

applied to my dts64 branch after renaming the qos nodes, removing the status
property and moving the rga domain up a bit. Please double check:
https://git.kernel.org/cgit/linux/kernel/git/mmind/linux-rockchip.git/commit/?id=6d3cdbf1c219f9817290903e9d1e8de7462a7b54



From dianders at chromium.org  Wed Jul  6 14:04:42 2016
From: dianders at chromium.org (Doug Anderson)
Date: Wed, 6 Jul 2016 14:04:42 -0700
Subject: [PATCH v3] arm64: dts: rockchip: add the power domain node for
 rk3399
In-Reply-To: <4362000.pLfftZqEvu@phil>
References: <1467635644-16798-1-git-send-email-wxt@rock-chips.com>
 <4362000.pLfftZqEvu@phil>
Message-ID: <CAD=FV=WrSVL=eYn=0rY=yc8pPfuBrZ44kAa-D6AW=2V8CdoR5g@mail.gmail.com>

Heiko,

On Wed, Jul 6, 2016 at 1:56 PM, Heiko Stuebner <heiko at sntech.de> wrote:
> Am Montag, 4. Juli 2016, 20:34:04 schrieb Caesar Wang:
>> From: Elaine Zhang <zhangqing at rock-chips.com>
>>
>> In order to meet low power requirements, a power management unit (PMU) is
>> designed for controlling power resources in RK3399. The RK3399 PMU is
>> dedicated for managing the power of the whole chip.
>>
>> 1. add pd node for RK3399 Soc
>> 2. create power domain tree
>> 3. add qos node for domain
>>
>> From the DT/binds and driver can get more detail information:
>> The driver:
>>         drivers/soc/rockchip/pm_domains.c
>> The document:
>>         Documentation/devicetree/bindings/soc/rockchip/power_domain.txt
>>
>> Note:
>> As the TRM lists many voltage domains and power domains, then this patch
>> adds some domains for driver. Due to some domains
>> (e.g. emmc, usb, core)...We can't turned off it on
>> bootup, or says some device driver can't handle the power domain enough.
>> Maybe We will add more other domains in the future or later.
>>
>> Signed-off-by: Elaine Zhang <zhangqing at rock-chips.com>
>> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
>> Cc: linux-arm-kernel at lists.infradead.org
>> Cc: linux-rockchip at lists.infradead.org
>> Cc: Heiko Stuebner <heiko at sntech.de>
>
> applied to my dts64 branch after renaming the qos nodes, removing the status
> property and moving the rga domain up a bit. Please double check:
> https://git.kernel.org/cgit/linux/kernel/git/mmind/linux-rockchip.git/commit/?id=6d3cdbf1c219f9817290903e9d1e8de7462a7b54

This looks like the same as my attempt to do these fixes too.  ;)

-Doug


From mturquette at baylibre.com  Wed Jul  6 15:22:46 2016
From: mturquette at baylibre.com (Michael Turquette)
Date: Wed, 06 Jul 2016 15:22:46 -0700
Subject: [RESEND PATCH v5 4/5] clk: Kconfig: Name RK818 in Kconfig for
 COMMON_CLK_RK808
In-Reply-To: <1464850228-17244-4-git-send-email-w.egorov@phytec.de>
References: <1464850228-17244-1-git-send-email-w.egorov@phytec.de>
 <1464850228-17244-4-git-send-email-w.egorov@phytec.de>
Message-ID: <146784376682.73491.439491426208764255@resonance>

Quoting Wadim Egorov (2016-06-01 23:50:27)
> The RK808 and RK818 PMICs are using a similar register map.
> We can reuse the clk driver for the RK818 PMIC. So let's add
> the RK818 in the Kconfig description.
> 
> Signed-off-by: Wadim Egorov <w.egorov at phytec.de>
> Acked-by: Stephen Boyd <sboyd at codeaurora.org>

Applied.

Regards,
Mike

> ---
>  drivers/clk/Kconfig | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
> index 53ddba2..87b9fd2 100644
> --- a/drivers/clk/Kconfig
> +++ b/drivers/clk/Kconfig
> @@ -49,10 +49,10 @@ config COMMON_CLK_MAX77802
>           This driver supports Maxim 77802 crystal oscillator clock.
>  
>  config COMMON_CLK_RK808
> -       tristate "Clock driver for RK808"
> +       tristate "Clock driver for RK808/RK818"
>         depends on MFD_RK808
>         ---help---
> -         This driver supports RK808 crystal oscillator clock. These
> +         This driver supports RK808 and RK818 crystal oscillator clock. These
>           multi-function devices have two fixed-rate oscillators,
>           clocked at 32KHz each. Clkout1 is always on, Clkout2 can off
>           by control register.
> -- 
> 1.9.1
> 


From dianders at chromium.org  Wed Jul  6 15:41:20 2016
From: dianders at chromium.org (Doug Anderson)
Date: Wed, 6 Jul 2016 15:41:20 -0700
Subject: [RFC PATCH 2/3] clk: adjust clocks to their requested rate after
 parent changes
In-Reply-To: <1601887.i7XxTCUQZu@phil>
References: <1462206982-10444-1-git-send-email-heiko@sntech.de>
 <1462206982-10444-3-git-send-email-heiko@sntech.de>
 <577B6162.3020609@rock-chips.com> <1601887.i7XxTCUQZu@phil>
Message-ID: <CAD=FV=WQeK35+zLpdaLqn5ShFRrUBx1r2LzqS=_THsWqy6iV6g@mail.gmail.com>

Hi,

On Tue, Jul 5, 2016 at 3:24 PM, Heiko Stuebner <heiko at sntech.de> wrote:
>> I tests found a problem, about set the freq order.
>> e.p:
>> [VPLL]
>>
>>      ------ [div] ----- dclk_vop
>> If I change VPLL freq 148.5M to 594M, dclk_vop freq will changed as:
>> 148.5M->24M->594M->1485.5M.
>> But we not hope the dclk_vop have a high freq,it will make the system
>> crash or make vop not work well.
>>
>> So if the VPLL is improve the freq, we need to set dclk_vop div first,
>> and than set VPLL freq.
>> If VPLL is reduce the freq, we need to set vpll first,and set dclk_vop
>> div.
>>
>> This is just a example,for all change parent freq, we need follow this
>> operation.
>> Do you have a better idea for this problem?
>
> In general it seems my simplicistic approach only really works for really
> simple clock-setups and thus is likely not really usable for general things.
>
> For the VPLL on the rk3399 we were discussion a different approach in [0],
> as VPLL usage (aka which vop gets to control it) is likely to complicated to
> have this done in the clock-framework-
>
> Doug wanted to take a look and add some thoughts and I guess he'll just do
> that after the 4th of july celebrations.

Right.  IMHO you should do something to _really_ make sure that one of
the VOPs is parented on either GPLL or CPLL and then let the other VOP
be in control of VPLL.  How we actually do that is up for debate.  I
still have it on my list of things to do to look at Heiko's response
to my previous proposal, but given that I'm currently backlogged from
the two day holiday and that I'm only a few days away from a week long
vacation (and subsequent backlog), I'm not 100% sure I'll get to it
soon.  :(  I'll do my best, though...

In addition to the link Heiko gave, you could also look at
<https://chromium-review.googlesource.com/#/c/354165/>.  That's also
not fully baked but is another way to possibly tackle the problem.

In general if there is only one clock tree under VPLL then you
shouldn't need to worry too much about things getting out of whack.


-Doug


From dianders at chromium.org  Wed Jul  6 16:01:42 2016
From: dianders at chromium.org (Doug Anderson)
Date: Wed, 6 Jul 2016 16:01:42 -0700
Subject: [RFC PATCH 2/3] clk: adjust clocks to their requested rate after
 parent changes
In-Reply-To: <577C6152.4010702@rock-chips.com>
References: <1462206982-10444-1-git-send-email-heiko@sntech.de>
 <1462206982-10444-3-git-send-email-heiko@sntech.de>
 <577B6162.3020609@rock-chips.com>
 <1601887.i7XxTCUQZu@phil> <577C6152.4010702@rock-chips.com>
Message-ID: <CAD=FV=WAZ125yjG0rAEodSL3LLdBAEx24k+uWYbYYqNCpnYByQ@mail.gmail.com>

Hi,

On Tue, Jul 5, 2016 at 6:39 PM, Elaine Zhang <zhangqing at rock-chips.com> wrote:
> OK, Regardless of the VPLL and vop.
> The current software of clock, there are some problems.
> e.p:
> [GPLL]
>        ------ [div] ----- aclk_perilp
> If I set GPLL 1200M use "assigned-clock-rate".My be the default div of
> aclk_perilp is 2, GPLL default freq is 600M.
> If I set GPLL first,the aclk_perilp freq will changed as
> 300M->600M->300M.But aclk_perilp 600M is a unsafety.It will make the system
> crash.
> Aclk_perilp is sizeoff at 300M.It not allowed over frequency.
>
> So if I init the PLL freq use "assigned-clock-rate", I need to list it's
> child clk, make sure it's child clk div is enough,make sure the child clk
> freq is below the  sizeoff freq.
>
> Do you have a better idea for this problem?

As I understand it, this is a problem that the CCF has had for a long
time.  I'll probably get shot if our firmware guy ever reads this, but
I could suggest that one sane way to "solve" this is to say that
firmware should prepare the clock tree in such a way that the kernel
doesn't happen to run into this issue.  Honestly I think that's why
we've never happened to run into this before.  ...but even I think
that's pretty ugly.

Another very ugly (and non-landable) way to solve this is to do this
in device tree:

* Set GPLL to something known slow, like 300M
* Set aclk_perilp to 300M / 4 = 75M
* Set GPLL to 1200M
* Poof, aclk_perilp is now magically the rate you want.


Presumably one nice way to "solve" this cleanly is to somehow add the
max clock rate into the code and adjust the divider (or parent) of a
clock to always make sure that it was under the max.  I'm not sure if
you could do this somehow using PRE_RATE_CHANGE notifiers or if you'd
need to touch the CCF itself.  I suspect the later.


-Doug


From briannorris at chromium.org  Wed Jul  6 17:12:55 2016
From: briannorris at chromium.org (Brian Norris)
Date: Wed, 6 Jul 2016 17:12:55 -0700
Subject: [PATCH v6 2/2] PCI: Rockchip: Add Rockchip PCIe controller support
In-Reply-To: <1467789398-13501-2-git-send-email-shawn.lin@rock-chips.com>
References: <1467789398-13501-1-git-send-email-shawn.lin@rock-chips.com>
 <1467789398-13501-2-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <20160707001254.GA100467@google.com>

Hi Shawn,

On Wed, Jul 06, 2016 at 03:16:38PM +0800, Shawn Lin wrote:
> This patch adds Rockchip PCIe controller support found
> on RK3399 Soc platform.
> 
> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
> 
> ---
> 
> Changes in v6:
> - use "depends on PCI_MSI_IRQ_DOMAIN" suggested by Arnd
> 
> Changes in v5:
> - handle multiple pending INTx at the same time
>   suggested by Marc
> 
> Changes in v4:
> - address the comments from Brain
> 
> Changes in v3:
> - remove header file
> - remove struct msi_controller and move most of variables
>   of rockchip_pcie_port to become the local ones.
> - allow legacy int even if enabling MSI
> - drop regulator set voltage operation suggested by Doug
> 
> Changes in v2:
> - remove phy related stuff and call phy API
> - add new head file and define lots of macro to make
>   the code more readable
> - remove lots msi related code suggested by Marc
> - add IO window address translation
> - init_port and parse_dt reconstruction suggested by Bharat
> - improve wr_own_conf suggested by Arnd
> - make pcie as an interrupt controller and fix wrong int handler
>   suggested by Marc
> - remove PCI_PROBE_ONLY suggested by Lorenzo
> 
>  drivers/pci/host/Kconfig         |   11 +
>  drivers/pci/host/Makefile        |    1 +
>  drivers/pci/host/pcie-rockchip.c | 1213 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 1225 insertions(+)
>  create mode 100644 drivers/pci/host/pcie-rockchip.c
> 

[...]

> diff --git a/drivers/pci/host/pcie-rockchip.c b/drivers/pci/host/pcie-rockchip.c
> new file mode 100644
> index 0000000..7996556
> --- /dev/null
> +++ b/drivers/pci/host/pcie-rockchip.c
> @@ -0,0 +1,1213 @@

[...]

> +/**
> + * rockchip_pcie_init_port - Initialize hardware
> + * @port: PCIe port information
> + */
> +static int rockchip_pcie_init_port(struct rockchip_pcie_port *port)
> +{
> +	int err;
> +	u32 status;
> +	unsigned long timeout;
> +
> +	gpiod_set_value(port->ep_gpio, 0);
> +
> +	err = phy_init(port->phy);
> +	if (err < 0) {
> +		dev_err(port->dev, "fail to init phy, err %d\n", err);
> +		return err;
> +	}
> +
> +	err = reset_control_assert(port->core_rst);
> +	if (err) {
> +		dev_err(port->dev, "assert core_rst err %d\n", err);
> +		return err;
> +	}
> +
> +	err = reset_control_assert(port->mgmt_rst);
> +	if (err) {
> +		dev_err(port->dev, "assert mgmt_rst err %d\n", err);
> +		return err;
> +	}
> +
> +	err = reset_control_assert(port->mgmt_sticky_rst);
> +	if (err) {
> +		dev_err(port->dev, "assert mgmt_sticky_rst err %d\n", err);
> +		return err;
> +	}
> +
> +	err = reset_control_assert(port->pipe_rst);
> +	if (err) {
> +		dev_err(port->dev, "assert pipe_rst err %d\n", err);
> +		return err;
> +	}
> +
> +	pcie_write(port,
> +		   HIWORD_UPDATE(PCIE_CLIENT_CONF_ENABLE,
> +				 PCIE_CLIENT_CONF_ENABLE_MASK,
> +				 PCIE_CLIENT_CONF_ENABLE_SHIFT) |
> +		   HIWORD_UPDATE(PCIE_CLIENT_CONF_LANE_NUM(port->lanes),
> +				 PCIE_CLIENT_CONF_LANE_NUM_MASK,
> +				 PCIE_CLIENT_CONF_LANE_NUM_SHIFT) |
> +		   HIWORD_UPDATE(PCIE_CLIENT_MODE_RC,
> +				 PCIE_CLIENT_MODE_MASK,
> +				 PCIE_CLIENT_MODE_SHIFT) |
> +		   HIWORD_UPDATE(PCIE_CLIENT_ARI_ENABLE,
> +				 PCIE_CLIENT_ARI_ENABLE_MASK,
> +				 PCIE_CLIENT_ARI_ENABLE_SHIFT) |
> +		   HIWORD_UPDATE(PCIE_CLIENT_GEN_SEL_2,
> +				 PCIE_CLIENT_GEN_SEL_MASK,
> +				 PCIE_CLIENT_GEN_SEL_SHIFT),
> +		   PCIE_CLIENT_BASE);
> +
> +	err = phy_power_on(port->phy);
> +	if (err) {
> +		dev_err(port->dev, "fail to power on phy, err %d\n", err);
> +		return err;
> +	}
> +
> +	err = reset_control_deassert(port->core_rst);
> +	if (err) {
> +		dev_err(port->dev, "deassert core_rst err %d\n", err);
> +		return err;
> +	}
> +
> +	err = reset_control_deassert(port->mgmt_rst);
> +	if (err) {
> +		dev_err(port->dev, "deassert mgmt_rst err %d\n", err);
> +		return err;
> +	}
> +
> +	err = reset_control_deassert(port->mgmt_sticky_rst);
> +	if (err) {
> +		dev_err(port->dev, "deassert mgmt_sticky_rst err %d\n", err);
> +		return err;
> +	}
> +
> +	err = reset_control_deassert(port->pipe_rst);
> +	if (err) {
> +		dev_err(port->dev, "deassert pipe_rst err %d\n", err);
> +		return err;
> +	}
> +
> +	/* Enable Gen1 training */
> +	pcie_write(port,
> +		   HIWORD_UPDATE(PCIE_CLIENT_LINK_TRAIN_ENABLE,
> +				 PCIE_CLIENT_LINK_TRAIN_MASK,
> +				 PCIE_CLIENT_LINK_TRAIN_SHIFT),
> +		   PCIE_CLIENT_BASE);
> +
> +	gpiod_set_value(port->ep_gpio, 1);
> +
> +	/* 500ms timeout value should be enough for gen1/2 taining */
> +	timeout = jiffies + msecs_to_jiffies(500);
> +
> +	for (;;) {
> +		status = pcie_read(port, PCIE_CLIENT_BASIC_STATUS1);
> +		if (((status >> PCIE_CLIENT_LINK_STATUS_SHIFT) &
> +		      PCIE_CLIENT_LINK_STATUS_MASK) ==
> +		      PCIE_CLIENT_LINK_STATUS_UP) {
> +			dev_dbg(port->dev, "pcie link training gen1 pass!\n");
> +			break;
> +		}
> +
> +		msleep(20);
> +
> +		if (!time_before(jiffies, timeout))
> +			break;
> +
> +	}
> +	status = pcie_read(port, PCIE_CLIENT_BASIC_STATUS1);
> +	err = (((status >> PCIE_CLIENT_LINK_STATUS_SHIFT) &
> +		 PCIE_CLIENT_LINK_STATUS_MASK) ==
> +		 PCIE_CLIENT_LINK_STATUS_UP) ? 0 : -ETIMEDOUT;
> +	if (err) {
> +		dev_err(port->dev, "pcie link training gen1 timeout!\n");
> +		return err;
> +	}

You're doing an extra PCIe read, even in the "success" case (you only
really need to double-check the timeout case, to be sure you're not
declaring timeout prematurely). That's probably fine, but you could
maybe avoid this more easily if you factored your timeout loop into a
separate function, and just 'return 0' from the loop on success, and
only 'break' to double-check for timeouts.

> +
> +	/*
> +	 * Enable retrain for gen2. This should be configured only after
> +	 * gen1 finished.
> +	 */
> +	status = pcie_read(port,
> +			   PCIE_RC_CONFIG_LCS + PCIE_RC_CONFIG_BASE);
> +	status |= PCIE_CORE_LCSR_RETRAIN_LINK;
> +	pcie_write(port, status,
> +		   PCIE_RC_CONFIG_LCS + PCIE_RC_CONFIG_BASE);
> +
> +	timeout = jiffies + msecs_to_jiffies(500);
> +	for (;;) {
> +		status = pcie_read(port, PCIE_CORE_CTRL_MGMT_BASE);
> +		if (((status >> PCIE_CORE_PL_CONF_SPEED_SHIFT) &
> +		      PCIE_CORE_PL_CONF_SPEED_MASK) ==
> +		      PCIE_CORE_PL_CONF_SPEED_5G) {
> +			dev_dbg(port->dev, "pcie link training gen2 pass!\n");
> +			break;
> +		}
> +
> +		msleep(20);
> +
> +		if (!time_before(jiffies, timeout))
> +			break;
> +	}
> +	status = pcie_read(port, PCIE_CORE_CTRL_MGMT_BASE);
> +	err = (((status >> PCIE_CORE_PL_CONF_SPEED_SHIFT) &
> +		PCIE_CORE_PL_CONF_SPEED_MASK) ==
> +		PCIE_CORE_PL_CONF_SPEED_5G) ? 0 : -ETIMEDOUT;
> +	if (err)
> +		dev_dbg(port->dev, "pcie link training gen2 timeout, fall back to gen1!\n");

Same here.

> +
> +	/* Check the final link with from negotiated lane counter from MGMT */
> +	status = pcie_read(port, PCIE_CORE_CTRL_MGMT_BASE);
> +	status =  0x1 << ((status >> PCIE_CORE_PL_CONF_LANE_SHIFT) &
> +			   PCIE_CORE_PL_CONF_LANE_MASK);
> +	dev_dbg(port->dev, "current link width is x%d\n", status);
> +
> +	pcie_write(port, ROCKCHIP_VENDOR_ID, PCIE_RC_CONFIG_BASE);
> +	pcie_write(port, PCI_CLASS_BRIDGE_PCI << PCIE_CORE_RC_CONF_SCC_SHIFT,
> +		   PCIE_RC_CONFIG_BASE + PCIE_RC_CONFIG_RID_CCR);
> +	pcie_write(port, 0x0, PCIE_CORE_CTRL_MGMT_BASE + PCIE_RC_BAR_CONF);
> +
> +	pcie_write(port, (RC_REGION_0_ADDR_TRANS_L + RC_REGION_0_PASS_BITS),
> +		   PCIE_CORE_AXI_CONF_BASE);
> +	pcie_write(port, RC_REGION_0_ADDR_TRANS_H,
> +		   PCIE_CORE_AXI_CONF_BASE + PCIE_CORE_OB_REGION_ADDR1);
> +	pcie_write(port, 0x0080000a,
> +		   PCIE_CORE_AXI_CONF_BASE + PCIE_CORE_OB_REGION_DESC0);
> +	pcie_write(port, 0x0,
> +		   PCIE_CORE_AXI_CONF_BASE + PCIE_CORE_OB_REGION_DESC1);
> +
> +	return 0;
> +}
> +
> +/**
> + * rockchip_pcie_parse_dt - Parse Device tree
> + * @port: PCIe port information
> + *
> + * Return: '0' on success and error value on failure
> + */
> +static int rockchip_pcie_parse_dt(struct rockchip_pcie_port *port)
> +{
> +	struct device *dev = port->dev;
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct device_node *node = dev->of_node;
> +	struct resource *regs;
> +	int irq;
> +	int err = -ENODEV;
> +
> +	regs = platform_get_resource_byname(pdev,
> +					    IORESOURCE_MEM,
> +					    "axi-base");
> +	if (!regs) {
> +		dev_err(dev, "missing axi-base property\n");
> +		return err;
> +	}
> +
> +	port->reg_base = devm_ioremap_resource(dev, regs);
> +	if (IS_ERR(port->reg_base))
> +		return PTR_ERR(port->reg_base);
> +
> +	regs = platform_get_resource_byname(pdev,
> +					    IORESOURCE_MEM,
> +					    "apb-base");
> +	if (!regs) {
> +		dev_err(dev, "missing apb-base property\n");
> +		return err;
> +	}
> +
> +	port->apb_base = devm_ioremap_resource(dev, regs);
> +	if (IS_ERR(port->apb_base))
> +		return PTR_ERR(port->apb_base);
> +
> +	port->phy = devm_phy_get(dev, "pcie-phy");
> +	if (IS_ERR(port->phy)) {
> +		if (PTR_ERR(port->phy) != -EPROBE_DEFER)
> +			dev_err(dev, "Missing phy\n");
> +		return PTR_ERR(port->phy);
> +	}
> +
> +	port->lanes = 1;
> +	err = of_property_read_u32(node, "num-lanes", &port->lanes);
> +	if (!err && ((port->lanes == 0) ||
> +		     (port->lanes == 3) ||
> +		     (port->lanes > 4))) {
> +		dev_warn(dev, "invalid num-lanes, default use one lane\n");
> +		port->lanes = 1;
> +	}
> +
> +	port->core_rst = devm_reset_control_get(dev, "core");
> +	if (IS_ERR(port->core_rst)) {
> +		if (PTR_ERR(port->core_rst) != -EPROBE_DEFER)
> +			dev_err(dev, "missing core rst property in node %s\n",
> +				node->name);
> +		return PTR_ERR(port->core_rst);
> +	}
> +
> +	port->mgmt_rst = devm_reset_control_get(dev, "mgmt");
> +	if (IS_ERR(port->mgmt_rst)) {
> +		if (PTR_ERR(port->mgmt_rst) != -EPROBE_DEFER)
> +			dev_err(dev, "missing mgmt rst property in node %s\n",
> +				node->name);
> +		return PTR_ERR(port->mgmt_rst);
> +	}
> +
> +	port->mgmt_sticky_rst = devm_reset_control_get(dev, "mgmt-sticky");
> +	if (IS_ERR(port->mgmt_sticky_rst)) {
> +		if (PTR_ERR(port->mgmt_sticky_rst) != -EPROBE_DEFER)
> +			dev_err(dev, "missing mgmt-sticky rst property in node %s\n",
> +				node->name);
> +		return PTR_ERR(port->mgmt_sticky_rst);
> +	}
> +
> +	port->pipe_rst = devm_reset_control_get(dev, "pipe");
> +	if (IS_ERR(port->pipe_rst)) {
> +		if (PTR_ERR(port->pipe_rst) != -EPROBE_DEFER)
> +			dev_err(dev, "missing pipe rst property in node %s\n",
> +				node->name);
> +		return PTR_ERR(port->pipe_rst);
> +	}
> +
> +	port->ep_gpio = devm_gpiod_get(dev, "ep", GPIOD_OUT_HIGH);
> +	if (IS_ERR(port->ep_gpio)) {
> +		dev_err(dev, "missing ep-gpios property in node %s\n",
> +			node->name);
> +		return PTR_ERR(port->ep_gpio);
> +	}
> +
> +	port->aclk_pcie = devm_clk_get(dev, "aclk");
> +	if (IS_ERR(port->aclk_pcie)) {
> +		dev_err(dev, "aclk clock not found.\n");
> +		return PTR_ERR(port->aclk_pcie);
> +	}
> +
> +	port->aclk_perf_pcie = devm_clk_get(dev, "aclk-perf");
> +	if (IS_ERR(port->aclk_perf_pcie)) {
> +		dev_err(dev, "aclk_perf clock not found.\n");
> +		return PTR_ERR(port->aclk_perf_pcie);
> +	}
> +
> +	port->hclk_pcie = devm_clk_get(dev, "hclk");
> +	if (IS_ERR(port->hclk_pcie)) {
> +		dev_err(dev, "hclk clock not found.\n");
> +		return PTR_ERR(port->hclk_pcie);
> +	}
> +
> +	port->clk_pcie_pm = devm_clk_get(dev, "pm");
> +	if (IS_ERR(port->clk_pcie_pm)) {
> +		dev_err(dev, "pm clock not found.\n");
> +		return PTR_ERR(port->clk_pcie_pm);
> +	}
> +
> +	irq = platform_get_irq_byname(pdev, "sys");
> +	if (irq < 0) {
> +		dev_err(dev, "missing pcie_sys IRQ resource\n");
> +		return -EINVAL;
> +	}
> +
> +	err = devm_request_irq(dev, irq, rockchip_pcie_subsys_irq_handler,
> +			       IRQF_SHARED, "pcie-sys", port);
> +	if (err) {
> +		dev_err(dev, "failed to request pcie subsystem irq\n");
> +		return err;
> +	}
> +
> +	irq = platform_get_irq_byname(pdev, "legacy");
> +	if (irq < 0) {
> +		dev_err(dev, "missing pcie_legacy IRQ resource\n");
> +		return -EINVAL;
> +	}
> +
> +	irq_set_chained_handler_and_data(irq,
> +					 rockchip_pcie_legacy_int_handler,
> +					 port);
> +
> +	irq = platform_get_irq_byname(pdev, "client");
> +	if (irq < 0) {
> +		dev_err(dev, "missing pcie-client IRQ resource\n");
> +		return -EINVAL;
> +	}
> +
> +	err = devm_request_irq(dev, irq, rockchip_pcie_client_irq_handler,
> +			       IRQF_SHARED, "pcie-client", port);
> +	if (err) {
> +		dev_err(dev, "failed to request pcie client irq\n");
> +		return err;
> +	}
> +
> +	port->vpcie3v3 = devm_regulator_get_optional(dev, "vpcie3v3");
> +	if (IS_ERR(port->vpcie3v3)) {
> +		if (PTR_ERR(port->vpcie3v3) == -EPROBE_DEFER)
> +			return -EPROBE_DEFER;
> +		dev_info(dev, "No vpcie3v3 regulator found.\n");
> +	}
> +
> +	port->vpcie1v8 = devm_regulator_get_optional(dev, "vpcie1v8");
> +	if (IS_ERR(port->vpcie1v8)) {
> +		if (PTR_ERR(port->vpcie1v8) == -EPROBE_DEFER)
> +			return -EPROBE_DEFER;
> +		dev_info(dev, "No vpcie1v8 regulator found.\n");
> +	}
> +
> +	port->vpcie0v9 = devm_regulator_get_optional(dev, "vpcie0v9");
> +	if (IS_ERR(port->vpcie0v9)) {
> +		if (PTR_ERR(port->vpcie0v9) == -EPROBE_DEFER)
> +			return -EPROBE_DEFER;
> +		dev_info(dev, "No vpcie0v9 regulator found.\n");
> +	}
> +
> +	return 0;
> +}
> +
> +static int rockchip_pcie_set_vpcie(struct rockchip_pcie_port *port)
> +{
> +	int err;
> +
> +	if (!IS_ERR(port->vpcie3v3)) {
> +		err = regulator_enable(port->vpcie3v3);
> +		if (err) {
> +			dev_err(port->dev, "Fail to enable vpcie3v3 regulator.\n");
> +			goto err_out;
> +		}
> +	}
> +
> +	if (!IS_ERR(port->vpcie1v8)) {
> +		err = regulator_enable(port->vpcie1v8);
> +		if (err) {
> +			dev_err(port->dev, "Fail to enable vpcie1v8 regulator.\n");
> +			goto err_disable_3v3;
> +		}
> +	}
> +
> +	if (!IS_ERR(port->vpcie0v9)) {
> +		err = regulator_enable(port->vpcie0v9);
> +		if (err) {
> +			dev_err(port->dev, "Fail to enable vpcie0v9 regulator.\n");
> +			goto err_disable_1v8;
> +		}
> +	}
> +
> +	return 0;
> +
> +err_disable_1v8:
> +	if (!IS_ERR(port->vpcie1v8))
> +		regulator_disable(port->vpcie1v8);
> +err_disable_3v3:
> +	if (!IS_ERR(port->vpcie3v3))
> +		regulator_disable(port->vpcie3v3);
> +err_out:
> +	return err;
> +}
> +
> +static void rockchip_pcie_enable_interrupts(struct rockchip_pcie_port *port)
> +{
> +	pcie_write(port, (PCIE_CLIENT_INT_CLI << 16) &
> +		   (~PCIE_CLIENT_INT_CLI), PCIE_CLIENT_INT_MASK);
> +	pcie_write(port, PCIE_CORE_INT, PCIE_CORE_INT_MASK);
> +}
> +
> +static int rockchip_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
> +				  irq_hw_number_t hwirq)
> +{
> +	irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq);
> +	irq_set_chip_data(irq, domain->host_data);
> +
> +	return 0;
> +}
> +
> +static const struct irq_domain_ops intx_domain_ops = {
> +	.map = rockchip_pcie_intx_map,
> +};
> +
> +static int rockchip_pcie_init_irq_domain(struct rockchip_pcie_port *pp)
> +{
> +	struct device *dev = pp->dev;
> +
> +	pp->irq_domain = irq_domain_add_linear(dev->of_node, 4,
> +					       &intx_domain_ops, pp);
> +	if (!pp->irq_domain) {
> +		dev_err(dev, "Failed to get a INTx IRQ domain\n");
> +		return PTR_ERR(pp->irq_domain);
> +	}
> +
> +	return 0;
> +}
> +
> +static irqreturn_t rockchip_pcie_subsys_irq_handler(int irq, void *arg)
> +{
> +	struct rockchip_pcie_port *pp = arg;
> +	u32 reg;
> +	u32 sub_reg;
> +
> +	reg = pcie_read(pp, PCIE_CLIENT_INT_STATUS);
> +	if (reg & PCIE_CLIENT_INT_LOCAL) {
> +		dev_dbg(pp->dev, "local interrupt recived\n");
> +		sub_reg = pcie_read(pp, PCIE_CORE_INT_STATUS);
> +		if (sub_reg & PCIE_CORE_INT_PRFPE)
> +			dev_dbg(pp->dev, "Parity error detected while reading from the PNP Receive FIFO RAM\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_CRFPE)
> +			dev_dbg(pp->dev, "Parity error detected while reading from the Completion Receive FIFO RAM\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_RRPE)
> +			dev_dbg(pp->dev, "Parity error detected while reading from Replay Buffer RAM\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_PRFO)
> +			dev_dbg(pp->dev, "Overflow occurred in the PNP Receive FIFO\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_CRFO)
> +			dev_dbg(pp->dev, "Overflow occurred in the Completion Receive FIFO\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_RT)
> +			dev_dbg(pp->dev, "Replay timer timed out\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_RTR)
> +			dev_dbg(pp->dev, "Replay timer rolled over after 4 transmissions of the same TLP\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_PE)
> +			dev_dbg(pp->dev, "Phy error detected on receive side\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_MTR)
> +			dev_dbg(pp->dev, "Malformed TLP received from the link\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_UCR)
> +			dev_dbg(pp->dev, "Malformed TLP received from the link\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_FCE)
> +			dev_dbg(pp->dev, "An error was observed in the flow control advertisements from the other side\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_CT)
> +			dev_dbg(pp->dev, "A request timed out waiting for completion\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_UTC)
> +			dev_dbg(pp->dev, "Unmapped TC error\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_MMVC)
> +			dev_dbg(pp->dev, "MSI mask register changes\n");
> +
> +		pcie_write(pp, sub_reg, PCIE_CORE_INT_STATUS);
> +	}
> +
> +	pcie_write(pp, PCIE_CLIENT_INT_SUBSYSTEM | PCIE_CLIENT_INT_LOCAL,
> +		   PCIE_CLIENT_INT_STATUS);

Is this IRQ handler just for debugging? Seems kinda excessive. Similar
comment below.

> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t rockchip_pcie_client_irq_handler(int irq, void *arg)
> +{
> +	struct rockchip_pcie_port *pp = arg;
> +	u32 reg;
> +
> +	reg = pcie_read(pp, PCIE_CLIENT_INT_STATUS);
> +	if (reg & PCIE_CLIENT_INT_LEGACY_DONE)
> +		dev_dbg(pp->dev, "legacy done interrupt recived\n");
> +
> +	if (reg & PCIE_CLIENT_INT_MSG)
> +		dev_dbg(pp->dev, "message done interrupt recived\n");
> +
> +	if (reg & PCIE_CLIENT_INT_HOT_RST)
> +		dev_dbg(pp->dev, "hot reset interrupt recived\n");
> +
> +	if (reg & PCIE_CLIENT_INT_DPA)
> +		dev_dbg(pp->dev, "dpa interrupt recived\n");
> +
> +	if (reg & PCIE_CLIENT_INT_FATAL_ERR)
> +		dev_dbg(pp->dev, "fatal error interrupt recived\n");
> +
> +	if (reg & PCIE_CLIENT_INT_NFATAL_ERR)
> +		dev_dbg(pp->dev, "no fatal error interrupt recived\n");
> +
> +	if (reg & PCIE_CLIENT_INT_CORR_ERR)
> +		dev_dbg(pp->dev, "correctable error interrupt recived\n");

I thought somebody may have asked about this previously, but are you
really just installing this interrupt handler just to dump debug
messages? Seems like it'b be better just to avoid requesting this IRQ
completely, IMO. But maybe that's debatable.

> +
> +	pcie_write(pp, reg, PCIE_CLIENT_INT_STATUS);

Is it safe to just overwrite the entire status register? I see it is
made up of both Read Only and Write-1-to-Clear bits, and not all of
those bits are owned by this IRQ handler; some of them are for
rockchip_pcie_subsys_irq_handler(). So shouldn't you only write the bits
you know you handle? Like:

	pcie_write(pp, reg & (PCIE_CLIENT_INT_LEGACY_DONE |
			PCIE_CLIENT_INT_MSG | PCIE_CLIENT_INT_HOT_RST |
			PCIE_CLIENT_INT_DPA | PCIE_CLIENT_INT_FATAL_ERR |
			PCIE_CLIENT_INT_NFATAL_ERR | PCIE_CLIENT_INT_CORR_ERR),
			PCIE_CLIENT_INT_STATUS);

Or, if you just kill this entire IRQ handler, you can avoid this
potential source of errors.

(FWIW, it's possible this handler is causing me some problems with
legacy interrupts. Once I remove the "request_irq()" for this and the
other handler, I can get things kinda working again.)

> +
> +	return IRQ_HANDLED;
> +}
> +
> +static void rockchip_pcie_legacy_int_handler(struct irq_desc *desc)
> +{
> +	struct irq_chip *chip = irq_desc_get_chip(desc);
> +	struct rockchip_pcie_port *port;
> +	u32 reg;
> +	u32 hwirq;
> +	u32 virq;
> +
> +	chained_irq_enter(chip, desc);
> +	port = irq_desc_get_handler_data(desc);
> +
> +	reg = pcie_read(port, PCIE_CLIENT_INT_STATUS);
> +	reg = (reg & ROCKCHIP_PCIE_RPIFR1_INTR_MASK) >>
> +	       ROCKCHIP_PCIE_RPIFR1_INTR_SHIFT;
> +
> +	while (reg) {
> +		hwirq = ffs(reg);
> +		reg &= ~BIT(hwirq);

Per Marc's suggestion, you have created an infinite loop :)

This should be:

	hwirq = ffs(reg);
	reg &= ~BIT(hwirq - 1);

...which brings me to this question: are you ever testing non-MSI (i.e.,
"legacy") interrupts on this driver? There seems to be quite a bit of
discussion about the structuring of the interrupt-map and
interrupt-controller handling, but it appears this mostly/only gets
actually *used* for the legacy interrupt case, and on my tests, legacy
interrupts aren't really working, at least not with the new binding
examples that you proposed. But then, you didn't send a rk3399.dtsi
update, so perhaps I constructed my DTS wrong...

If you haven't been testing legacy interrupts, I'd recommend booting
with "pci=nomsi" and see what happens.

> +
> +		virq = irq_find_mapping(port->irq_domain, hwirq);
> +		if (virq)
> +			generic_handle_irq(virq);
> +		else
> +			dev_err(port->dev, "unexpected IRQ, INT%d\n", hwirq);
> +	}
> +
> +	chained_irq_exit(chip, desc);
> +}
> +

[...]

Brian


From briannorris at chromium.org  Wed Jul  6 17:39:12 2016
From: briannorris at chromium.org (Brian Norris)
Date: Wed, 6 Jul 2016 17:39:12 -0700
Subject: [PATCH v6 1/2] Documentation: bindings: add dt doc for Rockchip
 PCIe controller
In-Reply-To: <1467789398-13501-1-git-send-email-shawn.lin@rock-chips.com>
References: <1467789398-13501-1-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <20160707003912.GB100467@google.com>

Hi Shawn,

On Wed, Jul 06, 2016 at 03:16:37PM +0800, Shawn Lin wrote:
> This patch adds a binding that describes the Rockchip PCIe controller
> found on Rockchip SoCs PCIe interface.
> 
> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
> 
> Acked-by: Rob Herring <robh at kernel.org>
> ---
> 
> Changes in v6:
> - add ack tag from Rob
> 
> Changes in v5:
> - fix wrong example reported by Marc
> - add seperate section to describe the interrupt controller child
>   node
> 
> Changes in v4:
> - fix example of adding intermediate interrupt controller for pcie
>   legacy interrrupt
> 
> Changes in v3:
> - fix example dts code suggested by Rob and Marc
> - remove driver's behaviour of regulator
> 
> Changes in v2:
> - fix lots clk/reset stuff suggested by Heiko
> - remove msi-parent and add msi-map suggested by Marc
> - drop phy related stuff
> - some others minor fixes
> 
>  .../devicetree/bindings/pci/rockchip-pcie.txt      | 104 +++++++++++++++++++++
>  1 file changed, 104 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/pci/rockchip-pcie.txt
> 
> diff --git a/Documentation/devicetree/bindings/pci/rockchip-pcie.txt b/Documentation/devicetree/bindings/pci/rockchip-pcie.txt
> new file mode 100644
> index 0000000..7616ecc
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pci/rockchip-pcie.txt
> @@ -0,0 +1,104 @@
> +* Rockchip AXI PCIe Root Port Bridge DT description
> +
> +Required properties:
> +- #address-cells: Address representation for root ports, set to <3>
> +- #size-cells: Size representation for root ports, set to <2>
> +- #interrupt-cells: specifies the number of cells needed to encode an
> +		interrupt source. The value must be 1.
> +- compatible: Should contain "rockchip,rk3399-pcie"
> +- reg: Two register ranges as listed in the reg-names property
> +- reg-names: Must include the following names
> +	- "axi-base"
> +	- "apb-base"
> +- clocks: Must contain an entry for each entry in clock-names.
> +		See ../clocks/clock-bindings.txt for details.
> +- clock-names: Must include the following entries:
> +	- "aclk"
> +	- "aclk-perf"
> +	- "hclk"
> +	- "pm"
> +- msi-map: Maps a Requester ID to an MSI controller and associated.
> +		See ./pci-msi.txt
> +- phys: From PHY bindings: Phandle for the Generic PHY for PCIe.
> +- phy-names:  MUST be "pcie-phy".
> +- interrupts: Three interrupt entries must be specified.
> +- interrupt-names: Must include the following names
> +	- "sys"
> +	- "legacy"
> +	- "client"
> +- resets: Must contain five entries for each entry in reset-names.
> +	   See ../reset/reset.txt for details.
> +- reset-names: Must include the following names
> +	- "core"
> +	- "mgmt"
> +	- "mgmt-sticky"
> +	- "pipe"
> +- pinctrl-names : The pin control state names
> +- pinctrl-0: The "default" pinctrl state
> +- #interrupt-cells: specifies the number of cells needed to encode an
> +	interrupt source. The value must be 1.
> +- interrupt-map-mask and interrupt-map: standard PCI properties
> +
> +*Interrupt controller child node*
> +The core controller provides a single interrupt for legacy INTx. So,
> +pcie node should create a interrupt controller node to support 'interrupt-map'
> +DT functionality. The driver will create an IRQ domain for this map, decode
> +the four INTx interrupts in ISR and route them to this domain.

Where in your driver do you actually handle this child node? I don't see
anything, but perhaps I'm missing something. I see how your earlier
revisions of this driver used of_get_next_child() to acquire the child
node, for use with irq_domain_add_linear(). But that's not in this
version...

> +
> +Required properties for Interrupt controller child node:
> +- interrupt-controller: identifies the node as an interrupt controller
> +- #address-cells: specifies the number of cells needed to encode an
> +	address. The value must be 0.
> +- #interrupt-cells: specifies the number of cells needed to encode an
> +	interrupt source. The value must be 1.
> +
> +Optional Property:

These optional properties apply to the pcie node, not the interrupt
controller child, right? Seems like the subnode and its properties
should be last (i.e., the 'Optional Property' section should be above
'Interrupt controller child node').

> +- ep-gpios: contain the entry for pre-reset gpio
> +- num-lanes: number of lanes to use
> +- vpcie3v3-supply: The phandle to the 3.3v regulator to use for pcie.
> +- vpcie1v8-supply: The phandle to the 1.8v regulator to use for pcie.
> +- vpcie0v9-supply: The phandle to the 0.9v regulator to use for pcie.
> +
> +Example:
> +
> +pcie0: pcie at f8000000 {
> +	compatible = "rockchip,rk3399-pcie";
> +	#address-cells = <3>;
> +	#size-cells = <2>;
> +	clocks = <&cru ACLK_PCIE>, <&cru ACLK_PERF_PCIE>,
> +		 <&cru PCLK_PCIE>, <&cru SCLK_PCIE_PM>;
> +	clock-names = "aclk", "aclk-perf",
> +		      "hclk", "pm";
> +	bus-range = <0x0 0x1>;
> +	interrupts = <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 50 IRQ_TYPE_LEVEL_HIGH>,
> +		     <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>;
> +	interrupt-names = "sys", "legacy", "client";
> +	assigned-clocks = <&cru SCLK_PCIEPHY_REF>;
> +	assigned-clock-parents = <&cru SCLK_PCIEPHY_REF100M>;
> +	assigned-clock-rates = <100000000>;
> +	ep-gpios = <&gpio3 13 GPIO_ACTIVE_HIGH>;
> +	ranges = <0x83000000 0x0 0xfa000000 0x0 0xfa000000 0x0 0x600000
> +		  0x81000000 0x0 0xfa600000 0x0 0xfa600000 0x0 0x100000>;
> +	num-lanes = <4>;
> +	msi-map = <0x0 &its 0x0 0x1000>;
> +	reg = < 0x0 0xf8000000 0x0 0x2000000 >, < 0x0 0xfd000000 0x0 0x1000000 >;
> +	reg-names = "axi-base", "apb-base";
> +	resets = <&cru SRST_PCIE_CORE>, <&cru SRST_PCIE_MGMT>,
> +		 <&cru SRST_PCIE_MGMT_STICKY>, <&cru SRST_PCIE_PIPE>;
> +	reset-names = "core", "mgmt", "mgmt-sticky", "pipe";
> +	phys = <&pcie_phy>;
> +	phy-names = "pcie-phy";
> +	pinctrl-names = "default";
> +	pinctrl-0 = <&pcie_clkreq>;
> +	#interrupt-cells = <1>;
> +	interrupt-map-mask = <0 0 0 7>;
> +	interrupt-map = <0 0 0 1 &pcie0_intc 1>,
> +			<0 0 0 2 &pcie0_intc 2>,
> +			<0 0 0 3 &pcie0_intc 3>,
> +			<0 0 0 4 &pcie0_intc 4>;

I'm a little lost on this one, so forgive my ignorance; how did you
determine the last value in each entry (i.e., the 1, 2, 3, and 4 IRQ
numbers for pcie0_intc)? IIUC, those are supposed to represent indeces
into the IRQ status register found in the PCIe interrupt status
register, and so they should be 0-based (i.e., 0, 1, 2, 3). And then
you'd have:

	interrupt-map = <0 0 0 1 &pcie0_intc 0>,
			<0 0 0 2 &pcie0_intc 1>,
			<0 0 0 3 &pcie0_intc 2>,
			<0 0 0 4 &pcie0_intc 3>;

But then, I never got this sub-node binding to work quite right, so I
may be missing something.

EDIT: ooh, I see what's going on! I'll comment on the driver as well,
but it looks like you're translating the register status to a HW IRQ
number with 'ffs(reg)', which yields a 1-based index. I think it is most
sensible to use a 0-based index (i.e., 'ffs(reg) - 1'). Now, that only
will work if you get the whole interrupt-map + interrupt-controller
thing right (i.e., using a subnode for the interrupt controller) --
otherwise, IRQ mapping might not work right. I suspect that's one reason
the original driver writer might have used 1-based indexing in the first
place.

Brian

> +	pcie0_intc: interrupt-controller {
> +		interrupt-controller;
> +		#address-cells = <0>;
> +		#interrupt-cells = <1>;
> +	};
> +};
> -- 
> 2.3.7
> 
> 


From briannorris at chromium.org  Wed Jul  6 17:44:06 2016
From: briannorris at chromium.org (Brian Norris)
Date: Wed, 6 Jul 2016 17:44:06 -0700
Subject: [PATCH v6 2/2] PCI: Rockchip: Add Rockchip PCIe controller support
In-Reply-To: <20160707001254.GA100467@google.com>
References: <1467789398-13501-1-git-send-email-shawn.lin@rock-chips.com>
 <1467789398-13501-2-git-send-email-shawn.lin@rock-chips.com>
 <20160707001254.GA100467@google.com>
Message-ID: <20160707004404.GA57063@google.com>

Hi again,

On Wed, Jul 06, 2016 at 05:12:55PM -0700, Brian Norris wrote:
> On Wed, Jul 06, 2016 at 03:16:38PM +0800, Shawn Lin wrote:
> > +static void rockchip_pcie_legacy_int_handler(struct irq_desc *desc)
> > +{
> > +	struct irq_chip *chip = irq_desc_get_chip(desc);
> > +	struct rockchip_pcie_port *port;
> > +	u32 reg;
> > +	u32 hwirq;
> > +	u32 virq;
> > +
> > +	chained_irq_enter(chip, desc);
> > +	port = irq_desc_get_handler_data(desc);
> > +
> > +	reg = pcie_read(port, PCIE_CLIENT_INT_STATUS);
> > +	reg = (reg & ROCKCHIP_PCIE_RPIFR1_INTR_MASK) >>
> > +	       ROCKCHIP_PCIE_RPIFR1_INTR_SHIFT;
> > +
> > +	while (reg) {
> > +		hwirq = ffs(reg);

As I noted on the DT binding patch, the use of 'ffs' here is causing you
to make the interrupt controller binding implicitly 1-based, where it
would seemingly make more sense to be 0-based I think.

> > +		reg &= ~BIT(hwirq);
> 
> Per Marc's suggestion, you have created an infinite loop :)
> 
> This should be:
> 
> 	hwirq = ffs(reg);
> 	reg &= ~BIT(hwirq - 1);

So, this should actually be:

		hwirq = ffs(reg) - 1;
		reg &= ~BIT(hwirq);

> ...which brings me to this question: are you ever testing non-MSI (i.e.,
> "legacy") interrupts on this driver? There seems to be quite a bit of
> discussion about the structuring of the interrupt-map and
> interrupt-controller handling, but it appears this mostly/only gets
> actually *used* for the legacy interrupt case, and on my tests, legacy
> interrupts aren't really working, at least not with the new binding
> examples that you proposed. But then, you didn't send a rk3399.dtsi
> update, so perhaps I constructed my DTS wrong...
> 
> If you haven't been testing legacy interrupts, I'd recommend booting
> with "pci=nomsi" and see what happens.
> 
> > +
> > +		virq = irq_find_mapping(port->irq_domain, hwirq);
> > +		if (virq)
> > +			generic_handle_irq(virq);
> > +		else
> > +			dev_err(port->dev, "unexpected IRQ, INT%d\n", hwirq);
> > +	}
> > +
> > +	chained_irq_exit(chip, desc);
> > +}
> > +

Brian


From william.wu at rock-chips.com  Wed Jul  6 19:54:21 2016
From: william.wu at rock-chips.com (William Wu)
Date: Thu,  7 Jul 2016 10:54:21 +0800
Subject: [PATCH v6 0/5] support rockchip dwc3 driver
Message-ID: <1467860066-15142-1-git-send-email-william.wu@rock-chips.com>

This series add support for rockchip dwc3 driver,
and add additional optional properties for specific
platforms (e.g., rockchip rk3399 platform).

William Wu (5):
  usb: dwc3: of-simple: add compatible for rockchip rk3399
  usb: dwc3: add dis_u2_freeclk_exists_quirk
  usb: dwc3: add phyif_utmi_quirk
  usb: dwc3: add dis_del_phy_power_chg_quirk
  usb: dwc3: rockchip: add devicetree bindings documentation

 Documentation/devicetree/bindings/usb/dwc3.txt     |  9 ++++
 .../devicetree/bindings/usb/rockchip,dwc3.txt      | 59 ++++++++++++++++++++++
 drivers/usb/dwc3/core.c                            | 29 +++++++++++
 drivers/usb/dwc3/core.h                            | 20 ++++++++
 drivers/usb/dwc3/dwc3-of-simple.c                  |  1 +
 5 files changed, 118 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/usb/rockchip,dwc3.txt

-- 
1.9.1




From william.wu at rock-chips.com  Wed Jul  6 19:54:22 2016
From: william.wu at rock-chips.com (William Wu)
Date: Thu,  7 Jul 2016 10:54:22 +0800
Subject: [PATCH v6 1/5] usb: dwc3: of-simple: add compatible for rockchip
 rk3399
In-Reply-To: <1467860066-15142-1-git-send-email-william.wu@rock-chips.com>
References: <1467860066-15142-1-git-send-email-william.wu@rock-chips.com>
Message-ID: <1467860066-15142-2-git-send-email-william.wu@rock-chips.com>

Rockchip platform merely enable usb3 clocks and
populate its children. So we can use this generic
glue layer to support Rockchip dwc3.

Signed-off-by: William Wu <william.wu at rock-chips.com>
---
Changes in v6:
- None

Changes in v5:
- change compatible from "rockchip,dwc3" to "rockchip,rk3399-dwc3" (Heiko)

Changes in v4:
- None

Changes in v3:
- None

Changes in v2:
- sort the list of_dwc3_simple_match (Doug)

 drivers/usb/dwc3/dwc3-of-simple.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c
index 9743353..05c9349 100644
--- a/drivers/usb/dwc3/dwc3-of-simple.c
+++ b/drivers/usb/dwc3/dwc3-of-simple.c
@@ -161,6 +161,7 @@ static const struct dev_pm_ops dwc3_of_simple_dev_pm_ops = {
 
 static const struct of_device_id of_dwc3_simple_match[] = {
 	{ .compatible = "qcom,dwc3" },
+	{ .compatible = "rockchip,rk3399-dwc3" },
 	{ .compatible = "xlnx,zynqmp-dwc3" },
 	{ /* Sentinel */ }
 };
-- 
1.9.1




From william.wu at rock-chips.com  Wed Jul  6 19:54:23 2016
From: william.wu at rock-chips.com (William Wu)
Date: Thu,  7 Jul 2016 10:54:23 +0800
Subject: [PATCH v6 2/5] usb: dwc3: add dis_u2_freeclk_exists_quirk
In-Reply-To: <1467860066-15142-1-git-send-email-william.wu@rock-chips.com>
References: <1467860066-15142-1-git-send-email-william.wu@rock-chips.com>
Message-ID: <1467860066-15142-3-git-send-email-william.wu@rock-chips.com>

Add a quirk to clear the GUSB2PHYCFG.U2_FREECLK_EXISTS bit,
which specifies whether the USB2.0 PHY provides a free-running
PHY clock, which is active when the clock control input is active.

Signed-off-by: William Wu <william.wu at rock-chips.com>
---
Changes in v6:
- use '-' instead of '_' in dts (Rob Herring)

Changes in v5:
- None

Changes in v4:
- rebase on top of balbi testing/next, remove pdata (balbi)

Changes in v3:
- None

Changes in v2:
- None

 Documentation/devicetree/bindings/usb/dwc3.txt | 3 +++
 drivers/usb/dwc3/core.c                        | 5 +++++
 drivers/usb/dwc3/core.h                        | 5 +++++
 3 files changed, 13 insertions(+)

diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt
index 7d7ce08..020b0e9 100644
--- a/Documentation/devicetree/bindings/usb/dwc3.txt
+++ b/Documentation/devicetree/bindings/usb/dwc3.txt
@@ -39,6 +39,9 @@ Optional properties:
 			disabling the suspend signal to the PHY.
  - snps,dis_rxdet_inp3_quirk: when set core will disable receiver detection
 			in PHY P3 power state.
+ - snps,dis-u2-freeclk-exists-quirk: when set, clear the u2_freeclk_exists
+			in GUSB2PHYCFG, specify that USB2 PHY doesn't provide
+			a free-running PHY clock.
  - snps,is-utmi-l1-suspend: true when DWC3 asserts output signal
 			utmi_l1_suspend_n, false when asserts utmi_sleep_n
  - snps,hird-threshold: HIRD threshold
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 9466431..0b7bfd2 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -500,6 +500,9 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
 	if (dwc->dis_enblslpm_quirk)
 		reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
 
+	if (dwc->dis_u2_freeclk_exists_quirk)
+		reg &= ~DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS;
+
 	dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
 
 	return 0;
@@ -924,6 +927,8 @@ static int dwc3_probe(struct platform_device *pdev)
 				"snps,dis_enblslpm_quirk");
 	dwc->dis_rxdet_inp3_quirk = device_property_read_bool(dev,
 				"snps,dis_rxdet_inp3_quirk");
+	dwc->dis_u2_freeclk_exists_quirk = device_property_read_bool(dev,
+				"snps,dis-u2-freeclk-exists-quirk");
 
 	dwc->tx_de_emphasis_quirk = device_property_read_bool(dev,
 				"snps,tx_de_emphasis_quirk");
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 45d6de5..f321a5c 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -199,6 +199,7 @@
 
 /* Global USB2 PHY Configuration Register */
 #define DWC3_GUSB2PHYCFG_PHYSOFTRST	(1 << 31)
+#define DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS	(1 << 30)
 #define DWC3_GUSB2PHYCFG_SUSPHY		(1 << 6)
 #define DWC3_GUSB2PHYCFG_ULPI_UTMI	(1 << 4)
 #define DWC3_GUSB2PHYCFG_ENBLSLPM	(1 << 8)
@@ -799,6 +800,9 @@ struct dwc3_scratchpad_array {
  * @dis_u2_susphy_quirk: set if we disable usb2 suspend phy
  * @dis_enblslpm_quirk: set if we clear enblslpm in GUSB2PHYCFG,
  *                      disabling the suspend signal to the PHY.
+ * @dis_u2_freeclk_exists_quirk : set if we clear u2_freeclk_exists
+ *			in GUSB2PHYCFG, specify that USB2 PHY doesn't
+ *			provide a free-running PHY clock.
  * @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk
  * @tx_de_emphasis: Tx de-emphasis value
  * 	0	- -6dB de-emphasis
@@ -942,6 +946,7 @@ struct dwc3 {
 	unsigned		dis_u2_susphy_quirk:1;
 	unsigned		dis_enblslpm_quirk:1;
 	unsigned		dis_rxdet_inp3_quirk:1;
+	unsigned		dis_u2_freeclk_exists_quirk:1;
 
 	unsigned		tx_de_emphasis_quirk:1;
 	unsigned		tx_de_emphasis:2;
-- 
1.9.1




From william.wu at rock-chips.com  Wed Jul  6 19:54:24 2016
From: william.wu at rock-chips.com (William Wu)
Date: Thu,  7 Jul 2016 10:54:24 +0800
Subject: [PATCH v6 3/5] usb: dwc3: add phyif_utmi_quirk
In-Reply-To: <1467860066-15142-1-git-send-email-william.wu@rock-chips.com>
References: <1467860066-15142-1-git-send-email-william.wu@rock-chips.com>
Message-ID: <1467860066-15142-4-git-send-email-william.wu@rock-chips.com>

Add a quirk to configure the core to support the
UTMI+ PHY with an 8- or 16-bit interface. UTMI+ PHY
interface is hardware property, and it's platform
dependent. Normall, the PHYIf can be configured
during coreconsultant. But for some specific usb
cores(e.g. rk3399 soc dwc3), the default PHYIf
configuration value is fault, so we need to
reconfigure it by software.

And refer to the dwc3 databook, the GUSB2PHYCFG.USBTRDTIM
must be set to the corresponding value according to
the UTMI+ PHY interface.

Signed-off-by: William Wu <william.wu at rock-chips.com>
---
Changes in v6:
- use '-' instead of '_' in dts (Rob Herring)

Changes in v5:
- None

Changes in v4:
- rebase on top of balbi testing/next, remove pdata (balbi)

Changes in v3:
- None

Changes in v2:
- add a quirk for phyif_utmi (balbi)

 Documentation/devicetree/bindings/usb/dwc3.txt |  4 ++++
 drivers/usb/dwc3/core.c                        | 19 +++++++++++++++++++
 drivers/usb/dwc3/core.h                        | 12 ++++++++++++
 3 files changed, 35 insertions(+)

diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt
index 020b0e9..8d7317d 100644
--- a/Documentation/devicetree/bindings/usb/dwc3.txt
+++ b/Documentation/devicetree/bindings/usb/dwc3.txt
@@ -42,6 +42,10 @@ Optional properties:
  - snps,dis-u2-freeclk-exists-quirk: when set, clear the u2_freeclk_exists
 			in GUSB2PHYCFG, specify that USB2 PHY doesn't provide
 			a free-running PHY clock.
+ - snps,phyif-utmi-quirk: when set core will set phyif UTMI+ interface.
+ - snps,phyif-utmi: the value to configure the core to support a UTMI+ PHY
+			with an 8- or 16-bit interface. Value 0 select 8-bit
+			interface, value 1 select 16-bit interface.
  - snps,is-utmi-l1-suspend: true when DWC3 asserts output signal
 			utmi_l1_suspend_n, false when asserts utmi_sleep_n
  - snps,hird-threshold: HIRD threshold
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 0b7bfd2..94036b1 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -408,6 +408,7 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
 static int dwc3_phy_setup(struct dwc3 *dwc)
 {
 	u32 reg;
+	u32 usbtrdtim;
 	int ret;
 
 	reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
@@ -503,6 +504,15 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
 	if (dwc->dis_u2_freeclk_exists_quirk)
 		reg &= ~DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS;
 
+	if (dwc->phyif_utmi_quirk) {
+		reg &= ~(DWC3_GUSB2PHYCFG_PHYIF_MASK |
+		       DWC3_GUSB2PHYCFG_USBTRDTIM_MASK);
+		usbtrdtim = dwc->phyif_utmi ? USBTRDTIM_UTMI_16_BIT :
+			    USBTRDTIM_UTMI_8_BIT;
+		reg |= DWC3_GUSB2PHYCFG_PHYIF(dwc->phyif_utmi) |
+		       DWC3_GUSB2PHYCFG_USBTRDTIM(usbtrdtim);
+	}
+
 	dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
 
 	return 0;
@@ -834,6 +844,7 @@ static int dwc3_probe(struct platform_device *pdev)
 	struct resource		*res;
 	struct dwc3		*dwc;
 	u8			lpm_nyet_threshold;
+	u8			phyif_utmi;
 	u8			tx_de_emphasis;
 	u8			hird_threshold;
 
@@ -880,6 +891,9 @@ static int dwc3_probe(struct platform_device *pdev)
 	/* default to highest possible threshold */
 	lpm_nyet_threshold = 0xff;
 
+	/* default to UTMI+ 8-bit interface */
+	phyif_utmi = 0;
+
 	/* default to -3.5dB de-emphasis */
 	tx_de_emphasis = 1;
 
@@ -929,6 +943,10 @@ static int dwc3_probe(struct platform_device *pdev)
 				"snps,dis_rxdet_inp3_quirk");
 	dwc->dis_u2_freeclk_exists_quirk = device_property_read_bool(dev,
 				"snps,dis-u2-freeclk-exists-quirk");
+	dwc->phyif_utmi_quirk = device_property_read_bool(dev,
+				"snps,phyif-utmi-quirk");
+	 device_property_read_u8(dev, "snps,phyif-utmi",
+				 &phyif_utmi);
 
 	dwc->tx_de_emphasis_quirk = device_property_read_bool(dev,
 				"snps,tx_de_emphasis_quirk");
@@ -940,6 +958,7 @@ static int dwc3_probe(struct platform_device *pdev)
 				 &dwc->fladj);
 
 	dwc->lpm_nyet_threshold = lpm_nyet_threshold;
+	dwc->phyif_utmi = phyif_utmi;
 	dwc->tx_de_emphasis = tx_de_emphasis;
 
 	dwc->hird_threshold = hird_threshold
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index f321a5c..cf6696c 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -203,6 +203,12 @@
 #define DWC3_GUSB2PHYCFG_SUSPHY		(1 << 6)
 #define DWC3_GUSB2PHYCFG_ULPI_UTMI	(1 << 4)
 #define DWC3_GUSB2PHYCFG_ENBLSLPM	(1 << 8)
+#define DWC3_GUSB2PHYCFG_PHYIF(n)	(n << 3)
+#define DWC3_GUSB2PHYCFG_PHYIF_MASK	DWC3_GUSB2PHYCFG_PHYIF(1)
+#define DWC3_GUSB2PHYCFG_USBTRDTIM(n)	(n << 10)
+#define DWC3_GUSB2PHYCFG_USBTRDTIM_MASK	DWC3_GUSB2PHYCFG_USBTRDTIM(0xf)
+#define USBTRDTIM_UTMI_8_BIT		9
+#define USBTRDTIM_UTMI_16_BIT		5
 
 /* Global USB2 PHY Vendor Control Register */
 #define DWC3_GUSB2PHYACC_NEWREGREQ	(1 << 25)
@@ -803,6 +809,10 @@ struct dwc3_scratchpad_array {
  * @dis_u2_freeclk_exists_quirk : set if we clear u2_freeclk_exists
  *			in GUSB2PHYCFG, specify that USB2 PHY doesn't
  *			provide a free-running PHY clock.
+ * @phyif_utmi_quirk: set if we enable phyif UTMI+ quirk
+ * @phyif_utmi: UTMI+ PHY interface value
+ *	0	- 8 bits
+ *	1	- 16 bits
  * @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk
  * @tx_de_emphasis: Tx de-emphasis value
  * 	0	- -6dB de-emphasis
@@ -948,6 +958,8 @@ struct dwc3 {
 	unsigned		dis_rxdet_inp3_quirk:1;
 	unsigned		dis_u2_freeclk_exists_quirk:1;
 
+	unsigned		phyif_utmi_quirk:1;
+	unsigned		phyif_utmi:1;
 	unsigned		tx_de_emphasis_quirk:1;
 	unsigned		tx_de_emphasis:2;
 };
-- 
1.9.1




From william.wu at rock-chips.com  Wed Jul  6 19:54:25 2016
From: william.wu at rock-chips.com (William Wu)
Date: Thu,  7 Jul 2016 10:54:25 +0800
Subject: [PATCH v6 4/5] usb: dwc3: add dis_del_phy_power_chg_quirk
In-Reply-To: <1467860066-15142-1-git-send-email-william.wu@rock-chips.com>
References: <1467860066-15142-1-git-send-email-william.wu@rock-chips.com>
Message-ID: <1467860066-15142-5-git-send-email-william.wu@rock-chips.com>

Add a quirk to clear the GUSB3PIPECTL.DELAYP1TRANS bit,
which specifies whether disable delay PHY power change
from P0 to P1/P2/P3 when link state changing from U0
to U1/U2/U3 respectively.

Signed-off-by: William Wu <william.wu at rock-chips.com>
---
Changes in v6:
- use '-' instead of '_' in dts (Rob Herring)

Changes in v5:
- None

Changes in v4:
- rebase on top of balbi testing/next, remove pdata (balbi)

Changes in v3:
- None

Changes in v2:
- None

 Documentation/devicetree/bindings/usb/dwc3.txt | 2 ++
 drivers/usb/dwc3/core.c                        | 5 +++++
 drivers/usb/dwc3/core.h                        | 3 +++
 3 files changed, 10 insertions(+)

diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt
index 8d7317d..1c140df 100644
--- a/Documentation/devicetree/bindings/usb/dwc3.txt
+++ b/Documentation/devicetree/bindings/usb/dwc3.txt
@@ -42,6 +42,8 @@ Optional properties:
  - snps,dis-u2-freeclk-exists-quirk: when set, clear the u2_freeclk_exists
 			in GUSB2PHYCFG, specify that USB2 PHY doesn't provide
 			a free-running PHY clock.
+ - snps,dis-del-phy-power-chg-quirk: when set core will change PHY power
+			from P0 to P1/P2/P3 without delay.
  - snps,phyif-utmi-quirk: when set core will set phyif UTMI+ interface.
  - snps,phyif-utmi: the value to configure the core to support a UTMI+ PHY
 			with an 8- or 16-bit interface. Value 0 select 8-bit
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 94036b1..e79d6a4 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -449,6 +449,9 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
 	if (dwc->dis_u3_susphy_quirk)
 		reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
 
+	if (dwc->dis_del_phy_power_chg_quirk)
+		reg &= ~DWC3_GUSB3PIPECTL_DEPOCHANGE;
+
 	dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
 
 	reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
@@ -943,6 +946,8 @@ static int dwc3_probe(struct platform_device *pdev)
 				"snps,dis_rxdet_inp3_quirk");
 	dwc->dis_u2_freeclk_exists_quirk = device_property_read_bool(dev,
 				"snps,dis-u2-freeclk-exists-quirk");
+	dwc->dis_del_phy_power_chg_quirk = device_property_read_bool(dev,
+				"snps,dis-del-phy-power-chg-quirk");
 	dwc->phyif_utmi_quirk = device_property_read_bool(dev,
 				"snps,phyif-utmi-quirk");
 	 device_property_read_u8(dev, "snps,phyif-utmi",
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index cf6696c..55e136d 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -809,6 +809,8 @@ struct dwc3_scratchpad_array {
  * @dis_u2_freeclk_exists_quirk : set if we clear u2_freeclk_exists
  *			in GUSB2PHYCFG, specify that USB2 PHY doesn't
  *			provide a free-running PHY clock.
+ * @dis_del_phy_power_chg_quirk: set if we disable delay phy power
+ *			change quirk.
  * @phyif_utmi_quirk: set if we enable phyif UTMI+ quirk
  * @phyif_utmi: UTMI+ PHY interface value
  *	0	- 8 bits
@@ -957,6 +959,7 @@ struct dwc3 {
 	unsigned		dis_enblslpm_quirk:1;
 	unsigned		dis_rxdet_inp3_quirk:1;
 	unsigned		dis_u2_freeclk_exists_quirk:1;
+	unsigned		dis_del_phy_power_chg_quirk:1;
 
 	unsigned		phyif_utmi_quirk:1;
 	unsigned		phyif_utmi:1;
-- 
1.9.1




From william.wu at rock-chips.com  Wed Jul  6 19:58:44 2016
From: william.wu at rock-chips.com (William Wu)
Date: Thu,  7 Jul 2016 10:58:44 +0800
Subject: [PATCH v6 5/5] usb: dwc3: rockchip: add devicetree bindings
 documentation
In-Reply-To: <1467860066-15142-1-git-send-email-william.wu@rock-chips.com>
References: <1467860066-15142-1-git-send-email-william.wu@rock-chips.com>
Message-ID: <1467860324-15341-1-git-send-email-william.wu@rock-chips.com>

This patch adds the devicetree documentation required for Rockchip
USB3.0 core wrapper consisting of USB3.0 IP from Synopsys.

It supports DRD mode, and could operate in device mode (SS, HS, FS)
and host mode (SS, HS, FS, LS).

Signed-off-by: William Wu <william.wu at rock-chips.com>
---
Changes in v6:
- rename bus_clk, and add usbdrd3_1 node as an example (Heiko)

Changes in v5:
- rename clock-names, and remove unnecessary clocks (Heiko)

Changes in v4:
- modify commit log, and add phy documentation location (Sergei)

Changes in v3:
- add dwc3 address (balbi)

Changes in v2:
- add rockchip,dwc3.txt to Documentation/devicetree/bindings/ (balbi, Brian)

 .../devicetree/bindings/usb/rockchip,dwc3.txt      | 59 ++++++++++++++++++++++
 1 file changed, 59 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/usb/rockchip,dwc3.txt

diff --git a/Documentation/devicetree/bindings/usb/rockchip,dwc3.txt b/Documentation/devicetree/bindings/usb/rockchip,dwc3.txt
new file mode 100644
index 0000000..0536a93
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/rockchip,dwc3.txt
@@ -0,0 +1,59 @@
+Rockchip SuperSpeed DWC3 USB SoC controller
+
+Required properties:
+- compatible:	should contain "rockchip,rk3399-dwc3" for rk3399 SoC
+- clocks:	A list of phandle + clock-specifier pairs for the
+		clocks listed in clock-names
+- clock-names:	Should contain the following:
+  "ref_clk"	Controller reference clk, have to be 24 MHz
+  "suspend_clk"	Controller suspend clk, have to be 24 MHz or 32 KHz
+  "bus_clk"	Master/Core clock, have to be >= 62.5 MHz for SS
+		operation and >= 30MHz for HS operation
+  "grf_clk"	Controller grf clk
+
+Required child node:
+A child node must exist to represent the core DWC3 IP block. The name of
+the node is not important. The content of the node is defined in dwc3.txt.
+
+Phy documentation is provided in the following places:
+Documentation/devicetree/bindings/phy/rockchip,dwc3-usb-phy.txt
+
+Example device nodes:
+
+	usbdrd3_0: usb at fe800000 {
+		compatible = "rockchip,rk3399-dwc3";
+		clocks = <&cru SCLK_USB3OTG0_REF>, <&cru SCLK_USB3OTG0_SUSPEND>,
+			 <&cru ACLK_USB3OTG0>, <&cru ACLK_USB3_GRF>;
+		clock-names = "ref_clk", "suspend_clk",
+			      "bus_clk", "grf_clk";
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+		status = "disabled";
+		usbdrd_dwc3_0: dwc3 at fe800000 {
+			compatible = "snps,dwc3";
+			reg = <0x0 0xfe800000 0x0 0x100000>;
+			interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>;
+			dr_mode = "otg";
+			status = "disabled";
+		};
+	};
+
+	usbdrd3_1: usb at fe900000 {
+		compatible = "rockchip,rk3399-dwc3";
+		clocks = <&cru SCLK_USB3OTG1_REF>, <&cru SCLK_USB3OTG1_SUSPEND>,
+			 <&cru ACLK_USB3OTG1>, <&cru ACLK_USB3_GRF>;
+		clock-names = "ref_clk", "suspend_clk",
+			      "bus_clk", "grf_clk";
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+		status = "disabled";
+		usbdrd_dwc3_1: dwc3 at fe900000 {
+			compatible = "snps,dwc3";
+			reg = <0x0 0xfe900000 0x0 0x100000>;
+			interrupts = <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH>;
+			dr_mode = "otg";
+			status = "disabled";
+		};
+	};
-- 
1.9.1




From wxt at rock-chips.com  Wed Jul  6 21:46:40 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Thu,  7 Jul 2016 12:46:40 +0800
Subject: [PATCH] arm64: dts: rockchip: support the usb2phy for rk3399 evb
Message-ID: <1467866800-24794-1-git-send-email-wxt@rock-chips.com>

From: Frank Wang <frank.wang at rock-chips.com>

This patch adds the usb2phy needed dts node information for rk3399.

USB2.0 PHY is comprised of one Host port and one OTG port.
Host Port is for USB2.0 host controller; OTG port is for USB2.0 part of
USB3.0 OTG controller, and as a part to construct a fully feature TypeC
subsystem.

The USB2.0 vbus gpio is board specific, it's no same with all rk3399
boards, so move it into evb voard.

Signed-off-by: Frank Wang <frank.wang at rock-chips.com>
Signed-off-by: Caesar Wang <wxt at rock-chips.com>
---

 arch/arm64/boot/dts/rockchip/rk3399-evb.dts |  4 ++++
 arch/arm64/boot/dts/rockchip/rk3399.dtsi    | 19 +++++++++++++++++++
 2 files changed, 23 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3399-evb.dts b/arch/arm64/boot/dts/rockchip/rk3399-evb.dts
index d33aa06..9be3715 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-evb.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-evb.dts
@@ -105,6 +105,10 @@
 	status = "okay";
 };
 
+&usb2phy {
+	vbus_drv-gpio = <&gpio4 25 GPIO_ACTIVE_HIGH>;
+};
+
 &usb_host0_ehci {
 	status = "okay";
 };
diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index 4c84229..21d147f 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -242,6 +242,25 @@
 		status = "disabled";
 	};
 
+	usb2phy: usb2phy {
+		compatible = "rockchip,rk3399-usb-phy";
+		rockchip,grf = <&grf>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		usb2phy0: usb2-phy0 {
+			#phy-cells = <0>;
+			#clock-cells = <0>;
+			reg = <0xe458>;
+		};
+
+		usb2phy1: usb2-phy1 {
+			#phy-cells = <0>;
+			#clock-cells = <0>;
+			reg = <0xe468>;
+		};
+	};
+
 	usb_host0_ehci: usb at fe380000 {
 		compatible = "generic-ehci";
 		reg = <0x0 0xfe380000 0x0 0x20000>;
-- 
1.9.1



From olof at lixom.net  Wed Jul  6 22:22:58 2016
From: olof at lixom.net (Olof Johansson)
Date: Wed, 6 Jul 2016 22:22:58 -0700
Subject: [GIT PULL 1/2] Rockchip dts32 changes for 4.8
In-Reply-To: <1666093.aVfPg2VmbT@phil>
References: <1666093.aVfPg2VmbT@phil>
Message-ID: <20160707052258.GJ32389@localhost>

On Fri, Jul 01, 2016 at 02:19:25AM +0200, Heiko Stuebner wrote:
> Hi Arnd, Kevin, Olof,
> 
> please find below arm devicetree changes for 4.8. The biggest part is
> probably the support for audio on veyron devices where the renaming was
> coordinated with the alsa side and the overturned name was never part of
> an actual release.
> 
> Apart from that Veyron also got a node for its spi flash and we now also
> support the ethernet and i2s controller on the rk3228/rk3229 siblings.
> 
> So if it looks ok, please pull
> 
> Thanks
> Heiko
> 
> 
> The following changes since commit 1a695a905c18548062509178b98bc91e67510864:
> 
>   Linux 4.7-rc1 (2016-05-29 09:29:24 -0700)
> 
> are available in the git repository at:
> 
>   git://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip.git tags/v4.8-rockchip-dts32-1
> 
> for you to fetch changes up to 241eff3c198492b2d63e75723b774f2836fee8a3:
> 
>   ARM: dts: rockchip: add support rk3229 evb board (2016-06-27 21:38:50 +0200)

Merged, thanks!


-Olof



From olof at lixom.net  Wed Jul  6 22:23:42 2016
From: olof at lixom.net (Olof Johansson)
Date: Wed, 6 Jul 2016 22:23:42 -0700
Subject: [GIT PULL 2/2] Rockchip dts64 changes for 4.8
In-Reply-To: <5006101.OhzRdW27q2@phil>
References: <1666093.aVfPg2VmbT@phil>
 <5006101.OhzRdW27q2@phil>
Message-ID: <20160707052342.GK32389@localhost>

On Fri, Jul 01, 2016 at 02:22:19AM +0200, Heiko Stuebner wrote:
> Hi Arnd, Kevin, Olof,
> 
> please find below arm64 devicetree changes for 4.8.
> 
> Some new things for the rk3399 like emmc support as well some
> improvements+fixes for the rk3368 as explained in the tag.
> 
> So if ok, please pull
> 
> Thanks
> Heiko
> 
> 
> The following changes since commit 1a695a905c18548062509178b98bc91e67510864:
> 
>   Linux 4.7-rc1 (2016-05-29 09:29:24 -0700)
> 
> are available in the git repository at:
> 
>   git://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip.git tags/v4.8-rockchip-dts64-1
> 
> for you to fetch changes up to 5d26ad9cfb2ebe2aba75342813e7542d5a68d644:
> 
>   arm64: dts: rockchip: add ap_pwroff and ddrio_pwroff pins for rk3399 (2016-06-27 01:07:40 +0200)
> 
> ----------------------------------------------------------------
> The rk3399 gets support for its emmc controller as well as thermal,
> i2c and core io-domain nodes and some reasonable default rates
> for core clocks. The rk3368 also gets io-domains for its r88 board
> as well as a small fix for the gic's memory regions.

Merged, thanks.


-Olof



From daniel.lezcano at linaro.org  Thu Jul  7 01:00:38 2016
From: daniel.lezcano at linaro.org (Daniel Lezcano)
Date: Thu,  7 Jul 2016 10:00:38 +0200
Subject: [PATCH 05/93] dt-bindings: Document rk3399 rk-timer bindings
In-Reply-To: <1467878526-1238-1-git-send-email-daniel.lezcano@linaro.org>
References: <577E0BED.3020608@linaro.org>
 <1467878526-1238-1-git-send-email-daniel.lezcano@linaro.org>
Message-ID: <1467878526-1238-5-git-send-email-daniel.lezcano@linaro.org>

From: Huang Tao <huangtao at rock-chips.com>

Add a compatible string for rk3399 SoC because the timer is slightly different
from the older SoCs. So rename the file name from rockchip,rk3288-timer.txt to
rockchip,rk-timer.txt and clarify rockchip,rk3288-timer supported SoCs.

Signed-off-by: Huang Tao <huangtao at rock-chips.com>
Cc: Rob Herring <robh+dt at kernel.org>
Cc: Daniel Lezcano <daniel.lezcano at linaro.org>
Cc: Thomas Gleixner <tglx at linutronix.de>
Cc: Heiko Stuebner <heiko at sntech.de>
Signed-off-by: Caesar Wang <wxt at rock-chips.com>
Acked-by: Rob Herring <robh at kernel.org>
Signed-off-by: Daniel Lezcano <daniel.lezcano at linaro.org>
---
 .../devicetree/bindings/timer/rockchip,rk-timer.txt  | 20 ++++++++++++++++++++
 .../bindings/timer/rockchip,rk3288-timer.txt         | 18 ------------------
 2 files changed, 20 insertions(+), 18 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/timer/rockchip,rk-timer.txt
 delete mode 100644 Documentation/devicetree/bindings/timer/rockchip,rk3288-timer.txt

diff --git a/Documentation/devicetree/bindings/timer/rockchip,rk-timer.txt b/Documentation/devicetree/bindings/timer/rockchip,rk-timer.txt
new file mode 100644
index 0000000..a41b184
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/rockchip,rk-timer.txt
@@ -0,0 +1,20 @@
+Rockchip rk timer
+
+Required properties:
+- compatible: shall be one of:
+  "rockchip,rk3288-timer" - for rk3066, rk3036, rk3188, rk322x, rk3288, rk3368
+  "rockchip,rk3399-timer" - for rk3399
+- reg: base address of the timer register starting with TIMERS CONTROL register
+- interrupts: should contain the interrupts for Timer0
+- clocks : must contain an entry for each entry in clock-names
+- clock-names : must include the following entries:
+  "timer", "pclk"
+
+Example:
+	timer: timer at ff810000 {
+		compatible = "rockchip,rk3288-timer";
+		reg = <0xff810000 0x20>;
+		interrupts = <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&xin24m>, <&cru PCLK_TIMER>;
+		clock-names = "timer", "pclk";
+	};
diff --git a/Documentation/devicetree/bindings/timer/rockchip,rk3288-timer.txt b/Documentation/devicetree/bindings/timer/rockchip,rk3288-timer.txt
deleted file mode 100644
index 87f0b00..0000000
--- a/Documentation/devicetree/bindings/timer/rockchip,rk3288-timer.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-Rockchip rk3288 timer
-
-Required properties:
-- compatible: shall be "rockchip,rk3288-timer"
-- reg: base address of the timer register starting with TIMERS CONTROL register
-- interrupts: should contain the interrupts for Timer0
-- clocks : must contain an entry for each entry in clock-names
-- clock-names : must include the following entries:
-  "timer", "pclk"
-
-Example:
-	timer: timer at ff810000 {
-		compatible = "rockchip,rk3288-timer";
-		reg = <0xff810000 0x20>;
-		interrupts = <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>;
-		clocks = <&xin24m>, <&cru PCLK_TIMER>;
-		clock-names = "timer", "pclk";
-	};
-- 
1.9.1



From daniel.lezcano at linaro.org  Thu Jul  7 01:00:39 2016
From: daniel.lezcano at linaro.org (Daniel Lezcano)
Date: Thu,  7 Jul 2016 10:00:39 +0200
Subject: [PATCH 06/93] clocksource/drivers/rockchip: Add the dynamic irq flag
 to the timer
In-Reply-To: <1467878526-1238-1-git-send-email-daniel.lezcano@linaro.org>
References: <577E0BED.3020608@linaro.org>
 <1467878526-1238-1-git-send-email-daniel.lezcano@linaro.org>
Message-ID: <1467878526-1238-6-git-send-email-daniel.lezcano@linaro.org>

From: "Huang, Tao" <huangtao at rock-chips.com>

The rockchip timer is a broadcast timer. Add the CLOCK_EVT_FEAT_DYNIRQ flag
and set the cpumask to all possible cpus to save power by avoiding
unnecessary wakeups and IPIs.

Signed-off-by: Huang Tao <huangtao at rock-chips.com>
Cc: Daniel Lezcano <daniel.lezcano at linaro.org>
Cc: Thomas Gleixner <tglx at linutronix.de>
Cc: Heiko Stuebner <heiko at sntech.de>
Tested-by: Jianqun Xu <jay.xu at rock-chips.com>
Signed-off-by: Caesar Wang <wxt at rock-chips.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano at linaro.org>
---
 drivers/clocksource/rockchip_timer.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/clocksource/rockchip_timer.c b/drivers/clocksource/rockchip_timer.c
index b991b28..b510863 100644
--- a/drivers/clocksource/rockchip_timer.c
+++ b/drivers/clocksource/rockchip_timer.c
@@ -150,12 +150,13 @@ static void __init rk_timer_init(struct device_node *np)
 	}
 
 	ce->name = TIMER_NAME;
-	ce->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
+	ce->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT |
+		       CLOCK_EVT_FEAT_DYNIRQ;
 	ce->set_next_event = rk_timer_set_next_event;
 	ce->set_state_shutdown = rk_timer_shutdown;
 	ce->set_state_periodic = rk_timer_set_periodic;
 	ce->irq = irq;
-	ce->cpumask = cpumask_of(0);
+	ce->cpumask = cpu_possible_mask;
 	ce->rating = 250;
 
 	rk_timer_interrupt_clear(ce);
-- 
1.9.1



From daniel.lezcano at linaro.org  Thu Jul  7 01:00:40 2016
From: daniel.lezcano at linaro.org (Daniel Lezcano)
Date: Thu,  7 Jul 2016 10:00:40 +0200
Subject: [PATCH 07/93] clocksource/drivers/rockchip: Add support for the
 rk3399 SoC
In-Reply-To: <1467878526-1238-1-git-send-email-daniel.lezcano@linaro.org>
References: <577E0BED.3020608@linaro.org>
 <1467878526-1238-1-git-send-email-daniel.lezcano@linaro.org>
Message-ID: <1467878526-1238-7-git-send-email-daniel.lezcano@linaro.org>

From: "Huang, Tao" <huangtao at rock-chips.com>

The only difference between the rk3399 SoC and the other ones is the control
register offset which is different.

Add a new field to store the control register address depending on the SoC
and use it instead of the <base> + <control offset>.

Signed-off-by: Huang Tao <huangtao at rock-chips.com>
Cc: Daniel Lezcano <daniel.lezcano at linaro.org>
Cc: Thomas Gleixner <tglx at linutronix.de>
Cc: Heiko Stuebner <heiko at sntech.de>
Tested-by: Jianqun Xu <jay.xu at rock-chips.com>
Signed-off-by: Caesar Wang <wxt at rock-chips.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano at linaro.org>
---
 drivers/clocksource/rockchip_timer.c | 31 ++++++++++++++++++++++++++-----
 1 file changed, 26 insertions(+), 5 deletions(-)

diff --git a/drivers/clocksource/rockchip_timer.c b/drivers/clocksource/rockchip_timer.c
index b510863..a3f22b0 100644
--- a/drivers/clocksource/rockchip_timer.c
+++ b/drivers/clocksource/rockchip_timer.c
@@ -19,7 +19,8 @@
 
 #define TIMER_LOAD_COUNT0	0x00
 #define TIMER_LOAD_COUNT1	0x04
-#define TIMER_CONTROL_REG	0x10
+#define TIMER_CONTROL_REG3288	0x10
+#define TIMER_CONTROL_REG3399	0x1c
 #define TIMER_INT_STATUS	0x18
 
 #define TIMER_DISABLE		0x0
@@ -31,6 +32,7 @@
 struct bc_timer {
 	struct clock_event_device ce;
 	void __iomem *base;
+	void __iomem *ctrl;
 	u32 freq;
 };
 
@@ -46,15 +48,20 @@ static inline void __iomem *rk_base(struct clock_event_device *ce)
 	return rk_timer(ce)->base;
 }
 
+static inline void __iomem *rk_ctrl(struct clock_event_device *ce)
+{
+	return rk_timer(ce)->ctrl;
+}
+
 static inline void rk_timer_disable(struct clock_event_device *ce)
 {
-	writel_relaxed(TIMER_DISABLE, rk_base(ce) + TIMER_CONTROL_REG);
+	writel_relaxed(TIMER_DISABLE, rk_ctrl(ce));
 }
 
 static inline void rk_timer_enable(struct clock_event_device *ce, u32 flags)
 {
 	writel_relaxed(TIMER_ENABLE | TIMER_INT_UNMASK | flags,
-		       rk_base(ce) + TIMER_CONTROL_REG);
+		       rk_ctrl(ce));
 }
 
 static void rk_timer_update_counter(unsigned long cycles,
@@ -106,7 +113,7 @@ static irqreturn_t rk_timer_interrupt(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
-static void __init rk_timer_init(struct device_node *np)
+static void __init rk_timer_init(struct device_node *np, u32 ctrl_reg)
 {
 	struct clock_event_device *ce = &bc_timer.ce;
 	struct clk *timer_clk;
@@ -118,6 +125,7 @@ static void __init rk_timer_init(struct device_node *np)
 		pr_err("Failed to get base address for '%s'\n", TIMER_NAME);
 		return;
 	}
+	bc_timer.ctrl = bc_timer.base + ctrl_reg;
 
 	pclk = of_clk_get_by_name(np, "pclk");
 	if (IS_ERR(pclk)) {
@@ -180,4 +188,17 @@ out_unmap:
 	iounmap(bc_timer.base);
 }
 
-CLOCKSOURCE_OF_DECLARE(rk_timer, "rockchip,rk3288-timer", rk_timer_init);
+static void __init rk3288_timer_init(struct device_node *np)
+{
+	rk_timer_init(np, TIMER_CONTROL_REG3288);
+}
+
+static void __init rk3399_timer_init(struct device_node *np)
+{
+	rk_timer_init(np, TIMER_CONTROL_REG3399);
+}
+
+CLOCKSOURCE_OF_DECLARE(rk3288_timer, "rockchip,rk3288-timer",
+		       rk3288_timer_init);
+CLOCKSOURCE_OF_DECLARE(rk3399_timer, "rockchip,rk3399-timer",
+		       rk3399_timer_init);
-- 
1.9.1



From daniel.lezcano at linaro.org  Thu Jul  7 01:00:41 2016
From: daniel.lezcano at linaro.org (Daniel Lezcano)
Date: Thu,  7 Jul 2016 10:00:41 +0200
Subject: [PATCH 08/93] arm64: dts: rockchip: Add rktimer device node for rk3399
In-Reply-To: <1467878526-1238-1-git-send-email-daniel.lezcano@linaro.org>
References: <577E0BED.3020608@linaro.org>
 <1467878526-1238-1-git-send-email-daniel.lezcano@linaro.org>
Message-ID: <1467878526-1238-8-git-send-email-daniel.lezcano@linaro.org>

From: Huang Tao <huangtao at rock-chips.com>

Add a 'rktimer' node in the device treee for the ARM64 rk3399 SoC.

Signed-off-by: Huang Tao <huangtao at rock-chips.com>
Cc: Daniel Lezcano <daniel.lezcano at linaro.org>
Cc: Thomas Gleixner <tglx at linutronix.de>
Cc: Heiko Stuebner <heiko at sntech.de>
Tested-by: Jianqun Xu <jay.xu at rock-chips.com>
Signed-off-by: Caesar Wang <wxt at rock-chips.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano at linaro.org>
---
 arch/arm64/boot/dts/rockchip/rk3399.dtsi | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index 46f325a..f0c0d76 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -492,6 +492,14 @@
 		interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>;
 	};
 
+	rktimer: rktimer at ff850000 {
+		compatible = "rockchip,rk3399-timer";
+		reg = <0x0 0xff850000 0x0 0x1000>;
+		interrupts = <GIC_SPI 81 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru PCLK_TIMER0>, <&cru SCLK_TIMER00>;
+		clock-names = "pclk", "timer";
+	};
+
 	spdif: spdif at ff870000 {
 		compatible = "rockchip,rk3399-spdif";
 		reg = <0x0 0xff870000 0x0 0x1000>;
-- 
1.9.1



From daniel.lezcano at linaro.org  Thu Jul  7 01:00:46 2016
From: daniel.lezcano at linaro.org (Daniel Lezcano)
Date: Thu,  7 Jul 2016 10:00:46 +0200
Subject: [PATCH 13/93] clocksource/drivers/rockchip_timer: Convert init
 function to return error
In-Reply-To: <1467878526-1238-1-git-send-email-daniel.lezcano@linaro.org>
References: <577E0BED.3020608@linaro.org>
 <1467878526-1238-1-git-send-email-daniel.lezcano@linaro.org>
Message-ID: <1467878526-1238-13-git-send-email-daniel.lezcano@linaro.org>

The init functions do not return any error. They behave as the following:

 - panic, thus leading to a kernel crash while another timer may work and
   make the system boot up correctly

 or

 - print an error and let the caller unaware if the state of the system

Change that by converting the init functions to return an error conforming
to the CLOCKSOURCE_OF_RET prototype.

Proper error handling (rollback, errno value) will be changed later case
by case, thus this change just return back an error or success in the init
function.

Signed-off-by: Daniel Lezcano <daniel.lezcano at linaro.org>
Reviewed-by: Heiko Stuebner <heiko at sntech.de>

on a rk3399-evb
Tested-by: Heiko Stuebner <heiko at sntech.de>
---
 drivers/clocksource/rockchip_timer.c | 35 +++++++++++++++++++++--------------
 1 file changed, 21 insertions(+), 14 deletions(-)

diff --git a/drivers/clocksource/rockchip_timer.c b/drivers/clocksource/rockchip_timer.c
index a3f22b0..85aee69 100644
--- a/drivers/clocksource/rockchip_timer.c
+++ b/drivers/clocksource/rockchip_timer.c
@@ -113,38 +113,42 @@ static irqreturn_t rk_timer_interrupt(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
-static void __init rk_timer_init(struct device_node *np, u32 ctrl_reg)
+static int __init rk_timer_init(struct device_node *np, u32 ctrl_reg)
 {
 	struct clock_event_device *ce = &bc_timer.ce;
 	struct clk *timer_clk;
 	struct clk *pclk;
-	int ret, irq;
+	int ret = -EINVAL, irq;
 
 	bc_timer.base = of_iomap(np, 0);
 	if (!bc_timer.base) {
 		pr_err("Failed to get base address for '%s'\n", TIMER_NAME);
-		return;
+		return -ENXIO;
 	}
 	bc_timer.ctrl = bc_timer.base + ctrl_reg;
 
 	pclk = of_clk_get_by_name(np, "pclk");
 	if (IS_ERR(pclk)) {
+		ret = PTR_ERR(pclk);
 		pr_err("Failed to get pclk for '%s'\n", TIMER_NAME);
 		goto out_unmap;
 	}
 
-	if (clk_prepare_enable(pclk)) {
+	ret = clk_prepare_enable(pclk);
+	if (ret) {
 		pr_err("Failed to enable pclk for '%s'\n", TIMER_NAME);
 		goto out_unmap;
 	}
 
 	timer_clk = of_clk_get_by_name(np, "timer");
 	if (IS_ERR(timer_clk)) {
+		ret = PTR_ERR(timer_clk);
 		pr_err("Failed to get timer clock for '%s'\n", TIMER_NAME);
 		goto out_timer_clk;
 	}
 
-	if (clk_prepare_enable(timer_clk)) {
+	ret = clk_prepare_enable(timer_clk);
+	if (ret) {
 		pr_err("Failed to enable timer clock\n");
 		goto out_timer_clk;
 	}
@@ -153,6 +157,7 @@ static void __init rk_timer_init(struct device_node *np, u32 ctrl_reg)
 
 	irq = irq_of_parse_and_map(np, 0);
 	if (!irq) {
+		ret = -EINVAL;
 		pr_err("Failed to map interrupts for '%s'\n", TIMER_NAME);
 		goto out_irq;
 	}
@@ -178,7 +183,7 @@ static void __init rk_timer_init(struct device_node *np, u32 ctrl_reg)
 
 	clockevents_config_and_register(ce, bc_timer.freq, 1, UINT_MAX);
 
-	return;
+	return 0;
 
 out_irq:
 	clk_disable_unprepare(timer_clk);
@@ -186,19 +191,21 @@ out_timer_clk:
 	clk_disable_unprepare(pclk);
 out_unmap:
 	iounmap(bc_timer.base);
+
+	return ret;
 }
 
-static void __init rk3288_timer_init(struct device_node *np)
+static int __init rk3288_timer_init(struct device_node *np)
 {
-	rk_timer_init(np, TIMER_CONTROL_REG3288);
+	return rk_timer_init(np, TIMER_CONTROL_REG3288);
 }
 
-static void __init rk3399_timer_init(struct device_node *np)
+static int __init rk3399_timer_init(struct device_node *np)
 {
-	rk_timer_init(np, TIMER_CONTROL_REG3399);
+	return rk_timer_init(np, TIMER_CONTROL_REG3399);
 }
 
-CLOCKSOURCE_OF_DECLARE(rk3288_timer, "rockchip,rk3288-timer",
-		       rk3288_timer_init);
-CLOCKSOURCE_OF_DECLARE(rk3399_timer, "rockchip,rk3399-timer",
-		       rk3399_timer_init);
+CLOCKSOURCE_OF_DECLARE_RET(rk3288_timer, "rockchip,rk3288-timer",
+			   rk3288_timer_init);
+CLOCKSOURCE_OF_DECLARE_RET(rk3399_timer, "rockchip,rk3399-timer",
+			   rk3399_timer_init);
-- 
1.9.1



From broonie at kernel.org  Thu Jul  7 01:03:56 2016
From: broonie at kernel.org (Mark Brown)
Date: Thu, 7 Jul 2016 10:03:56 +0200
Subject: [PATCH] regulator: pwm: Fix regulator ramp delay for continuous
 mode
In-Reply-To: <20160706142719.73713123@bbrezillon>
References: <1467089591-7631-1-git-send-email-dianders@chromium.org>
 <20160706142719.73713123@bbrezillon>
Message-ID: <20160707080356.GF6247@sirena.org.uk>

On Wed, Jul 06, 2016 at 02:27:19PM +0200, Boris Brezillon wrote:

> I realize I may have introduced another bug when adding the
> ->enable()/disable() methods: we are only waiting for the ramp up delay
> when changing the voltage, but it should probably be applied when
> enabling the PWM, and conditionally applied when changing the voltage
> only if the PWM is enabled.

Yes, it should be applied on enable.  
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160707/5cee89ac/attachment-0001.sig>

From daniel.lezcano at linaro.org  Thu Jul  7 01:01:45 2016
From: daniel.lezcano at linaro.org (Daniel Lezcano)
Date: Thu,  7 Jul 2016 10:01:45 +0200
Subject: [PATCH 72/93] clocksources: Switch back to the clksrc table
In-Reply-To: <1467878526-1238-1-git-send-email-daniel.lezcano@linaro.org>
References: <577E0BED.3020608@linaro.org>
 <1467878526-1238-1-git-send-email-daniel.lezcano@linaro.org>
Message-ID: <1467878526-1238-72-git-send-email-daniel.lezcano@linaro.org>

All the clocksource drivers's init function are now converted to return
an error code. CLOCKSOURCE_OF_DECLARE is no longer used as well as the
clksrc-of table.

Let's convert back the names:
 - CLOCKSOURCE_OF_DECLARE_RET => CLOCKSOURCE_OF_DECLARE
 - clksrc-of-ret              => clksrc-of

Signed-off-by: Daniel Lezcano <daniel.lezcano at linaro.org>

For exynos_mct and samsung_pwm_timer:
Acked-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>

For arch/arc:
Acked-by: Vineet Gupta <vgupta at synopsys.com>

For mediatek driver:
Acked-by: Matthias Brugger <matthias.bgg at gmail.com>

For the Rockchip-part
Acked-by: Heiko Stuebner <heiko at sntech.de>

For STi :
Acked-by: Patrice Chotard <patrice.chotard at st.com>

For the mps2-timer.c and versatile.c changes:
Acked-by: Liviu Dudau <Liviu.Dudau at arm.com>

For the OXNAS part :
Acked-by: Neil Armstrong <narmstrong at baylibre.com>

For LPC32xx driver:
Acked-by: Sylvain Lemieux <slemieux.tyco at gmail.com>

For Broadcom Kona timer change:
Acked-by: Ray Jui <ray.jui at broadcom.com>

For Sun4i and Sun5i:
Acked-by: Chen-Yu Tsai <wens at csie.org>

For Meson6:
Acked-by: Carlo Caione <carlo at caione.org>

For Keystone:
Acked-by: Santosh Shilimkar <ssantosh at kernel.org>

For NPS:
Acked-by: Noam Camus <noamca at mellanox.com>

For bcm2835:
Acked-by: Eric Anholt <eric at anholt.net>
---
 arch/arc/kernel/time.c                    |  6 +++---
 arch/arm/kernel/smp_twd.c                 |  6 +++---
 arch/microblaze/kernel/timer.c            |  2 +-
 arch/mips/ralink/cevt-rt3352.c            |  2 +-
 arch/nios2/kernel/time.c                  |  2 +-
 drivers/clocksource/arm_arch_timer.c      |  6 +++---
 drivers/clocksource/arm_global_timer.c    |  2 +-
 drivers/clocksource/armv7m_systick.c      |  2 +-
 drivers/clocksource/asm9260_timer.c       |  2 +-
 drivers/clocksource/bcm2835_timer.c       |  2 +-
 drivers/clocksource/bcm_kona_timer.c      |  4 ++--
 drivers/clocksource/cadence_ttc_timer.c   |  2 +-
 drivers/clocksource/clksrc-dbx500-prcmu.c |  2 +-
 drivers/clocksource/clksrc-probe.c        | 14 --------------
 drivers/clocksource/clksrc_st_lpc.c       |  2 +-
 drivers/clocksource/clps711x-timer.c      |  2 +-
 drivers/clocksource/dw_apb_timer_of.c     |  8 ++++----
 drivers/clocksource/exynos_mct.c          |  4 ++--
 drivers/clocksource/fsl_ftm_timer.c       |  2 +-
 drivers/clocksource/h8300_timer16.c       |  2 +-
 drivers/clocksource/h8300_timer8.c        |  2 +-
 drivers/clocksource/h8300_tpu.c           |  2 +-
 drivers/clocksource/meson6_timer.c        |  2 +-
 drivers/clocksource/mips-gic-timer.c      |  2 +-
 drivers/clocksource/moxart_timer.c        |  2 +-
 drivers/clocksource/mps2-timer.c          |  2 +-
 drivers/clocksource/mtk_timer.c           |  2 +-
 drivers/clocksource/mxs_timer.c           |  2 +-
 drivers/clocksource/nomadik-mtu.c         |  2 +-
 drivers/clocksource/pxa_timer.c           |  2 +-
 drivers/clocksource/qcom-timer.c          |  4 ++--
 drivers/clocksource/rockchip_timer.c      |  8 ++++----
 drivers/clocksource/samsung_pwm_timer.c   |  8 ++++----
 drivers/clocksource/sun4i_timer.c         |  2 +-
 drivers/clocksource/tango_xtal.c          |  2 +-
 drivers/clocksource/tegra20_timer.c       |  4 ++--
 drivers/clocksource/time-armada-370-xp.c  |  6 +++---
 drivers/clocksource/time-efm32.c          |  4 ++--
 drivers/clocksource/time-lpc32xx.c        |  2 +-
 drivers/clocksource/time-orion.c          |  2 +-
 drivers/clocksource/time-pistachio.c      |  2 +-
 drivers/clocksource/timer-atlas7.c        |  2 +-
 drivers/clocksource/timer-atmel-pit.c     |  2 +-
 drivers/clocksource/timer-atmel-st.c      |  2 +-
 drivers/clocksource/timer-digicolor.c     |  2 +-
 drivers/clocksource/timer-imx-gpt.c       | 24 ++++++++++++------------
 drivers/clocksource/timer-integrator-ap.c |  2 +-
 drivers/clocksource/timer-keystone.c      |  2 +-
 drivers/clocksource/timer-nps.c           |  4 ++--
 drivers/clocksource/timer-oxnas-rps.c     |  4 ++--
 drivers/clocksource/timer-prima2.c        |  2 +-
 drivers/clocksource/timer-sp804.c         |  4 ++--
 drivers/clocksource/timer-stm32.c         |  2 +-
 drivers/clocksource/timer-sun5i.c         |  4 ++--
 drivers/clocksource/timer-ti-32k.c        |  2 +-
 drivers/clocksource/timer-u300.c          |  2 +-
 drivers/clocksource/versatile.c           |  4 ++--
 drivers/clocksource/vf_pit_timer.c        |  2 +-
 drivers/clocksource/vt8500_timer.c        |  2 +-
 drivers/clocksource/zevio-timer.c         |  2 +-
 include/asm-generic/vmlinux.lds.h         |  2 --
 include/linux/clocksource.h               |  5 +----
 62 files changed, 98 insertions(+), 117 deletions(-)

diff --git a/arch/arc/kernel/time.c b/arch/arc/kernel/time.c
index 09de669..98f22d2 100644
--- a/arch/arc/kernel/time.c
+++ b/arch/arc/kernel/time.c
@@ -130,7 +130,7 @@ static int __init arc_cs_setup_gfrc(struct device_node *node)
 
 	return clocksource_register_hz(&arc_counter_gfrc, arc_timer_freq);
 }
-CLOCKSOURCE_OF_DECLARE_RET(arc_gfrc, "snps,archs-timer-gfrc", arc_cs_setup_gfrc);
+CLOCKSOURCE_OF_DECLARE(arc_gfrc, "snps,archs-timer-gfrc", arc_cs_setup_gfrc);
 
 #endif
 
@@ -192,7 +192,7 @@ static int __init arc_cs_setup_rtc(struct device_node *node)
 
 	return clocksource_register_hz(&arc_counter_rtc, arc_timer_freq);
 }
-CLOCKSOURCE_OF_DECLARE_RET(arc_rtc, "snps,archs-timer-rtc", arc_cs_setup_rtc);
+CLOCKSOURCE_OF_DECLARE(arc_rtc, "snps,archs-timer-rtc", arc_cs_setup_rtc);
 
 #endif
 
@@ -379,7 +379,7 @@ static int __init arc_of_timer_init(struct device_node *np)
 
 	return ret;
 }
-CLOCKSOURCE_OF_DECLARE_RET(arc_clkevt, "snps,arc-timer", arc_of_timer_init);
+CLOCKSOURCE_OF_DECLARE(arc_clkevt, "snps,arc-timer", arc_of_timer_init);
 
 /*
  * Called from start_kernel() - boot CPU only
diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c
index 2b24be4..b6ec65e 100644
--- a/arch/arm/kernel/smp_twd.c
+++ b/arch/arm/kernel/smp_twd.c
@@ -412,7 +412,7 @@ out:
 	WARN(err, "twd_local_timer_of_register failed (%d)\n", err);
 	return err;
 }
-CLOCKSOURCE_OF_DECLARE_RET(arm_twd_a9, "arm,cortex-a9-twd-timer", twd_local_timer_of_register);
-CLOCKSOURCE_OF_DECLARE_RET(arm_twd_a5, "arm,cortex-a5-twd-timer", twd_local_timer_of_register);
-CLOCKSOURCE_OF_DECLARE_RET(arm_twd_11mp, "arm,arm11mp-twd-timer", twd_local_timer_of_register);
+CLOCKSOURCE_OF_DECLARE(arm_twd_a9, "arm,cortex-a9-twd-timer", twd_local_timer_of_register);
+CLOCKSOURCE_OF_DECLARE(arm_twd_a5, "arm,cortex-a5-twd-timer", twd_local_timer_of_register);
+CLOCKSOURCE_OF_DECLARE(arm_twd_11mp, "arm,arm11mp-twd-timer", twd_local_timer_of_register);
 #endif
diff --git a/arch/microblaze/kernel/timer.c b/arch/microblaze/kernel/timer.c
index 7f35e7b..5bbf38b 100644
--- a/arch/microblaze/kernel/timer.c
+++ b/arch/microblaze/kernel/timer.c
@@ -332,5 +332,5 @@ static int __init xilinx_timer_init(struct device_node *timer)
 	return 0;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(xilinx_timer, "xlnx,xps-timer-1.00.a",
+CLOCKSOURCE_OF_DECLARE(xilinx_timer, "xlnx,xps-timer-1.00.a",
 		       xilinx_timer_init);
diff --git a/arch/mips/ralink/cevt-rt3352.c b/arch/mips/ralink/cevt-rt3352.c
index f2d3c79..f24eee0 100644
--- a/arch/mips/ralink/cevt-rt3352.c
+++ b/arch/mips/ralink/cevt-rt3352.c
@@ -150,4 +150,4 @@ static int __init ralink_systick_init(struct device_node *np)
 	return 0;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(systick, "ralink,cevt-systick", ralink_systick_init);
+CLOCKSOURCE_OF_DECLARE(systick, "ralink,cevt-systick", ralink_systick_init);
diff --git a/arch/nios2/kernel/time.c b/arch/nios2/kernel/time.c
index b75e40e..d9563dd 100644
--- a/arch/nios2/kernel/time.c
+++ b/arch/nios2/kernel/time.c
@@ -352,4 +352,4 @@ void __init time_init(void)
 	clocksource_probe();
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(nios2_timer, ALTR_TIMER_COMPATIBLE, nios2_time_init);
+CLOCKSOURCE_OF_DECLARE(nios2_timer, ALTR_TIMER_COMPATIBLE, nios2_time_init);
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index d0cda68..9e33309 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -784,8 +784,8 @@ static int __init arch_timer_of_init(struct device_node *np)
 
 	return arch_timer_init();
 }
-CLOCKSOURCE_OF_DECLARE_RET(armv7_arch_timer, "arm,armv7-timer", arch_timer_of_init);
-CLOCKSOURCE_OF_DECLARE_RET(armv8_arch_timer, "arm,armv8-timer", arch_timer_of_init);
+CLOCKSOURCE_OF_DECLARE(armv7_arch_timer, "arm,armv7-timer", arch_timer_of_init);
+CLOCKSOURCE_OF_DECLARE(armv8_arch_timer, "arm,armv8-timer", arch_timer_of_init);
 
 static int __init arch_timer_mem_init(struct device_node *np)
 {
@@ -868,7 +868,7 @@ out:
 	of_node_put(best_frame);
 	return ret;
 }
-CLOCKSOURCE_OF_DECLARE_RET(armv7_arch_timer_mem, "arm,armv7-timer-mem",
+CLOCKSOURCE_OF_DECLARE(armv7_arch_timer_mem, "arm,armv7-timer-mem",
 		       arch_timer_mem_init);
 
 #ifdef CONFIG_ACPI
diff --git a/drivers/clocksource/arm_global_timer.c b/drivers/clocksource/arm_global_timer.c
index 40104fc..2a9ceb6 100644
--- a/drivers/clocksource/arm_global_timer.c
+++ b/drivers/clocksource/arm_global_timer.c
@@ -358,5 +358,5 @@ out_unmap:
 }
 
 /* Only tested on r2p2 and r3p0  */
-CLOCKSOURCE_OF_DECLARE_RET(arm_gt, "arm,cortex-a9-global-timer",
+CLOCKSOURCE_OF_DECLARE(arm_gt, "arm,cortex-a9-global-timer",
 			global_timer_of_register);
diff --git a/drivers/clocksource/armv7m_systick.c b/drivers/clocksource/armv7m_systick.c
index 2b55410..e93af1f 100644
--- a/drivers/clocksource/armv7m_systick.c
+++ b/drivers/clocksource/armv7m_systick.c
@@ -81,5 +81,5 @@ out_unmap:
 	return ret;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(arm_systick, "arm,armv7m-systick",
+CLOCKSOURCE_OF_DECLARE(arm_systick, "arm,armv7m-systick",
 			system_timer_of_register);
diff --git a/drivers/clocksource/asm9260_timer.c b/drivers/clocksource/asm9260_timer.c
index d113c02..1ba871b 100644
--- a/drivers/clocksource/asm9260_timer.c
+++ b/drivers/clocksource/asm9260_timer.c
@@ -238,5 +238,5 @@ static int __init asm9260_timer_init(struct device_node *np)
 
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(asm9260_timer, "alphascale,asm9260-timer",
+CLOCKSOURCE_OF_DECLARE(asm9260_timer, "alphascale,asm9260-timer",
 		asm9260_timer_init);
diff --git a/drivers/clocksource/bcm2835_timer.c b/drivers/clocksource/bcm2835_timer.c
index 2dcf896..e71acf2 100644
--- a/drivers/clocksource/bcm2835_timer.c
+++ b/drivers/clocksource/bcm2835_timer.c
@@ -142,5 +142,5 @@ static int __init bcm2835_timer_init(struct device_node *node)
 
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(bcm2835, "brcm,bcm2835-system-timer",
+CLOCKSOURCE_OF_DECLARE(bcm2835, "brcm,bcm2835-system-timer",
 			bcm2835_timer_init);
diff --git a/drivers/clocksource/bcm_kona_timer.c b/drivers/clocksource/bcm_kona_timer.c
index c251aa6..86f87be3d 100644
--- a/drivers/clocksource/bcm_kona_timer.c
+++ b/drivers/clocksource/bcm_kona_timer.c
@@ -195,9 +195,9 @@ static int __init kona_timer_init(struct device_node *node)
 	return 0;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(brcm_kona, "brcm,kona-timer", kona_timer_init);
+CLOCKSOURCE_OF_DECLARE(brcm_kona, "brcm,kona-timer", kona_timer_init);
 /*
  * bcm,kona-timer is deprecated by brcm,kona-timer
  * being kept here for driver compatibility
  */
-CLOCKSOURCE_OF_DECLARE_RET(bcm_kona, "bcm,kona-timer", kona_timer_init);
+CLOCKSOURCE_OF_DECLARE(bcm_kona, "bcm,kona-timer", kona_timer_init);
diff --git a/drivers/clocksource/cadence_ttc_timer.c b/drivers/clocksource/cadence_ttc_timer.c
index e2e7631..388a77b 100644
--- a/drivers/clocksource/cadence_ttc_timer.c
+++ b/drivers/clocksource/cadence_ttc_timer.c
@@ -539,4 +539,4 @@ static int __init ttc_timer_init(struct device_node *timer)
 	return 0;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(ttc, "cdns,ttc", ttc_timer_init);
+CLOCKSOURCE_OF_DECLARE(ttc, "cdns,ttc", ttc_timer_init);
diff --git a/drivers/clocksource/clksrc-dbx500-prcmu.c b/drivers/clocksource/clksrc-dbx500-prcmu.c
index 5a59d29..77a365f 100644
--- a/drivers/clocksource/clksrc-dbx500-prcmu.c
+++ b/drivers/clocksource/clksrc-dbx500-prcmu.c
@@ -86,5 +86,5 @@ static int __init clksrc_dbx500_prcmu_init(struct device_node *node)
 #endif
 	return clocksource_register_hz(&clocksource_dbx500_prcmu, RATE_32K);
 }
-CLOCKSOURCE_OF_DECLARE_RET(dbx500_prcmu, "stericsson,db8500-prcmu-timer-4",
+CLOCKSOURCE_OF_DECLARE(dbx500_prcmu, "stericsson,db8500-prcmu-timer-4",
 		       clksrc_dbx500_prcmu_init);
diff --git a/drivers/clocksource/clksrc-probe.c b/drivers/clocksource/clksrc-probe.c
index 5fa6a55..bc62be9 100644
--- a/drivers/clocksource/clksrc-probe.c
+++ b/drivers/clocksource/clksrc-probe.c
@@ -20,19 +20,14 @@
 #include <linux/clocksource.h>
 
 extern struct of_device_id __clksrc_of_table[];
-extern struct of_device_id __clksrc_ret_of_table[];
 
 static const struct of_device_id __clksrc_of_table_sentinel
 	__used __section(__clksrc_of_table_end);
 
-static const struct of_device_id __clksrc_ret_of_table_sentinel
-	__used __section(__clksrc_ret_of_table_end);
-
 void __init clocksource_probe(void)
 {
 	struct device_node *np;
 	const struct of_device_id *match;
-	of_init_fn_1 init_func;
 	of_init_fn_1_ret init_func_ret;
 	unsigned clocksources = 0;
 	int ret;
@@ -41,15 +36,6 @@ void __init clocksource_probe(void)
 		if (!of_device_is_available(np))
 			continue;
 
-		init_func = match->data;
-		init_func(np);
-		clocksources++;
-	}
-
-	for_each_matching_node_and_match(np, __clksrc_ret_of_table, &match) {
-		if (!of_device_is_available(np))
-			continue;
-
 		init_func_ret = match->data;
 
 		ret = init_func_ret(np);
diff --git a/drivers/clocksource/clksrc_st_lpc.c b/drivers/clocksource/clksrc_st_lpc.c
index c9022a9..03cc492 100644
--- a/drivers/clocksource/clksrc_st_lpc.c
+++ b/drivers/clocksource/clksrc_st_lpc.c
@@ -132,4 +132,4 @@ static int __init st_clksrc_of_register(struct device_node *np)
 
 	return ret;
 }
-CLOCKSOURCE_OF_DECLARE_RET(ddata, "st,stih407-lpc", st_clksrc_of_register);
+CLOCKSOURCE_OF_DECLARE(ddata, "st,stih407-lpc", st_clksrc_of_register);
diff --git a/drivers/clocksource/clps711x-timer.c b/drivers/clocksource/clps711x-timer.c
index 3b66198..84aed78 100644
--- a/drivers/clocksource/clps711x-timer.c
+++ b/drivers/clocksource/clps711x-timer.c
@@ -119,5 +119,5 @@ static int __init clps711x_timer_init(struct device_node *np)
 		return -EINVAL;
 	}
 }
-CLOCKSOURCE_OF_DECLARE_RET(clps711x, "cirrus,clps711x-timer", clps711x_timer_init);
+CLOCKSOURCE_OF_DECLARE(clps711x, "cirrus,clps711x-timer", clps711x_timer_init);
 #endif
diff --git a/drivers/clocksource/dw_apb_timer_of.c b/drivers/clocksource/dw_apb_timer_of.c
index 4985a2c..aee6c0d 100644
--- a/drivers/clocksource/dw_apb_timer_of.c
+++ b/drivers/clocksource/dw_apb_timer_of.c
@@ -167,7 +167,7 @@ static int __init dw_apb_timer_init(struct device_node *timer)
 
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(pc3x2_timer, "picochip,pc3x2-timer", dw_apb_timer_init);
-CLOCKSOURCE_OF_DECLARE_RET(apb_timer_osc, "snps,dw-apb-timer-osc", dw_apb_timer_init);
-CLOCKSOURCE_OF_DECLARE_RET(apb_timer_sp, "snps,dw-apb-timer-sp", dw_apb_timer_init);
-CLOCKSOURCE_OF_DECLARE_RET(apb_timer, "snps,dw-apb-timer", dw_apb_timer_init);
+CLOCKSOURCE_OF_DECLARE(pc3x2_timer, "picochip,pc3x2-timer", dw_apb_timer_init);
+CLOCKSOURCE_OF_DECLARE(apb_timer_osc, "snps,dw-apb-timer-osc", dw_apb_timer_init);
+CLOCKSOURCE_OF_DECLARE(apb_timer_sp, "snps,dw-apb-timer-sp", dw_apb_timer_init);
+CLOCKSOURCE_OF_DECLARE(apb_timer, "snps,dw-apb-timer", dw_apb_timer_init);
diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c
index f6caed0..0d18dd4b 100644
--- a/drivers/clocksource/exynos_mct.c
+++ b/drivers/clocksource/exynos_mct.c
@@ -627,5 +627,5 @@ static int __init mct_init_ppi(struct device_node *np)
 {
 	return mct_init_dt(np, MCT_INT_PPI);
 }
-CLOCKSOURCE_OF_DECLARE_RET(exynos4210, "samsung,exynos4210-mct", mct_init_spi);
-CLOCKSOURCE_OF_DECLARE_RET(exynos4412, "samsung,exynos4412-mct", mct_init_ppi);
+CLOCKSOURCE_OF_DECLARE(exynos4210, "samsung,exynos4210-mct", mct_init_spi);
+CLOCKSOURCE_OF_DECLARE(exynos4412, "samsung,exynos4412-mct", mct_init_ppi);
diff --git a/drivers/clocksource/fsl_ftm_timer.c b/drivers/clocksource/fsl_ftm_timer.c
index 9ad4ca3..738515b 100644
--- a/drivers/clocksource/fsl_ftm_timer.c
+++ b/drivers/clocksource/fsl_ftm_timer.c
@@ -369,4 +369,4 @@ err:
 	kfree(priv);
 	return ret;
 }
-CLOCKSOURCE_OF_DECLARE_RET(flextimer, "fsl,ftm-timer", ftm_timer_init);
+CLOCKSOURCE_OF_DECLARE(flextimer, "fsl,ftm-timer", ftm_timer_init);
diff --git a/drivers/clocksource/h8300_timer16.c b/drivers/clocksource/h8300_timer16.c
index 9d99fc8..07d9d5b 100644
--- a/drivers/clocksource/h8300_timer16.c
+++ b/drivers/clocksource/h8300_timer16.c
@@ -187,5 +187,5 @@ free_clk:
 	return ret;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(h8300_16bit, "renesas,16bit-timer",
+CLOCKSOURCE_OF_DECLARE(h8300_16bit, "renesas,16bit-timer",
 			   h8300_16timer_init);
diff --git a/drivers/clocksource/h8300_timer8.c b/drivers/clocksource/h8300_timer8.c
index 0292a19..546bb18 100644
--- a/drivers/clocksource/h8300_timer8.c
+++ b/drivers/clocksource/h8300_timer8.c
@@ -215,4 +215,4 @@ free_clk:
 	return ret;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(h8300_8bit, "renesas,8bit-timer", h8300_8timer_init);
+CLOCKSOURCE_OF_DECLARE(h8300_8bit, "renesas,8bit-timer", h8300_8timer_init);
diff --git a/drivers/clocksource/h8300_tpu.c b/drivers/clocksource/h8300_tpu.c
index 4faf718..7bdf199 100644
--- a/drivers/clocksource/h8300_tpu.c
+++ b/drivers/clocksource/h8300_tpu.c
@@ -154,4 +154,4 @@ free_clk:
 	return ret;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(h8300_tpu, "renesas,tpu", h8300_tpu_init);
+CLOCKSOURCE_OF_DECLARE(h8300_tpu, "renesas,tpu", h8300_tpu_init);
diff --git a/drivers/clocksource/meson6_timer.c b/drivers/clocksource/meson6_timer.c
index 3a6e78f..52af591 100644
--- a/drivers/clocksource/meson6_timer.c
+++ b/drivers/clocksource/meson6_timer.c
@@ -174,5 +174,5 @@ static int __init meson6_timer_init(struct device_node *node)
 					1, 0xfffe);
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(meson6, "amlogic,meson6-timer",
+CLOCKSOURCE_OF_DECLARE(meson6, "amlogic,meson6-timer",
 		       meson6_timer_init);
diff --git a/drivers/clocksource/mips-gic-timer.c b/drivers/clocksource/mips-gic-timer.c
index b164b87..1572c7a 100644
--- a/drivers/clocksource/mips-gic-timer.c
+++ b/drivers/clocksource/mips-gic-timer.c
@@ -222,5 +222,5 @@ static void __init gic_clocksource_of_init(struct device_node *node)
 
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(mips_gic_timer, "mti,gic-timer",
+CLOCKSOURCE_OF_DECLARE(mips_gic_timer, "mti,gic-timer",
 		       gic_clocksource_of_init);
diff --git a/drivers/clocksource/moxart_timer.c b/drivers/clocksource/moxart_timer.c
index b9c30cd..8414544 100644
--- a/drivers/clocksource/moxart_timer.c
+++ b/drivers/clocksource/moxart_timer.c
@@ -178,4 +178,4 @@ static int __init moxart_timer_init(struct device_node *node)
 
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(moxart, "moxa,moxart-timer", moxart_timer_init);
+CLOCKSOURCE_OF_DECLARE(moxart, "moxa,moxart-timer", moxart_timer_init);
diff --git a/drivers/clocksource/mps2-timer.c b/drivers/clocksource/mps2-timer.c
index c303fa9..3e4431e 100644
--- a/drivers/clocksource/mps2-timer.c
+++ b/drivers/clocksource/mps2-timer.c
@@ -274,4 +274,4 @@ static int __init mps2_timer_init(struct device_node *np)
 	return 0;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(mps2_timer, "arm,mps2-timer", mps2_timer_init);
+CLOCKSOURCE_OF_DECLARE(mps2_timer, "arm,mps2-timer", mps2_timer_init);
diff --git a/drivers/clocksource/mtk_timer.c b/drivers/clocksource/mtk_timer.c
index 432a2c0..9065949 100644
--- a/drivers/clocksource/mtk_timer.c
+++ b/drivers/clocksource/mtk_timer.c
@@ -265,4 +265,4 @@ err_kzalloc:
 
 	return -EINVAL;
 }
-CLOCKSOURCE_OF_DECLARE_RET(mtk_mt6577, "mediatek,mt6577-timer", mtk_timer_init);
+CLOCKSOURCE_OF_DECLARE(mtk_mt6577, "mediatek,mt6577-timer", mtk_timer_init);
diff --git a/drivers/clocksource/mxs_timer.c b/drivers/clocksource/mxs_timer.c
index 17b9d19..630a8d3 100644
--- a/drivers/clocksource/mxs_timer.c
+++ b/drivers/clocksource/mxs_timer.c
@@ -295,4 +295,4 @@ static int __init mxs_timer_init(struct device_node *np)
 
 	return setup_irq(irq, &mxs_timer_irq);
 }
-CLOCKSOURCE_OF_DECLARE_RET(mxs, "fsl,timrot", mxs_timer_init);
+CLOCKSOURCE_OF_DECLARE(mxs, "fsl,timrot", mxs_timer_init);
diff --git a/drivers/clocksource/nomadik-mtu.c b/drivers/clocksource/nomadik-mtu.c
index d2be5b3..3c124d1 100644
--- a/drivers/clocksource/nomadik-mtu.c
+++ b/drivers/clocksource/nomadik-mtu.c
@@ -284,5 +284,5 @@ static int __init nmdk_timer_of_init(struct device_node *node)
 
 	return nmdk_timer_init(base, irq, pclk, clk);
 }
-CLOCKSOURCE_OF_DECLARE_RET(nomadik_mtu, "st,nomadik-mtu",
+CLOCKSOURCE_OF_DECLARE(nomadik_mtu, "st,nomadik-mtu",
 		       nmdk_timer_of_init);
diff --git a/drivers/clocksource/pxa_timer.c b/drivers/clocksource/pxa_timer.c
index 59af75c..937e10b 100644
--- a/drivers/clocksource/pxa_timer.c
+++ b/drivers/clocksource/pxa_timer.c
@@ -213,7 +213,7 @@ static int __init pxa_timer_dt_init(struct device_node *np)
 
 	return pxa_timer_common_init(irq, clk_get_rate(clk));
 }
-CLOCKSOURCE_OF_DECLARE_RET(pxa_timer, "marvell,pxa-timer", pxa_timer_dt_init);
+CLOCKSOURCE_OF_DECLARE(pxa_timer, "marvell,pxa-timer", pxa_timer_dt_init);
 
 /*
  * Legacy timer init for non device-tree boards.
diff --git a/drivers/clocksource/qcom-timer.c b/drivers/clocksource/qcom-timer.c
index 79f73bd..6625763 100644
--- a/drivers/clocksource/qcom-timer.c
+++ b/drivers/clocksource/qcom-timer.c
@@ -273,5 +273,5 @@ static int __init msm_dt_timer_init(struct device_node *np)
 
 	return msm_timer_init(freq, 32, irq, !!percpu_offset);
 }
-CLOCKSOURCE_OF_DECLARE_RET(kpss_timer, "qcom,kpss-timer", msm_dt_timer_init);
-CLOCKSOURCE_OF_DECLARE_RET(scss_timer, "qcom,scss-timer", msm_dt_timer_init);
+CLOCKSOURCE_OF_DECLARE(kpss_timer, "qcom,kpss-timer", msm_dt_timer_init);
+CLOCKSOURCE_OF_DECLARE(scss_timer, "qcom,scss-timer", msm_dt_timer_init);
diff --git a/drivers/clocksource/rockchip_timer.c b/drivers/clocksource/rockchip_timer.c
index 85aee69..23e267a 100644
--- a/drivers/clocksource/rockchip_timer.c
+++ b/drivers/clocksource/rockchip_timer.c
@@ -205,7 +205,7 @@ static int __init rk3399_timer_init(struct device_node *np)
 	return rk_timer_init(np, TIMER_CONTROL_REG3399);
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(rk3288_timer, "rockchip,rk3288-timer",
-			   rk3288_timer_init);
-CLOCKSOURCE_OF_DECLARE_RET(rk3399_timer, "rockchip,rk3399-timer",
-			   rk3399_timer_init);
+CLOCKSOURCE_OF_DECLARE(rk3288_timer, "rockchip,rk3288-timer",
+		       rk3288_timer_init);
+CLOCKSOURCE_OF_DECLARE(rk3399_timer, "rockchip,rk3399-timer",
+		       rk3399_timer_init);
diff --git a/drivers/clocksource/samsung_pwm_timer.c b/drivers/clocksource/samsung_pwm_timer.c
index 27a9797..54565bd 100644
--- a/drivers/clocksource/samsung_pwm_timer.c
+++ b/drivers/clocksource/samsung_pwm_timer.c
@@ -466,7 +466,7 @@ static int __init s3c2410_pwm_clocksource_init(struct device_node *np)
 {
 	return samsung_pwm_alloc(np, &s3c24xx_variant);
 }
-CLOCKSOURCE_OF_DECLARE_RET(s3c2410_pwm, "samsung,s3c2410-pwm", s3c2410_pwm_clocksource_init);
+CLOCKSOURCE_OF_DECLARE(s3c2410_pwm, "samsung,s3c2410-pwm", s3c2410_pwm_clocksource_init);
 
 static const struct samsung_pwm_variant s3c64xx_variant = {
 	.bits		= 32,
@@ -479,7 +479,7 @@ static int __init s3c64xx_pwm_clocksource_init(struct device_node *np)
 {
 	return samsung_pwm_alloc(np, &s3c64xx_variant);
 }
-CLOCKSOURCE_OF_DECLARE_RET(s3c6400_pwm, "samsung,s3c6400-pwm", s3c64xx_pwm_clocksource_init);
+CLOCKSOURCE_OF_DECLARE(s3c6400_pwm, "samsung,s3c6400-pwm", s3c64xx_pwm_clocksource_init);
 
 static const struct samsung_pwm_variant s5p64x0_variant = {
 	.bits		= 32,
@@ -492,7 +492,7 @@ static int __init s5p64x0_pwm_clocksource_init(struct device_node *np)
 {
 	return samsung_pwm_alloc(np, &s5p64x0_variant);
 }
-CLOCKSOURCE_OF_DECLARE_RET(s5p6440_pwm, "samsung,s5p6440-pwm", s5p64x0_pwm_clocksource_init);
+CLOCKSOURCE_OF_DECLARE(s5p6440_pwm, "samsung,s5p6440-pwm", s5p64x0_pwm_clocksource_init);
 
 static const struct samsung_pwm_variant s5p_variant = {
 	.bits		= 32,
@@ -505,5 +505,5 @@ static int __init s5p_pwm_clocksource_init(struct device_node *np)
 {
 	return samsung_pwm_alloc(np, &s5p_variant);
 }
-CLOCKSOURCE_OF_DECLARE_RET(s5pc100_pwm, "samsung,s5pc100-pwm", s5p_pwm_clocksource_init);
+CLOCKSOURCE_OF_DECLARE(s5pc100_pwm, "samsung,s5pc100-pwm", s5p_pwm_clocksource_init);
 #endif
diff --git a/drivers/clocksource/sun4i_timer.c b/drivers/clocksource/sun4i_timer.c
index 4453730..97669ee 100644
--- a/drivers/clocksource/sun4i_timer.c
+++ b/drivers/clocksource/sun4i_timer.c
@@ -226,5 +226,5 @@ static int __init sun4i_timer_init(struct device_node *node)
 
 	return ret;
 }
-CLOCKSOURCE_OF_DECLARE_RET(sun4i, "allwinner,sun4i-a10-timer",
+CLOCKSOURCE_OF_DECLARE(sun4i, "allwinner,sun4i-a10-timer",
 		       sun4i_timer_init);
diff --git a/drivers/clocksource/tango_xtal.c b/drivers/clocksource/tango_xtal.c
index 7dc716c..12fcef8 100644
--- a/drivers/clocksource/tango_xtal.c
+++ b/drivers/clocksource/tango_xtal.c
@@ -53,4 +53,4 @@ static int __init tango_clocksource_init(struct device_node *np)
 	return 0;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(tango, "sigma,tick-counter", tango_clocksource_init);
+CLOCKSOURCE_OF_DECLARE(tango, "sigma,tick-counter", tango_clocksource_init);
diff --git a/drivers/clocksource/tegra20_timer.c b/drivers/clocksource/tegra20_timer.c
index 543c37e..f960891 100644
--- a/drivers/clocksource/tegra20_timer.c
+++ b/drivers/clocksource/tegra20_timer.c
@@ -237,7 +237,7 @@ static int __init tegra20_init_timer(struct device_node *np)
 
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(tegra20_timer, "nvidia,tegra20-timer", tegra20_init_timer);
+CLOCKSOURCE_OF_DECLARE(tegra20_timer, "nvidia,tegra20-timer", tegra20_init_timer);
 
 static int __init tegra20_init_rtc(struct device_node *np)
 {
@@ -261,4 +261,4 @@ static int __init tegra20_init_rtc(struct device_node *np)
 
 	return register_persistent_clock(NULL, tegra_read_persistent_clock64);
 }
-CLOCKSOURCE_OF_DECLARE_RET(tegra20_rtc, "nvidia,tegra20-rtc", tegra20_init_rtc);
+CLOCKSOURCE_OF_DECLARE(tegra20_rtc, "nvidia,tegra20-rtc", tegra20_init_rtc);
diff --git a/drivers/clocksource/time-armada-370-xp.c b/drivers/clocksource/time-armada-370-xp.c
index bc4ab48..a4e5923 100644
--- a/drivers/clocksource/time-armada-370-xp.c
+++ b/drivers/clocksource/time-armada-370-xp.c
@@ -371,7 +371,7 @@ static int __init armada_xp_timer_init(struct device_node *np)
 
 	return armada_370_xp_timer_common_init(np);
 }
-CLOCKSOURCE_OF_DECLARE_RET(armada_xp, "marvell,armada-xp-timer",
+CLOCKSOURCE_OF_DECLARE(armada_xp, "marvell,armada-xp-timer",
 		       armada_xp_timer_init);
 
 static int __init armada_375_timer_init(struct device_node *np)
@@ -409,7 +409,7 @@ static int __init armada_375_timer_init(struct device_node *np)
 
 	return armada_370_xp_timer_common_init(np);
 }
-CLOCKSOURCE_OF_DECLARE_RET(armada_375, "marvell,armada-375-timer",
+CLOCKSOURCE_OF_DECLARE(armada_375, "marvell,armada-375-timer",
 		       armada_375_timer_init);
 
 static int __init armada_370_timer_init(struct device_node *np)
@@ -432,5 +432,5 @@ static int __init armada_370_timer_init(struct device_node *np)
 
 	return armada_370_xp_timer_common_init(np);
 }
-CLOCKSOURCE_OF_DECLARE_RET(armada_370, "marvell,armada-370-timer",
+CLOCKSOURCE_OF_DECLARE(armada_370, "marvell,armada-370-timer",
 		       armada_370_timer_init);
diff --git a/drivers/clocksource/time-efm32.c b/drivers/clocksource/time-efm32.c
index 6e2f79f..5ac344b 100644
--- a/drivers/clocksource/time-efm32.c
+++ b/drivers/clocksource/time-efm32.c
@@ -283,5 +283,5 @@ static int __init efm32_timer_init(struct device_node *np)
 
 	return ret;
 }
-CLOCKSOURCE_OF_DECLARE_RET(efm32compat, "efm32,timer", efm32_timer_init);
-CLOCKSOURCE_OF_DECLARE_RET(efm32, "energymicro,efm32-timer", efm32_timer_init);
+CLOCKSOURCE_OF_DECLARE(efm32compat, "efm32,timer", efm32_timer_init);
+CLOCKSOURCE_OF_DECLARE(efm32, "energymicro,efm32-timer", efm32_timer_init);
diff --git a/drivers/clocksource/time-lpc32xx.c b/drivers/clocksource/time-lpc32xx.c
index cb5b866..9649cfd 100644
--- a/drivers/clocksource/time-lpc32xx.c
+++ b/drivers/clocksource/time-lpc32xx.c
@@ -311,4 +311,4 @@ static int __init lpc32xx_timer_init(struct device_node *np)
 
 	return ret;
 }
-CLOCKSOURCE_OF_DECLARE_RET(lpc32xx_timer, "nxp,lpc3220-timer", lpc32xx_timer_init);
+CLOCKSOURCE_OF_DECLARE(lpc32xx_timer, "nxp,lpc3220-timer", lpc32xx_timer_init);
diff --git a/drivers/clocksource/time-orion.c b/drivers/clocksource/time-orion.c
index 5fdeb5d..a28f496 100644
--- a/drivers/clocksource/time-orion.c
+++ b/drivers/clocksource/time-orion.c
@@ -167,4 +167,4 @@ static int __init orion_timer_init(struct device_node *np)
 
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(orion_timer, "marvell,orion-timer", orion_timer_init);
+CLOCKSOURCE_OF_DECLARE(orion_timer, "marvell,orion-timer", orion_timer_init);
diff --git a/drivers/clocksource/time-pistachio.c b/drivers/clocksource/time-pistachio.c
index adaaec5..a7d9a08 100644
--- a/drivers/clocksource/time-pistachio.c
+++ b/drivers/clocksource/time-pistachio.c
@@ -214,5 +214,5 @@ static int __init pistachio_clksrc_of_init(struct device_node *node)
 	sched_clock_register(pistachio_read_sched_clock, 32, rate);
 	return clocksource_register_hz(&pcs_gpt.cs, rate);
 }
-CLOCKSOURCE_OF_DECLARE_RET(pistachio_gptimer, "img,pistachio-gptimer",
+CLOCKSOURCE_OF_DECLARE(pistachio_gptimer, "img,pistachio-gptimer",
 		       pistachio_clksrc_of_init);
diff --git a/drivers/clocksource/timer-atlas7.c b/drivers/clocksource/timer-atlas7.c
index 7b1a007..90f8fbc 100644
--- a/drivers/clocksource/timer-atlas7.c
+++ b/drivers/clocksource/timer-atlas7.c
@@ -304,4 +304,4 @@ static int __init sirfsoc_of_timer_init(struct device_node *np)
 
 	return sirfsoc_atlas7_timer_init(np);
 }
-CLOCKSOURCE_OF_DECLARE_RET(sirfsoc_atlas7_timer, "sirf,atlas7-tick", sirfsoc_of_timer_init);
+CLOCKSOURCE_OF_DECLARE(sirfsoc_atlas7_timer, "sirf,atlas7-tick", sirfsoc_of_timer_init);
diff --git a/drivers/clocksource/timer-atmel-pit.c b/drivers/clocksource/timer-atmel-pit.c
index ffaca7c..1ffac0c 100644
--- a/drivers/clocksource/timer-atmel-pit.c
+++ b/drivers/clocksource/timer-atmel-pit.c
@@ -270,5 +270,5 @@ static int __init at91sam926x_pit_dt_init(struct device_node *node)
 
 	return at91sam926x_pit_common_init(data);
 }
-CLOCKSOURCE_OF_DECLARE_RET(at91sam926x_pit, "atmel,at91sam9260-pit",
+CLOCKSOURCE_OF_DECLARE(at91sam926x_pit, "atmel,at91sam9260-pit",
 		       at91sam926x_pit_dt_init);
diff --git a/drivers/clocksource/timer-atmel-st.c b/drivers/clocksource/timer-atmel-st.c
index e9331d3..e90ab5b 100644
--- a/drivers/clocksource/timer-atmel-st.c
+++ b/drivers/clocksource/timer-atmel-st.c
@@ -260,5 +260,5 @@ static int __init atmel_st_timer_init(struct device_node *node)
 	/* register clocksource */
 	return clocksource_register_hz(&clk32k, sclk_rate);
 }
-CLOCKSOURCE_OF_DECLARE_RET(atmel_st_timer, "atmel,at91rm9200-st",
+CLOCKSOURCE_OF_DECLARE(atmel_st_timer, "atmel,at91rm9200-st",
 		       atmel_st_timer_init);
diff --git a/drivers/clocksource/timer-digicolor.c b/drivers/clocksource/timer-digicolor.c
index b929061..10318cc 100644
--- a/drivers/clocksource/timer-digicolor.c
+++ b/drivers/clocksource/timer-digicolor.c
@@ -202,5 +202,5 @@ static int __init digicolor_timer_init(struct device_node *node)
 
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(conexant_digicolor, "cnxt,cx92755-timer",
+CLOCKSOURCE_OF_DECLARE(conexant_digicolor, "cnxt,cx92755-timer",
 		       digicolor_timer_init);
diff --git a/drivers/clocksource/timer-imx-gpt.c b/drivers/clocksource/timer-imx-gpt.c
index d5640a74..f595460 100644
--- a/drivers/clocksource/timer-imx-gpt.c
+++ b/drivers/clocksource/timer-imx-gpt.c
@@ -545,15 +545,15 @@ static int __init imx6dl_timer_init_dt(struct device_node *np)
 	return mxc_timer_init_dt(np, GPT_TYPE_IMX6DL);
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(imx1_timer, "fsl,imx1-gpt", imx1_timer_init_dt);
-CLOCKSOURCE_OF_DECLARE_RET(imx21_timer, "fsl,imx21-gpt", imx21_timer_init_dt);
-CLOCKSOURCE_OF_DECLARE_RET(imx27_timer, "fsl,imx27-gpt", imx21_timer_init_dt);
-CLOCKSOURCE_OF_DECLARE_RET(imx31_timer, "fsl,imx31-gpt", imx31_timer_init_dt);
-CLOCKSOURCE_OF_DECLARE_RET(imx25_timer, "fsl,imx25-gpt", imx31_timer_init_dt);
-CLOCKSOURCE_OF_DECLARE_RET(imx50_timer, "fsl,imx50-gpt", imx31_timer_init_dt);
-CLOCKSOURCE_OF_DECLARE_RET(imx51_timer, "fsl,imx51-gpt", imx31_timer_init_dt);
-CLOCKSOURCE_OF_DECLARE_RET(imx53_timer, "fsl,imx53-gpt", imx31_timer_init_dt);
-CLOCKSOURCE_OF_DECLARE_RET(imx6q_timer, "fsl,imx6q-gpt", imx31_timer_init_dt);
-CLOCKSOURCE_OF_DECLARE_RET(imx6dl_timer, "fsl,imx6dl-gpt", imx6dl_timer_init_dt);
-CLOCKSOURCE_OF_DECLARE_RET(imx6sl_timer, "fsl,imx6sl-gpt", imx6dl_timer_init_dt);
-CLOCKSOURCE_OF_DECLARE_RET(imx6sx_timer, "fsl,imx6sx-gpt", imx6dl_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx1_timer, "fsl,imx1-gpt", imx1_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx21_timer, "fsl,imx21-gpt", imx21_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx27_timer, "fsl,imx27-gpt", imx21_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx31_timer, "fsl,imx31-gpt", imx31_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx25_timer, "fsl,imx25-gpt", imx31_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx50_timer, "fsl,imx50-gpt", imx31_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx51_timer, "fsl,imx51-gpt", imx31_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx53_timer, "fsl,imx53-gpt", imx31_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx6q_timer, "fsl,imx6q-gpt", imx31_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx6dl_timer, "fsl,imx6dl-gpt", imx6dl_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx6sl_timer, "fsl,imx6sl-gpt", imx6dl_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx6sx_timer, "fsl,imx6sx-gpt", imx6dl_timer_init_dt);
diff --git a/drivers/clocksource/timer-integrator-ap.c b/drivers/clocksource/timer-integrator-ap.c
index 675face..df6e672 100644
--- a/drivers/clocksource/timer-integrator-ap.c
+++ b/drivers/clocksource/timer-integrator-ap.c
@@ -232,5 +232,5 @@ static int __init integrator_ap_timer_init_of(struct device_node *node)
 	return 0;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(integrator_ap_timer, "arm,integrator-timer",
+CLOCKSOURCE_OF_DECLARE(integrator_ap_timer, "arm,integrator-timer",
 		       integrator_ap_timer_init_of);
diff --git a/drivers/clocksource/timer-keystone.c b/drivers/clocksource/timer-keystone.c
index 4199823..ab68a47 100644
--- a/drivers/clocksource/timer-keystone.c
+++ b/drivers/clocksource/timer-keystone.c
@@ -226,5 +226,5 @@ err:
 	return error;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(keystone_timer, "ti,keystone-timer",
+CLOCKSOURCE_OF_DECLARE(keystone_timer, "ti,keystone-timer",
 			   keystone_timer_init);
diff --git a/drivers/clocksource/timer-nps.c b/drivers/clocksource/timer-nps.c
index b5c7b2b..70c149a 100644
--- a/drivers/clocksource/timer-nps.c
+++ b/drivers/clocksource/timer-nps.c
@@ -96,5 +96,5 @@ static int __init nps_timer_init(struct device_node *node)
 	return nps_setup_clocksource(node, clk);
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(ezchip_nps400_clksrc, "ezchip,nps400-timer",
-			   nps_timer_init);
+CLOCKSOURCE_OF_DECLARE(ezchip_nps400_clksrc, "ezchip,nps400-timer",
+		       nps_timer_init);
diff --git a/drivers/clocksource/timer-oxnas-rps.c b/drivers/clocksource/timer-oxnas-rps.c
index 0d99f40..bd887e2 100644
--- a/drivers/clocksource/timer-oxnas-rps.c
+++ b/drivers/clocksource/timer-oxnas-rps.c
@@ -293,5 +293,5 @@ err_alloc:
 	return ret;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(ox810se_rps,
-			   "oxsemi,ox810se-rps-timer", oxnas_rps_timer_init);
+CLOCKSOURCE_OF_DECLARE(ox810se_rps,
+		       "oxsemi,ox810se-rps-timer", oxnas_rps_timer_init);
diff --git a/drivers/clocksource/timer-prima2.c b/drivers/clocksource/timer-prima2.c
index 7b1084d..dae8a66 100644
--- a/drivers/clocksource/timer-prima2.c
+++ b/drivers/clocksource/timer-prima2.c
@@ -246,5 +246,5 @@ static int __init sirfsoc_prima2_timer_init(struct device_node *np)
 
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(sirfsoc_prima2_timer,
+CLOCKSOURCE_OF_DECLARE(sirfsoc_prima2_timer,
 	"sirf,prima2-tick", sirfsoc_prima2_timer_init);
diff --git a/drivers/clocksource/timer-sp804.c b/drivers/clocksource/timer-sp804.c
index 3dc47ef..d078633 100644
--- a/drivers/clocksource/timer-sp804.c
+++ b/drivers/clocksource/timer-sp804.c
@@ -287,7 +287,7 @@ err:
 	iounmap(base);
 	return ret;
 }
-CLOCKSOURCE_OF_DECLARE_RET(sp804, "arm,sp804", sp804_of_init);
+CLOCKSOURCE_OF_DECLARE(sp804, "arm,sp804", sp804_of_init);
 
 static int __init integrator_cp_of_init(struct device_node *np)
 {
@@ -335,4 +335,4 @@ err:
 	iounmap(base);
 	return ret;
 }
-CLOCKSOURCE_OF_DECLARE_RET(intcp, "arm,integrator-cp-timer", integrator_cp_of_init);
+CLOCKSOURCE_OF_DECLARE(intcp, "arm,integrator-cp-timer", integrator_cp_of_init);
diff --git a/drivers/clocksource/timer-stm32.c b/drivers/clocksource/timer-stm32.c
index d5bf352..1b2574c 100644
--- a/drivers/clocksource/timer-stm32.c
+++ b/drivers/clocksource/timer-stm32.c
@@ -187,4 +187,4 @@ err_clk_get:
 	return ret;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(stm32, "st,stm32-timer", stm32_clockevent_init);
+CLOCKSOURCE_OF_DECLARE(stm32, "st,stm32-timer", stm32_clockevent_init);
diff --git a/drivers/clocksource/timer-sun5i.c b/drivers/clocksource/timer-sun5i.c
index f0a3ffb..c184eb8 100644
--- a/drivers/clocksource/timer-sun5i.c
+++ b/drivers/clocksource/timer-sun5i.c
@@ -346,7 +346,7 @@ static int __init sun5i_timer_init(struct device_node *node)
 
 	return sun5i_setup_clockevent(node, timer_base, clk, irq);
 }
-CLOCKSOURCE_OF_DECLARE_RET(sun5i_a13, "allwinner,sun5i-a13-hstimer",
+CLOCKSOURCE_OF_DECLARE(sun5i_a13, "allwinner,sun5i-a13-hstimer",
 			   sun5i_timer_init);
-CLOCKSOURCE_OF_DECLARE_RET(sun7i_a20, "allwinner,sun7i-a20-hstimer",
+CLOCKSOURCE_OF_DECLARE(sun7i_a20, "allwinner,sun7i-a20-hstimer",
 			   sun5i_timer_init);
diff --git a/drivers/clocksource/timer-ti-32k.c b/drivers/clocksource/timer-ti-32k.c
index e4ad3c6e..92b7e39 100644
--- a/drivers/clocksource/timer-ti-32k.c
+++ b/drivers/clocksource/timer-ti-32k.c
@@ -124,5 +124,5 @@ static int __init ti_32k_timer_init(struct device_node *np)
 
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(ti_32k_timer, "ti,omap-counter32k",
+CLOCKSOURCE_OF_DECLARE(ti_32k_timer, "ti,omap-counter32k",
 		ti_32k_timer_init);
diff --git a/drivers/clocksource/timer-u300.c b/drivers/clocksource/timer-u300.c
index a6a0dec..704e40c 100644
--- a/drivers/clocksource/timer-u300.c
+++ b/drivers/clocksource/timer-u300.c
@@ -458,5 +458,5 @@ static int __init u300_timer_init_of(struct device_node *np)
 	return 0;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(u300_timer, "stericsson,u300-apptimer",
+CLOCKSOURCE_OF_DECLARE(u300_timer, "stericsson,u300-apptimer",
 		       u300_timer_init_of);
diff --git a/drivers/clocksource/versatile.c b/drivers/clocksource/versatile.c
index 8daeffa..220b490 100644
--- a/drivers/clocksource/versatile.c
+++ b/drivers/clocksource/versatile.c
@@ -38,7 +38,7 @@ static int __init versatile_sched_clock_init(struct device_node *node)
 
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(vexpress, "arm,vexpress-sysreg",
+CLOCKSOURCE_OF_DECLARE(vexpress, "arm,vexpress-sysreg",
 		       versatile_sched_clock_init);
-CLOCKSOURCE_OF_DECLARE_RET(versatile, "arm,versatile-sysreg",
+CLOCKSOURCE_OF_DECLARE(versatile, "arm,versatile-sysreg",
 		       versatile_sched_clock_init);
diff --git a/drivers/clocksource/vf_pit_timer.c b/drivers/clocksource/vf_pit_timer.c
index ca4dff4..55d8d84 100644
--- a/drivers/clocksource/vf_pit_timer.c
+++ b/drivers/clocksource/vf_pit_timer.c
@@ -201,4 +201,4 @@ static int __init pit_timer_init(struct device_node *np)
 
 	return pit_clockevent_init(clk_rate, irq);
 }
-CLOCKSOURCE_OF_DECLARE_RET(vf610, "fsl,vf610-pit", pit_timer_init);
+CLOCKSOURCE_OF_DECLARE(vf610, "fsl,vf610-pit", pit_timer_init);
diff --git a/drivers/clocksource/vt8500_timer.c b/drivers/clocksource/vt8500_timer.c
index 1bc8707..b150694 100644
--- a/drivers/clocksource/vt8500_timer.c
+++ b/drivers/clocksource/vt8500_timer.c
@@ -165,4 +165,4 @@ static int __init vt8500_timer_init(struct device_node *np)
 	return 0;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(vt8500, "via,vt8500-timer", vt8500_timer_init);
+CLOCKSOURCE_OF_DECLARE(vt8500, "via,vt8500-timer", vt8500_timer_init);
diff --git a/drivers/clocksource/zevio-timer.c b/drivers/clocksource/zevio-timer.c
index cb4cf056..9a53f5e 100644
--- a/drivers/clocksource/zevio-timer.c
+++ b/drivers/clocksource/zevio-timer.c
@@ -215,4 +215,4 @@ static int __init zevio_timer_init(struct device_node *node)
 	return zevio_timer_add(node);
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(zevio_timer, "lsi,zevio-timer", zevio_timer_init);
+CLOCKSOURCE_OF_DECLARE(zevio_timer, "lsi,zevio-timer", zevio_timer_init);
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 8c6c626..6a67ab9 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -173,7 +173,6 @@
 	*(__##name##_of_table_end)
 
 #define CLKSRC_OF_TABLES()	OF_TABLE(CONFIG_CLKSRC_OF, clksrc)
-#define CLKSRC_RET_OF_TABLES()	OF_TABLE(CONFIG_CLKSRC_OF, clksrc_ret)
 #define IRQCHIP_OF_MATCH_TABLE() OF_TABLE(CONFIG_IRQCHIP, irqchip)
 #define CLK_OF_TABLES()		OF_TABLE(CONFIG_COMMON_CLK, clk)
 #define IOMMU_OF_TABLES()	OF_TABLE(CONFIG_OF_IOMMU, iommu)
@@ -532,7 +531,6 @@
 	CLK_OF_TABLES()							\
 	RESERVEDMEM_OF_TABLES()						\
 	CLKSRC_OF_TABLES()						\
-	CLKSRC_RET_OF_TABLES()						\
 	IOMMU_OF_TABLES()						\
 	CPU_METHOD_OF_TABLES()						\
 	CPUIDLE_METHOD_OF_TABLES()					\
diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h
index 15c3839..0839818 100644
--- a/include/linux/clocksource.h
+++ b/include/linux/clocksource.h
@@ -244,10 +244,7 @@ extern int clocksource_mmio_init(void __iomem *, const char *,
 extern int clocksource_i8253_init(void);
 
 #define CLOCKSOURCE_OF_DECLARE(name, compat, fn) \
-	OF_DECLARE_1(clksrc, name, compat, fn)
-
-#define CLOCKSOURCE_OF_DECLARE_RET(name, compat, fn) \
-	OF_DECLARE_1_RET(clksrc_ret, name, compat, fn)
+	OF_DECLARE_1_RET(clksrc, name, compat, fn)
 
 #ifdef CONFIG_CLKSRC_PROBE
 extern void clocksource_probe(void);
-- 
1.9.1



From broonie at kernel.org  Thu Jul  7 01:16:40 2016
From: broonie at kernel.org (Mark Brown)
Date: Thu, 7 Jul 2016 10:16:40 +0200
Subject: [PATCH] regulator: pwm: Fix regulator ramp delay for continuous
 mode
In-Reply-To: <CAD=FV=W7kjMsXR3aRogCpEZWgvt2UZ7rX0B2Zg9yZOW7oGwepw@mail.gmail.com>
References: <1467089591-7631-1-git-send-email-dianders@chromium.org>
 <20160706142719.73713123@bbrezillon>
 <CAD=FV=W7kjMsXR3aRogCpEZWgvt2UZ7rX0B2Zg9yZOW7oGwepw@mail.gmail.com>
Message-ID: <20160707081640.GG6247@sirena.org.uk>

On Wed, Jul 06, 2016 at 11:31:45AM -0700, Doug Anderson wrote:

> I'll certainly let Mark comment here, but:

> * For enabling the PWM, I think you want want "regulator-enable-ramp-delay".

In the absence of that we should just be treating it as a voltage change
from zero.  Some regulators do have different characteristics on initial
ramp up but if we don't know about them we ought to just use the regular
one.  Note that the core does have standard code to apply delays which
may just be working here.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160707/376b9a73/attachment.sig>

From broonie at kernel.org  Thu Jul  7 01:17:11 2016
From: broonie at kernel.org (Mark Brown)
Date: Thu, 7 Jul 2016 10:17:11 +0200
Subject: [PATCH] regulator: pwm: Fix regulator ramp delay for continuous
 mode
In-Reply-To: <CAD=FV=W2JDP3EAdz9CWUKbcoCz34Mm7oCF2QCD4W45tyBOaP+A@mail.gmail.com>
References: <1467089591-7631-1-git-send-email-dianders@chromium.org>
 <20160701080614.GA9196@sirena.org.uk>
 <CAD=FV=WKR_Fq6o34886RCY-T-6AKdRqFz9H1aTiRq7Zu=o8TWA@mail.gmail.com>
 <20160702075939.GD6247@sirena.org.uk>
 <CAD=FV=W2JDP3EAdz9CWUKbcoCz34Mm7oCF2QCD4W45tyBOaP+A@mail.gmail.com>
Message-ID: <20160707081711.GH6247@sirena.org.uk>

On Wed, Jul 06, 2016 at 11:30:59AM -0700, Doug Anderson wrote:

> Note: which boards are you thinking would test this?  My grep showed
> zero boards using regulator-ramp-delay with PWM regulator.  While this
> is an important fix for anyone using the ramp delay, I'm not convinced
> that anyone in mainline is using it.

I just meant there are boards out there using the regulator, not the
specific feature.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160707/6d2aea2e/attachment.sig>

From ldewangan at nvidia.com  Thu Jul  7 01:36:23 2016
From: ldewangan at nvidia.com (Laxman Dewangan)
Date: Thu, 7 Jul 2016 14:06:23 +0530
Subject: [PATCH v2] regulator: pwm: Fix regulator ramp delay for continuous
 mode
In-Reply-To: <1467830521-15300-1-git-send-email-dianders@chromium.org>
References: <1467830521-15300-1-git-send-email-dianders@chromium.org>
Message-ID: <577E1487.9010000@nvidia.com>


On Thursday 07 July 2016 12:12 AM, Douglas Anderson wrote:
> The original commit adding support for continuous voltage mode didn't
> handle the regulator ramp delay properly.  It treated the delay as a
> fixed delay in uS despite the property being defined as uV / uS.  Let's
> adjust it.  Luckily there appear to be no users of this ramp delay for
> PWM regulators (as per grepping through device trees in linuxnext).
>
> Note also that the upper bound of usleep_range probably shouldn't be a
> full 1 ms longer than the lower bound since I've seen plenty of hardware
> with a ramp rate of ~5000 uS / uV and for small jumps the total delays
> are in the tens of uS.  1000 is way too much.  We'll try to be dynamic
> and use 10%.
>
> NOTE: This commit doesn't add support for regulator-enable-ramp-delay.
> That could be done in a future patch when someone has a user of that
> featre.
>
> Though this patch is shows as "fixing" a bug, there are no actual known
> users of continuous mode PWM regulator w/ ramp delay in mainline and so
> this likely won't have any effect on anyone unless they are working
> out-of-tree with private patches.  For anyone in this state, it is
> highly encouraged to also pick Boris Brezillon's WIP patches to get
> yourself a reliable and glitch-free regulator.
>
> Fixes: 4773be185a0f ("regulator: pwm-regulator: Add support for continuous-voltage")
> Signed-off-by: Douglas Anderson <dianders at chromium.org>


Looks fine here.
Acked-by: Laxman Dewangan <ldewangan at nvidia.com>

BTW, for some PWM regulator, the settling time for voltage change is 
same for any steps.



From broonie at kernel.org  Thu Jul  7 03:01:21 2016
From: broonie at kernel.org (Mark Brown)
Date: Thu, 07 Jul 2016 12:01:21 +0200
Subject: Applied "regulator: pwm: Fix regulator ramp delay for continuous
 mode" to the regulator tree
In-Reply-To: <1467830521-15300-1-git-send-email-dianders@chromium.org>
Message-ID: <E1bL67V-0000nK-ET@finisterre>

The patch

   regulator: pwm: Fix regulator ramp delay for continuous mode

has been applied to the regulator tree at

   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator.git 

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.  

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

>From c2588393e6315ab68207323d37d2a73713d6bc81 Mon Sep 17 00:00:00 2001
From: Douglas Anderson <dianders at chromium.org>
Date: Wed, 6 Jul 2016 11:42:01 -0700
Subject: [PATCH] regulator: pwm: Fix regulator ramp delay for continuous mode

The original commit adding support for continuous voltage mode didn't
handle the regulator ramp delay properly.  It treated the delay as a
fixed delay in uS despite the property being defined as uV / uS.  Let's
adjust it.  Luckily there appear to be no users of this ramp delay for
PWM regulators (as per grepping through device trees in linuxnext).

Note also that the upper bound of usleep_range probably shouldn't be a
full 1 ms longer than the lower bound since I've seen plenty of hardware
with a ramp rate of ~5000 uS / uV and for small jumps the total delays
are in the tens of uS.  1000 is way too much.  We'll try to be dynamic
and use 10%.

NOTE: This commit doesn't add support for regulator-enable-ramp-delay.
That could be done in a future patch when someone has a user of that
featre.

Though this patch is shows as "fixing" a bug, there are no actual known
users of continuous mode PWM regulator w/ ramp delay in mainline and so
this likely won't have any effect on anyone unless they are working
out-of-tree with private patches.  For anyone in this state, it is
highly encouraged to also pick Boris Brezillon's WIP patches to get
yourself a reliable and glitch-free regulator.

Fixes: 4773be185a0f ("regulator: pwm-regulator: Add support for continuous-voltage")
Signed-off-by: Douglas Anderson <dianders at chromium.org>
Acked-by: Laxman Dewangan <ldewangan at nvidia.com>
Signed-off-by: Mark Brown <broonie at kernel.org>
---
 drivers/regulator/pwm-regulator.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/drivers/regulator/pwm-regulator.c b/drivers/regulator/pwm-regulator.c
index 90f8b7fd0437..666bc3bb52ef 100644
--- a/drivers/regulator/pwm-regulator.c
+++ b/drivers/regulator/pwm-regulator.c
@@ -145,6 +145,7 @@ static int pwm_regulator_set_voltage(struct regulator_dev *rdev,
 	unsigned int duty_pulse;
 	u64 req_period;
 	u32 rem;
+	int old_uV = pwm_regulator_get_voltage(rdev);
 	int ret;
 
 	pwm_get_args(drvdata->pwm, &pargs);
@@ -174,8 +175,12 @@ static int pwm_regulator_set_voltage(struct regulator_dev *rdev,
 
 	drvdata->volt_uV = min_uV;
 
-	/* Delay required by PWM regulator to settle to the new voltage */
-	usleep_range(ramp_delay, ramp_delay + 1000);
+	if ((ramp_delay == 0) || !pwm_regulator_is_enabled(rdev))
+		return 0;
+
+	/* Ramp delay is in uV/uS. Adjust to uS and delay */
+	ramp_delay = DIV_ROUND_UP(abs(min_uV - old_uV), ramp_delay);
+	usleep_range(ramp_delay, ramp_delay + DIV_ROUND_UP(ramp_delay, 10));
 
 	return 0;
 }
-- 
2.8.1



From zhengxing at rock-chips.com  Thu Jul  7 03:01:12 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Thu,  7 Jul 2016 18:01:12 +0800
Subject: [PATCH] ARM: dts: rockchip: add dts for RK3288-Fennec boards
Message-ID: <1467885672-31740-1-git-send-email-zhengxing@rock-chips.com>

This adds support for RK3288-Fennec boards. Currently supported
are serial console, wired networking, hdmi output and USB.

Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
---

 Documentation/devicetree/bindings/arm/rockchip.txt |    4 +
 arch/arm/boot/dts/Makefile                         |    1 +
 arch/arm/boot/dts/rk3288-fennec.dts                |  385 ++++++++++++++++++++
 3 files changed, 390 insertions(+)
 create mode 100644 arch/arm/boot/dts/rk3288-fennec.dts

diff --git a/Documentation/devicetree/bindings/arm/rockchip.txt b/Documentation/devicetree/bindings/arm/rockchip.txt
index 6668645..d7a9b2e 100644
--- a/Documentation/devicetree/bindings/arm/rockchip.txt
+++ b/Documentation/devicetree/bindings/arm/rockchip.txt
@@ -110,6 +110,10 @@ Rockchip platforms device tree bindings
 - Rockchip RK3229 Evaluation board:
      - compatible = "rockchip,rk3229-evb", "rockchip,rk3229";
 
+- Rockchip RK3288 Fennec board:
+    Required root node properties:
+     - compatible = "rockchip,rk3288-fennec", "rockchip,rk3288";
+
 - Rockchip RK3399 evb:
     Required root node properties:
       - compatible = "rockchip,rk3399-evb", "rockchip,rk3399";
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index 5e0833f..20f3c7b 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -605,6 +605,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += \
 	rk3229-evb.dtb \
 	rk3288-evb-act8846.dtb \
 	rk3288-evb-rk808.dtb \
+	rk3288-fennec.dtb \
 	rk3288-firefly-beta.dtb \
 	rk3288-firefly.dtb \
 	rk3288-miqi.dtb \
diff --git a/arch/arm/boot/dts/rk3288-fennec.dts b/arch/arm/boot/dts/rk3288-fennec.dts
new file mode 100644
index 0000000..239d71e
--- /dev/null
+++ b/arch/arm/boot/dts/rk3288-fennec.dts
@@ -0,0 +1,385 @@
+/*
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of the
+ *     License, or (at your option) any later version.
+ *
+ *     This file 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.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/dts-v1/;
+
+#include "rk3288.dtsi"
+
+/ {
+	model = "Rockchip RK3288 Fennec Board";
+	compatible = "rockchip,rk3288-fennec", "rockchip,rk3288";
+
+	memory {
+		device_type = "memory";
+		reg = <0x0 0x80000000>;
+	};
+
+	ext_gmac: external-gmac-clock {
+		compatible = "fixed-clock";
+		#clock-cells = <0>;
+		clock-frequency = <125000000>;
+		clock-output-names = "ext_gmac";
+	};
+
+	vcc_sys: vsys-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_sys";
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+		regulator-always-on;
+		regulator-boot-on;
+	};
+};
+
+&cpu0 {
+	cpu0-supply = <&vdd_cpu>;
+};
+
+&emmc {
+	status = "okay";
+
+	bus-width = <8>;
+	cap-mmc-highspeed;
+	disable-wp;
+	non-removable;
+	num-slots = <1>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&emmc_clk &emmc_cmd &emmc_pwr &emmc_bus8>;
+};
+
+&gmac {
+	assigned-clocks = <&cru SCLK_MAC>;
+	assigned-clock-parents = <&ext_gmac>;
+	clock_in_out = "input";
+	pinctrl-names = "default";
+	pinctrl-0 = <&rgmii_pins>, <&phy_rst>, <&phy_pmeb>, <&phy_int>;
+	phy-supply = <&vcc_lan>;
+	phy-mode = "rgmii";
+	snps,reset-active-low;
+	snps,reset-delays-us = <0 10000 1000000>;
+	snps,reset-gpio = <&gpio4 8 GPIO_ACTIVE_LOW>;
+	tx_delay = <0x30>;
+	rx_delay = <0x10>;
+	status = "okay";
+};
+
+&hdmi {
+	status = "okay";
+};
+
+&i2c0 {
+	status = "okay";
+	clock-frequency = <400000>;
+
+	rk808: pmic at 1b {
+		compatible = "rockchip,rk808";
+		reg = <0x1b>;
+		interrupt-parent = <&gpio0>;
+		interrupts = <4 IRQ_TYPE_LEVEL_LOW>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pmic_int &global_pwroff>;
+		rockchip,system-power-controller;
+		wakeup-source;
+		#clock-cells = <1>;
+		clock-output-names = "xin32k", "rk808-clkout2";
+
+		vcc1-supply = <&vcc_sys>;
+		vcc2-supply = <&vcc_sys>;
+		vcc3-supply = <&vcc_sys>;
+		vcc4-supply = <&vcc_sys>;
+		vcc6-supply = <&vcc_sys>;
+		vcc7-supply = <&vcc_sys>;
+		vcc8-supply = <&vcc_io>;
+		vcc9-supply = <&vcc_io>;
+		vcc10-supply = <&vcc_io>;
+		vcc11-supply = <&vcc_io>;
+		vcc12-supply = <&vcc_io>;
+		vddio-supply = <&vcc_io>;
+
+		regulators {
+			vdd_cpu: DCDC_REG1 {
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-min-microvolt = <750000>;
+				regulator-max-microvolt = <1350000>;
+				regulator-name = "vdd_arm";
+				regulator-state-mem {
+					regulator-off-in-suspend;
+				};
+			};
+
+			vdd_gpu: DCDC_REG2 {
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-min-microvolt = <850000>;
+				regulator-max-microvolt = <1250000>;
+				regulator-name = "vdd_gpu";
+				regulator-state-mem {
+					regulator-on-in-suspend;
+					regulator-suspend-microvolt = <1000000>;
+				};
+			};
+
+			vcc_ddr: DCDC_REG3 {
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-name = "vcc_ddr";
+				regulator-state-mem {
+					regulator-on-in-suspend;
+				};
+			};
+
+			vcc_io: DCDC_REG4 {
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-min-microvolt = <3300000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-name = "vcc_io";
+				regulator-state-mem {
+					regulator-on-in-suspend;
+					regulator-suspend-microvolt = <3300000>;
+				};
+			};
+
+			vccio_pmu: LDO_REG1 {
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-min-microvolt = <3300000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-name = "vccio_pmu";
+				regulator-state-mem {
+					regulator-on-in-suspend;
+					regulator-suspend-microvolt = <3300000>;
+				};
+			};
+
+			vcca_33: LDO_REG2 {
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-min-microvolt = <3300000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-name = "vcca_33";
+				regulator-state-mem {
+					regulator-off-in-suspend;
+				};
+			};
+
+			vdd_10: LDO_REG3 {
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-min-microvolt = <1000000>;
+				regulator-max-microvolt = <1000000>;
+				regulator-name = "vdd_10";
+				regulator-state-mem {
+					regulator-on-in-suspend;
+					regulator-suspend-microvolt = <1000000>;
+				};
+			};
+
+			vcc_wl: LDO_REG4 {
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-name = "vcc_wl";
+				regulator-state-mem {
+					regulator-on-in-suspend;
+					regulator-suspend-microvolt = <1800000>;
+				};
+			};
+
+			vccio_sd: LDO_REG5 {
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-name = "vccio_sd";
+				regulator-state-mem {
+					regulator-on-in-suspend;
+					regulator-suspend-microvolt = <3300000>;
+				};
+			};
+
+			vdd10_lcd: LDO_REG6 {
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-min-microvolt = <1000000>;
+				regulator-max-microvolt = <1000000>;
+				regulator-name = "vdd10_lcd";
+				regulator-state-mem {
+					regulator-on-in-suspend;
+					regulator-suspend-microvolt = <1000000>;
+				};
+			};
+
+			vcc_18: LDO_REG7 {
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-name = "vcc_18";
+				regulator-state-mem {
+					regulator-on-in-suspend;
+					regulator-suspend-microvolt = <1800000>;
+				};
+			};
+
+			vcc18_lcd: LDO_REG8 {
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-name = "vcc18_lcd";
+				regulator-state-mem {
+					regulator-on-in-suspend;
+					regulator-suspend-microvolt = <1800000>;
+				};
+			};
+
+			vcc_sd: SWITCH_REG1 {
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-name = "vcc_sd";
+				regulator-state-mem {
+					regulator-on-in-suspend;
+				};
+			};
+
+			vcc_lan: SWITCH_REG2 {
+				regulator-always-on;
+				regulator-boot-on;
+				regulator-name = "vcc_lan";
+				regulator-state-mem {
+					regulator-on-in-suspend;
+				};
+			};
+		};
+	};
+};
+
+&pinctrl {
+	pcfg_output_high: pcfg-output-high {
+		output-high;
+	};
+
+	pcfg_output_low: pcfg-output-low {
+		output-low;
+	};
+
+	pcfg_pull_none_drv_8ma: pcfg-pull-none-drv-8ma {
+		drive-strength = <8>;
+	};
+
+	pcfg_pull_up_drv_8ma: pcfg-pull-up-drv-8ma {
+		bias-pull-up;
+		drive-strength = <8>;
+	};
+
+	gmac {
+		phy_int: phy-int {
+			rockchip,pins = <0 9 RK_FUNC_GPIO &pcfg_pull_up>;
+		};
+
+		phy_pmeb: phy-pmeb {
+			rockchip,pins = <0 8 RK_FUNC_GPIO &pcfg_pull_up>;
+		};
+
+		phy_rst: phy-rst {
+			rockchip,pins = <4 8 RK_FUNC_GPIO &pcfg_output_high>;
+		};
+	};
+
+	pmic {
+		pmic_int: pmic-int {
+			rockchip,pins = <RK_GPIO0 4 RK_FUNC_GPIO &pcfg_pull_up>;
+		};
+	};
+
+	usbphy {
+		host_drv: host-drv {
+			rockchip,pins = <0 14 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+};
+
+&uart2 {
+	status = "okay";
+};
+
+&usbphy {
+	status = "okay";
+
+	vbus_drv-gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>;
+
+	pinctrl-names = "default";
+	pinctrl-0 = <&host_drv>;
+};
+
+&usb_host0_ehci {
+	status = "okay";
+};
+
+&usb_host1 {
+	status = "okay";
+};
+
+&usb_otg {
+	status = "okay";
+};
+
+&usb_hsic {
+	status = "okay";
+};
+
+&vopb {
+	status = "okay";
+};
+
+&vopb_mmu {
+	status = "okay";
+};
+
+&vopl {
+	status = "okay";
+};
+
+&vopl_mmu {
+	status = "okay";
+};
-- 
1.7.9.5




From heiko at sntech.de  Thu Jul  7 03:11:41 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Thu, 07 Jul 2016 12:11:41 +0200
Subject: [PATCH] arm64: dts: rockchip: support the usb2phy for rk3399 evb
In-Reply-To: <1467866800-24794-1-git-send-email-wxt@rock-chips.com>
References: <1467866800-24794-1-git-send-email-wxt@rock-chips.com>
Message-ID: <2150255.RfISFviJKP@phil>

Hi,

Am Donnerstag, 7. Juli 2016, 12:46:40 schrieb Caesar Wang:
> From: Frank Wang <frank.wang at rock-chips.com>
> 
> This patch adds the usb2phy needed dts node information for rk3399.
> 
> USB2.0 PHY is comprised of one Host port and one OTG port.
> Host Port is for USB2.0 host controller; OTG port is for USB2.0 part of
> USB3.0 OTG controller, and as a part to construct a fully feature TypeC
> subsystem.
> 
> The USB2.0 vbus gpio is board specific, it's no same with all rk3399
> boards, so move it into evb voard.
> 
> Signed-off-by: Frank Wang <frank.wang at rock-chips.com>
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
> ---
> 
>  arch/arm64/boot/dts/rockchip/rk3399-evb.dts |  4 ++++
>  arch/arm64/boot/dts/rockchip/rk3399.dtsi    | 19 +++++++++++++++++++
>  2 files changed, 23 insertions(+)
> 
> diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> b/arch/arm64/boot/dts/rockchip/rk3399.dtsi index 4c84229..21d147f 100644
> --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> @@ -242,6 +242,25 @@
>  		status = "disabled";
>  	};
> 
> +	usb2phy: usb2phy {
> +		compatible = "rockchip,rk3399-usb-phy";
> +		rockchip,grf = <&grf>;
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +
> +		usb2phy0: usb2-phy0 {
> +			#phy-cells = <0>;
> +			#clock-cells = <0>;
> +			reg = <0xe458>;
> +		};
> +
> +		usb2phy1: usb2-phy1 {
> +			#phy-cells = <0>;
> +			#clock-cells = <0>;
> +			reg = <0xe468>;
> +		};
> +	};
> +

The rk3399 uses the innosilicon usb2 phy and the binding we've come up for 
it looks a lot different than the above. So I'm not sure where this is 
coming from, especially as I haven't seen the driver part for the rk3399 at 
all yet. So far Frank was always working on te rk3366 variant, which seemed 
to be the most complete.


Heiko


From broonie at kernel.org  Thu Jul  7 05:22:19 2016
From: broonie at kernel.org (Mark Brown)
Date: Thu, 7 Jul 2016 14:22:19 +0200
Subject: next-20160707 build: 1 failures 6 warnings (next-20160707)
In-Reply-To: <E1bL5XI-0004Z9-EP@optimist>
References: <E1bL5XI-0004Z9-EP@optimist>
Message-ID: <20160707122219.GO6247@sirena.org.uk>

On Thu, Jul 07, 2016 at 10:23:56AM +0100, Build bot for Mark Brown wrote:

Today's -next fails to build an arm64 allmodconfig due to:

> 	arm64-allmodconfig
> ../drivers/gpu/drm/rockchip/rockchip_drm_drv.c:17:27: fatal error: asm/dma-iommu.h: No such file or directory

triggered by c51f43a9c171ea (iommu/rockchip: Enable Rockchip IOMMU on
ARM64) which enables the Rockchip IOMMU driver on ARM64 which lacks the
above header.  Either the use of the header needs to be removed or an
explicit ARM64 dependency added at the DRM level.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160707/bbf16e3b/attachment.sig>

From tfiga at chromium.org  Thu Jul  7 06:18:02 2016
From: tfiga at chromium.org (Tomasz Figa)
Date: Thu, 7 Jul 2016 22:18:02 +0900
Subject: next-20160707 build: 1 failures 6 warnings (next-20160707)
In-Reply-To: <20160707122219.GO6247@sirena.org.uk>
References: <E1bL5XI-0004Z9-EP@optimist> <20160707122219.GO6247@sirena.org.uk>
Message-ID: <CAAFQd5DacKar+T1Fj62NvAZOT_QWBhahftZJXGq40Z19n-Z4Hg@mail.gmail.com>

Hi Mark,

On Thu, Jul 7, 2016 at 9:22 PM, Mark Brown <broonie at kernel.org> wrote:
> On Thu, Jul 07, 2016 at 10:23:56AM +0100, Build bot for Mark Brown wrote:
>
> Today's -next fails to build an arm64 allmodconfig due to:
>
>>       arm64-allmodconfig
>> ../drivers/gpu/drm/rockchip/rockchip_drm_drv.c:17:27: fatal error: asm/dma-iommu.h: No such file or directory
>
> triggered by c51f43a9c171ea (iommu/rockchip: Enable Rockchip IOMMU on
> ARM64) which enables the Rockchip IOMMU driver on ARM64 which lacks the
> above header.  Either the use of the header needs to be removed or an
> explicit ARM64 dependency added at the DRM level.

Looks like next is missing the DRM side of the series, which makes the
driver in question not need this header anymore.

This is probably because we missed this hidden dependency and decided
to push the IOMMU part separately through the IOMMU tree. I guess we
can remove the offending patch from the IOMMU tree and let the DRM
tree include it based on a topic branch from Joerg.

Joerg, David, Mark Yao, what do you think?

Best regards,
Tomasz


From jroedel at suse.de  Thu Jul  7 08:32:37 2016
From: jroedel at suse.de (Joerg Roedel)
Date: Thu, 7 Jul 2016 17:32:37 +0200
Subject: next-20160707 build: 1 failures 6 warnings (next-20160707)
In-Reply-To: <CAAFQd5DacKar+T1Fj62NvAZOT_QWBhahftZJXGq40Z19n-Z4Hg@mail.gmail.com>
References: <E1bL5XI-0004Z9-EP@optimist> <20160707122219.GO6247@sirena.org.uk>
 <CAAFQd5DacKar+T1Fj62NvAZOT_QWBhahftZJXGq40Z19n-Z4Hg@mail.gmail.com>
Message-ID: <20160707153237.GF11467@suse.de>

On Thu, Jul 07, 2016 at 10:18:02PM +0900, Tomasz Figa wrote:
> This is probably because we missed this hidden dependency and decided
> to push the IOMMU part separately through the IOMMU tree. I guess we
> can remove the offending patch from the IOMMU tree and let the DRM
> tree include it based on a topic branch from Joerg.
> 
> Joerg, David, Mark Yao, what do you think?

Will the DRM patches go upstream soon, or is there still discussion
around them? For now I am going to remove/revert that patch in my tree.


	Joerg


From dianders at chromium.org  Thu Jul  7 09:30:57 2016
From: dianders at chromium.org (Doug Anderson)
Date: Thu, 7 Jul 2016 09:30:57 -0700
Subject: [PATCH v2] regulator: pwm: Fix regulator ramp delay for
 continuous mode
In-Reply-To: <577E1487.9010000@nvidia.com>
References: <1467830521-15300-1-git-send-email-dianders@chromium.org>
 <577E1487.9010000@nvidia.com>
Message-ID: <CAD=FV=UQw7ttMECtO0aomx56G6xGWZh96NRug6Y4QuPJfXq8Tw@mail.gmail.com>

Hi,

On Thu, Jul 7, 2016 at 1:36 AM, Laxman Dewangan <ldewangan at nvidia.com> wrote:
>
> On Thursday 07 July 2016 12:12 AM, Douglas Anderson wrote:
>>
>> The original commit adding support for continuous voltage mode didn't
>> handle the regulator ramp delay properly.  It treated the delay as a
>> fixed delay in uS despite the property being defined as uV / uS.  Let's
>> adjust it.  Luckily there appear to be no users of this ramp delay for
>> PWM regulators (as per grepping through device trees in linuxnext).
>>
>> Note also that the upper bound of usleep_range probably shouldn't be a
>> full 1 ms longer than the lower bound since I've seen plenty of hardware
>> with a ramp rate of ~5000 uS / uV and for small jumps the total delays
>> are in the tens of uS.  1000 is way too much.  We'll try to be dynamic
>> and use 10%.
>>
>> NOTE: This commit doesn't add support for regulator-enable-ramp-delay.
>> That could be done in a future patch when someone has a user of that
>> featre.
>>
>> Though this patch is shows as "fixing" a bug, there are no actual known
>> users of continuous mode PWM regulator w/ ramp delay in mainline and so
>> this likely won't have any effect on anyone unless they are working
>> out-of-tree with private patches.  For anyone in this state, it is
>> highly encouraged to also pick Boris Brezillon's WIP patches to get
>> yourself a reliable and glitch-free regulator.
>>
>> Fixes: 4773be185a0f ("regulator: pwm-regulator: Add support for
>> continuous-voltage")
>> Signed-off-by: Douglas Anderson <dianders at chromium.org>
>
>
>
> Looks fine here.
> Acked-by: Laxman Dewangan <ldewangan at nvidia.com>
>
> BTW, for some PWM regulator, the settling time for voltage change is same
> for any steps.

In that case we should probably add a new PWM regulator property and
not abuse the existing one.  Maybe you use "pwm-regulator-settle-us"
or something?  ...and actually the right thing is probably to
implement 'regulator-ramp-delay' as doing several small steps in that
case.  So if you've got:

* settle time: 10 us
* ramp delay = 5000 uV / us
* voltage change of 200000 uV

In that case you'd probably want to break your 200 mV voltage change
into a few steps?  So you could do bump by 50mV, delay 10us, bump by
50mV, delay 10us, bump by 50mV, delay by 10us.

The final delay would be the same as with my patch applied but you'd
get there more smoothly.

-Doug


From afrid at nvidia.com  Thu Jul  7 11:23:22 2016
From: afrid at nvidia.com (Aleksandr Frid)
Date: Thu, 7 Jul 2016 18:23:22 +0000
Subject: [PATCH v2] regulator: pwm: Fix regulator ramp delay for
 continuous mode
In-Reply-To: <CAD=FV=UQw7ttMECtO0aomx56G6xGWZh96NRug6Y4QuPJfXq8Tw@mail.gmail.com>
References: <1467830521-15300-1-git-send-email-dianders@chromium.org>
 <577E1487.9010000@nvidia.com>
 <CAD=FV=UQw7ttMECtO0aomx56G6xGWZh96NRug6Y4QuPJfXq8Tw@mail.gmail.com>
Message-ID: <47095a54f7b44f8a98bdfe05cdea7865@HQMAIL106.nvidia.com>

Hi,

>>
In that case we should probably add a new PWM regulator property and not abuse the existing one.  Maybe you use "pwm-regulator-settle-us"
or something?  
>>
Looks reasonable to me.

>>
actually the right thing is probably to implement 'regulator-ramp-delay' as doing several small steps in that case
>>
Ramp delay uV/us  is not a "real" metric for some PWM regulators with exponential transition -- as opposite to fixed slew-rate linear transition on other regulators. So splitting transition into multiple steps to implement artificial  (in this case) metric seems questionable. 

-----Original Message-----
From: dianders at google.com [mailto:dianders at google.com] On Behalf Of Doug Anderson
Sent: Thursday, July 07, 2016 9:31 AM
To: Laxman Dewangan
Cc: Mark Brown; Boris Brezillon; Lee Jones; Brian Norris; open list:ARM/Rockchip SoC...; Heiko Stuebner; Thierry Reding; Liam Girdwood; linux-kernel at vger.kernel.org; Aleksandr Frid
Subject: Re: [PATCH v2] regulator: pwm: Fix regulator ramp delay for continuous mode

Hi,

On Thu, Jul 7, 2016 at 1:36 AM, Laxman Dewangan <ldewangan at nvidia.com> wrote:
>
> On Thursday 07 July 2016 12:12 AM, Douglas Anderson wrote:
>>
>> The original commit adding support for continuous voltage mode didn't 
>> handle the regulator ramp delay properly.  It treated the delay as a 
>> fixed delay in uS despite the property being defined as uV / uS.  
>> Let's adjust it.  Luckily there appear to be no users of this ramp 
>> delay for PWM regulators (as per grepping through device trees in linuxnext).
>>
>> Note also that the upper bound of usleep_range probably shouldn't be 
>> a full 1 ms longer than the lower bound since I've seen plenty of 
>> hardware with a ramp rate of ~5000 uS / uV and for small jumps the 
>> total delays are in the tens of uS.  1000 is way too much.  We'll try 
>> to be dynamic and use 10%.
>>
>> NOTE: This commit doesn't add support for regulator-enable-ramp-delay.
>> That could be done in a future patch when someone has a user of that 
>> featre.
>>
>> Though this patch is shows as "fixing" a bug, there are no actual 
>> known users of continuous mode PWM regulator w/ ramp delay in 
>> mainline and so this likely won't have any effect on anyone unless 
>> they are working out-of-tree with private patches.  For anyone in 
>> this state, it is highly encouraged to also pick Boris Brezillon's 
>> WIP patches to get yourself a reliable and glitch-free regulator.
>>
>> Fixes: 4773be185a0f ("regulator: pwm-regulator: Add support for
>> continuous-voltage")
>> Signed-off-by: Douglas Anderson <dianders at chromium.org>
>
>
>
> Looks fine here.
> Acked-by: Laxman Dewangan <ldewangan at nvidia.com>
>
> BTW, for some PWM regulator, the settling time for voltage change is 
> same for any steps.

In that case we should probably add a new PWM regulator property and not abuse the existing one.  Maybe you use "pwm-regulator-settle-us"
or something?  ...and actually the right thing is probably to implement 'regulator-ramp-delay' as doing several small steps in that case.  So if you've got:

* settle time: 10 us
* ramp delay = 5000 uV / us
* voltage change of 200000 uV

In that case you'd probably want to break your 200 mV voltage change into a few steps?  So you could do bump by 50mV, delay 10us, bump by 50mV, delay 10us, bump by 50mV, delay by 10us.

The final delay would be the same as with my patch applied but you'd get there more smoothly.

-Doug

From dianders at chromium.org  Thu Jul  7 11:31:36 2016
From: dianders at chromium.org (Doug Anderson)
Date: Thu, 7 Jul 2016 11:31:36 -0700
Subject: [PATCH v2] regulator: pwm: Fix regulator ramp delay for
 continuous mode
In-Reply-To: <47095a54f7b44f8a98bdfe05cdea7865@HQMAIL106.nvidia.com>
References: <1467830521-15300-1-git-send-email-dianders@chromium.org>
 <577E1487.9010000@nvidia.com>
 <CAD=FV=UQw7ttMECtO0aomx56G6xGWZh96NRug6Y4QuPJfXq8Tw@mail.gmail.com>
 <47095a54f7b44f8a98bdfe05cdea7865@HQMAIL106.nvidia.com>
Message-ID: <CAD=FV=UZrpSsOAxsuEWuMeie3NHQUY3RQyaUF0MK9mHDqPhRzQ@mail.gmail.com>

Hi,

On Thu, Jul 7, 2016 at 11:23 AM, Aleksandr Frid <afrid at nvidia.com> wrote:
> Hi,
>
>>>
> In that case we should probably add a new PWM regulator property and not abuse the existing one.  Maybe you use "pwm-regulator-settle-us"
> or something?
>>>
> Looks reasonable to me.
>
>>>
> actually the right thing is probably to implement 'regulator-ramp-delay' as doing several small steps in that case
>>>
> Ramp delay uV/us  is not a "real" metric for some PWM regulators with exponential transition -- as opposite to fixed slew-rate linear transition on other regulators. So splitting transition into multiple steps to implement artificial  (in this case) metric seems questionable.

In the case that you don't need multiple steps then don't specify a
regulator-ramp-delay, just a pwm-regulator-settle-us.

...the suggestion for multiple steps is because (so I'm told) it helps
avoid overshoot or undershoot problems.  In general the whole point of
ramping a regulator slowly is to avoid overshoot or undershoot
problems.  The "regulator-ramp-delay" property in Linux is a little
odd because it sort of "describes" the ramp delay and sort of "sets"
the ramp delay.  Many PMICs allow you to set how fast the regulator
will ramp and this property is used to specify how the register in the
PMIC should be set.  However, it is also used as the actual delay in
Linux.


-Doug


From afrid at nvidia.com  Thu Jul  7 11:43:33 2016
From: afrid at nvidia.com (Aleksandr Frid)
Date: Thu, 7 Jul 2016 18:43:33 +0000
Subject: [PATCH v2] regulator: pwm: Fix regulator ramp delay for
 continuous mode
In-Reply-To: <CAD=FV=UZrpSsOAxsuEWuMeie3NHQUY3RQyaUF0MK9mHDqPhRzQ@mail.gmail.com>
References: <1467830521-15300-1-git-send-email-dianders@chromium.org>
 <577E1487.9010000@nvidia.com>
 <CAD=FV=UQw7ttMECtO0aomx56G6xGWZh96NRug6Y4QuPJfXq8Tw@mail.gmail.com>
 <47095a54f7b44f8a98bdfe05cdea7865@HQMAIL106.nvidia.com>
 <CAD=FV=UZrpSsOAxsuEWuMeie3NHQUY3RQyaUF0MK9mHDqPhRzQ@mail.gmail.com>
Message-ID: <364098370f214216ad14eda6487687e9@HQMAIL106.nvidia.com>

>>
In the case that you don't need multiple steps then don't specify a regulator-ramp-delay, just a pwm-regulator-settle-us.
>>
Agreed

-----Original Message-----
From: dianders at google.com [mailto:dianders at google.com] On Behalf Of Doug Anderson
Sent: Thursday, July 07, 2016 11:32 AM
To: Aleksandr Frid
Cc: Laxman Dewangan; Mark Brown; Boris Brezillon; Lee Jones; Brian Norris; open list:ARM/Rockchip SoC...; Heiko Stuebner; Thierry Reding; Liam Girdwood; linux-kernel at vger.kernel.org
Subject: Re: [PATCH v2] regulator: pwm: Fix regulator ramp delay for continuous mode

Hi,

On Thu, Jul 7, 2016 at 11:23 AM, Aleksandr Frid <afrid at nvidia.com> wrote:
> Hi,
>
>>>
> In that case we should probably add a new PWM regulator property and not abuse the existing one.  Maybe you use "pwm-regulator-settle-us"
> or something?
>>>
> Looks reasonable to me.
>
>>>
> actually the right thing is probably to implement 
> 'regulator-ramp-delay' as doing several small steps in that case
>>>
> Ramp delay uV/us  is not a "real" metric for some PWM regulators with exponential transition -- as opposite to fixed slew-rate linear transition on other regulators. So splitting transition into multiple steps to implement artificial  (in this case) metric seems questionable.

In the case that you don't need multiple steps then don't specify a regulator-ramp-delay, just a pwm-regulator-settle-us.

...the suggestion for multiple steps is because (so I'm told) it helps avoid overshoot or undershoot problems.  In general the whole point of ramping a regulator slowly is to avoid overshoot or undershoot problems.  The "regulator-ramp-delay" property in Linux is a little odd because it sort of "describes" the ramp delay and sort of "sets"
the ramp delay.  Many PMICs allow you to set how fast the regulator will ramp and this property is used to specify how the register in the PMIC should be set.  However, it is also used as the actual delay in Linux.


-Doug

From heiko at sntech.de  Thu Jul  7 14:35:38 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Thu, 07 Jul 2016 23:35:38 +0200
Subject: [PATCH 0/3] ARM: dts: the dts support for rk3288 firefly reload
In-Reply-To: <1467829380-16242-1-git-send-email-ayaka@soulik.info>
References: <3015470.z4L7UQJKa9@phil>
 <1467829380-16242-1-git-send-email-ayaka@soulik.info>
Message-ID: <2686572.mni1Mn7R6t@phil>

Hi Randy,

Am Donnerstag, 7. Juli 2016, 02:22:57 schrieb Randy Li:
> The rk3288 firefly reload  is a Rockchip RK3288 based board be found by
> core board and main board. The regulators are connected in a different
> way to the previous version of firefly boards, it is necessary to
> move some common code to uncommon place.
> 
> I only tested the ethernet and confirmed that works.
> The usb in this board won't caused by the bugs in the driver.
> 
> This version follow the suggests from Heiko Stuebner,
> except the duplicated supply name problem, I don't think
> it could be fixed in that way.

I've now had a chance to look at that reload board on the firefly site.
Firefly also is the company name, so a board named that way is not 
necessarily a "variant" :-) .

And looking at the "reload" board this definitly seems to be a very 
different product with it being a system-on-module+baseboard design with 
additional peripherals like that sata bridge, camera interfaces and probably 
more.

As you might've seen, most Rockchip boards are based on some reference-
design, so are similar in a big part of their core layout.
So, looking at the vastly different product the reload is, I'd really like 
to have a separate dts for the reload, to not run into more confusing 
differences later on.

Also, when adding a new board, please also add an entry to 
Documentation/devicetree/bindingd/arm/rockchip.txt


Thanks
Heiko


From thierry.reding at gmail.com  Thu Jul  7 14:52:01 2016
From: thierry.reding at gmail.com (Thierry Reding)
Date: Thu, 7 Jul 2016 23:52:01 +0200
Subject: [PATCH v1 1/5] dt-bindings: Add support for LG LP079QX1-SP0V
 1536x2048 panel
In-Reply-To: <1467089472-18584-1-git-send-email-ykk@rock-chips.com>
References: <1467089288-18355-1-git-send-email-ykk@rock-chips.com>
 <1467089472-18584-1-git-send-email-ykk@rock-chips.com>
Message-ID: <20160707215159.GA744@mithrandir.ba.sec>

On Tue, Jun 28, 2016 at 12:51:12PM +0800, Yakir Yang wrote:
> The LG LP079QX1-SP0V is an 7.9" QXGA TFT with LED Backlight unit and
> 32 pins eDP interface. This module supports 1536x2048 mode.
> 
> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
> ---
>  .../devicetree/bindings/display/panel/lg,lp079qx1-sp0v.txt         | 7 +++++++
>  1 file changed, 7 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/display/panel/lg,lp079qx1-sp0v.txt

Applied, thanks.

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160707/eda340cd/attachment.sig>

From thierry.reding at gmail.com  Thu Jul  7 14:52:16 2016
From: thierry.reding at gmail.com (Thierry Reding)
Date: Thu, 7 Jul 2016 23:52:16 +0200
Subject: [PATCH v1 2/5] drm/panel: simple: Add support for LG
 LP079QX1-SP0V 1536x2048 panel
In-Reply-To: <1467089475-18627-1-git-send-email-ykk@rock-chips.com>
References: <1467089288-18355-1-git-send-email-ykk@rock-chips.com>
 <1467089475-18627-1-git-send-email-ykk@rock-chips.com>
Message-ID: <20160707215215.GB744@mithrandir.ba.sec>

On Tue, Jun 28, 2016 at 12:51:15PM +0800, Yakir Yang wrote:
> The LG LP079QX1-SP0V is an 7.9" QXGA TFT with LED Backlight unit and
> 32 pins eDP interface. This module supports 1536x2048 mode.
> 
> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
> ---
>  drivers/gpu/drm/panel/panel-simple.c | 26 ++++++++++++++++++++++++++
>  1 file changed, 26 insertions(+)

Applied, thanks.

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160707/21518cd6/attachment.sig>

From heiko at sntech.de  Thu Jul  7 15:31:24 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Fri, 08 Jul 2016 00:31:24 +0200
Subject: [PATCH v1 3/5] ARM: dts: rockchip: add eDP/panel display device
 nodes for rk3288-evb
In-Reply-To: <1467089478-18670-1-git-send-email-ykk@rock-chips.com>
References: <1467089288-18355-1-git-send-email-ykk@rock-chips.com>
 <1467089478-18670-1-git-send-email-ykk@rock-chips.com>
Message-ID: <35835280.FrP3zGa3K1@phil>

Am Dienstag, 28. Juni 2016, 12:51:18 schrieb Yakir Yang:
> The default eDP panel on RK3288 EVB board is LG LP079QX1-SP0V TFT LCD,
> we haven't declared the panel regulator in the 'panel-simple' device
> node here, so the specific board like ACT8846 / RK8080 need to support
> the panel power supply.
> 
> Signed-off-by: Yakir Yang <ykk at rock-chips.com>

applied, after reordering the entries to keep our alphabetical sorting.


Heiko


From heiko at sntech.de  Thu Jul  7 15:31:48 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Fri, 08 Jul 2016 00:31:48 +0200
Subject: [PATCH v1 4/5] ARM: dts: rockchip: add the panel power supply for
 rk3288-evb board with act8846 pmu
In-Reply-To: <1467089481-18713-1-git-send-email-ykk@rock-chips.com>
References: <1467089288-18355-1-git-send-email-ykk@rock-chips.com>
 <1467089481-18713-1-git-send-email-ykk@rock-chips.com>
Message-ID: <2916043.R0kG3Z3t2I@phil>

Am Dienstag, 28. Juni 2016, 12:51:21 schrieb Yakir Yang:
> Panel regulator is controller by a normal GPIO, so we need to
> write a regulator-fixed node for it.
> 
> Signed-off-by: Yakir Yang <ykk at rock-chips.com>

applied, thanks.


Heiko


From heiko at sntech.de  Thu Jul  7 15:35:57 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Fri, 08 Jul 2016 00:35:57 +0200
Subject: [PATCH v1 5/5] ARM: dts: rockchip: add the panel power supply for
 rk3288-evb board with rk808 pmu
In-Reply-To: <1467089484-18756-1-git-send-email-ykk@rock-chips.com>
References: <1467089288-18355-1-git-send-email-ykk@rock-chips.com>
 <1467089484-18756-1-git-send-email-ykk@rock-chips.com>
Message-ID: <5331482.3zk2CO4kJV@phil>

Am Dienstag, 28. Juni 2016, 12:51:24 schrieb Yakir Yang:
> Panel regulator is controller by a normal GPIO, so we need to
> write a regulator-fixed node for it.
> 
> Signed-off-by: Yakir Yang <ykk at rock-chips.com>

applied after rewording the commit message a bit.
On the rk808-evb vcc_lcd comes from the rk808 directly and not through some 
gpio regulator :-) .


Heiko


From heiko at sntech.de  Thu Jul  7 15:43:02 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Fri, 08 Jul 2016 00:43:02 +0200
Subject: [PATCH] ARM: dts: rockchip: add dts for RK3288-Fennec boards
In-Reply-To: <1467885672-31740-1-git-send-email-zhengxing@rock-chips.com>
References: <1467885672-31740-1-git-send-email-zhengxing@rock-chips.com>
Message-ID: <1804956.pJECJVkCcn@phil>

Am Donnerstag, 7. Juli 2016, 18:01:12 schrieb Xing Zheng:
> This adds support for RK3288-Fennec boards. Currently supported
> are serial console, wired networking, hdmi output and USB.
> 
> Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>

applied, thanks

Heiko


From mark.yao at rock-chips.com  Thu Jul  7 17:54:26 2016
From: mark.yao at rock-chips.com (Mark yao)
Date: Fri, 8 Jul 2016 08:54:26 +0800
Subject: [PATCH v5 7/8] drm/rockchip: Use common IOMMU API to attach
 devices
In-Reply-To: <1466734413-7453-8-git-send-email-zhengsq@rock-chips.com>
References: <1466734413-7453-1-git-send-email-zhengsq@rock-chips.com>
 <1466734413-7453-8-git-send-email-zhengsq@rock-chips.com>
Message-ID: <577EF9C2.2070902@rock-chips.com>

On 2016?06?24? 10:13, Shunqian Zheng wrote:
> Rockchip DRM used the arm special API, arm_iommu_*(), to attach
> iommu for ARM32 SoCs. This patch convert to common iommu API
> so it would support ARM64 like RK3399.
>
> Since previous patch added support for direct IOMMU address space
> management, there is no need to use DMA API anymore and this patch wires
> things to use the new method.
>
> Signed-off-by: Shunqian Zheng <zhengsq at rock-chips.com>
> Signed-off-by: Tomasz Figa <tfiga at chromium.org>

Looks good for me, and it works on rk3399 board, so

Acked-by: Mark Yao <mark.yao at rock-chips.com>

> ---
>   drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 100 +++++++++++++++-------------
>   1 file changed, 53 insertions(+), 47 deletions(-)
>
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> index 8b96c69..ca9624f 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> @@ -14,18 +14,18 @@
>    * GNU General Public License for more details.
>    */
>   
> -#include <asm/dma-iommu.h>
> -
>   #include <drm/drmP.h>
>   #include <drm/drm_crtc_helper.h>
>   #include <drm/drm_fb_helper.h>
>   #include <drm/drm_gem_cma_helper.h>
>   #include <linux/dma-mapping.h>
> +#include <linux/dma-iommu.h>
>   #include <linux/pm_runtime.h>
>   #include <linux/module.h>
>   #include <linux/of_graph.h>
>   #include <linux/component.h>
>   #include <linux/console.h>
> +#include <linux/iommu.h>
>   
>   #include <drm/rockchip_drm.h>
>   
> @@ -51,28 +51,31 @@ static struct drm_driver rockchip_drm_driver;
>   int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
>   				   struct device *dev)
>   {
> -	struct dma_iommu_mapping *mapping = drm_dev->dev->archdata.mapping;
> +	struct rockchip_drm_private *private = drm_dev->dev_private;
>   	int ret;
>   
>   	if (!is_support_iommu)
>   		return 0;
>   
> -	ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
> -	if (ret)
> +	ret = iommu_attach_device(private->domain, dev);
> +	if (ret) {
> +		dev_err(dev, "Failed to attach iommu device\n");
>   		return ret;
> +	}
>   
> -	dma_set_max_seg_size(dev, DMA_BIT_MASK(32));
> -
> -	return arm_iommu_attach_device(dev, mapping);
> +	return 0;
>   }
>   
>   void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
>   				    struct device *dev)
>   {
> +	struct rockchip_drm_private *private = drm_dev->dev_private;
> +	struct iommu_domain *domain = private->domain;
> +
>   	if (!is_support_iommu)
>   		return;
>   
> -	arm_iommu_detach_device(dev);
> +	iommu_detach_device(domain, dev);
>   }
>   
>   int rockchip_register_crtc_funcs(struct drm_crtc *crtc,
> @@ -137,11 +140,45 @@ static void rockchip_drm_crtc_disable_vblank(struct drm_device *dev,
>   		priv->crtc_funcs[pipe]->disable_vblank(crtc);
>   }
>   
> +static int rockchip_drm_init_iommu(struct drm_device *drm_dev)
> +{
> +	struct rockchip_drm_private *private = drm_dev->dev_private;
> +	struct iommu_domain_geometry *geometry;
> +	u64 start, end;
> +
> +	if (!is_support_iommu)
> +		return 0;
> +
> +	private->domain = iommu_domain_alloc(&platform_bus_type);
> +	if (!private->domain)
> +		return -ENOMEM;
> +
> +	geometry = &private->domain->geometry;
> +	start = geometry->aperture_start;
> +	end = geometry->aperture_end;
> +
> +	DRM_DEBUG("IOMMU context initialized (aperture: %#llx-%#llx)\n",
> +		  start, end);
> +	drm_mm_init(&private->mm, start, end - start + 1);
> +
> +	return 0;
> +}
> +
> +static void rockchip_iommu_cleanup(struct drm_device *drm_dev)
> +{
> +	struct rockchip_drm_private *private = drm_dev->dev_private;
> +
> +	if (!is_support_iommu)
> +		return;
> +
> +	drm_mm_takedown(&private->mm);
> +	iommu_domain_free(private->domain);
> +}
> +
>   static int rockchip_drm_bind(struct device *dev)
>   {
>   	struct drm_device *drm_dev;
>   	struct rockchip_drm_private *private;
> -	struct dma_iommu_mapping *mapping = NULL;
>   	int ret;
>   
>   	drm_dev = drm_dev_alloc(&rockchip_drm_driver, dev);
> @@ -162,38 +199,14 @@ static int rockchip_drm_bind(struct device *dev)
>   
>   	rockchip_drm_mode_config_init(drm_dev);
>   
> -	dev->dma_parms = devm_kzalloc(dev, sizeof(*dev->dma_parms),
> -				      GFP_KERNEL);
> -	if (!dev->dma_parms) {
> -		ret = -ENOMEM;
> +	ret = rockchip_drm_init_iommu(drm_dev);
> +	if (ret)
>   		goto err_config_cleanup;
> -	}
> -
> -	if (is_support_iommu) {
> -		/* TODO(djkurtz): fetch the mapping start/size from somewhere */
> -		mapping = arm_iommu_create_mapping(&platform_bus_type,
> -						   0x00000000,
> -						   SZ_2G);
> -		if (IS_ERR(mapping)) {
> -			ret = PTR_ERR(mapping);
> -			goto err_config_cleanup;
> -		}
> -
> -		ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
> -		if (ret)
> -			goto err_release_mapping;
> -
> -		dma_set_max_seg_size(dev, DMA_BIT_MASK(32));
> -
> -		ret = arm_iommu_attach_device(dev, mapping);
> -		if (ret)
> -			goto err_release_mapping;
> -	}
>   
>   	/* Try to bind all sub drivers. */
>   	ret = component_bind_all(dev, drm_dev);
>   	if (ret)
> -		goto err_detach_device;
> +		goto err_iommu_cleanup;
>   
>   	/* init kms poll for handling hpd */
>   	drm_kms_helper_poll_init(drm_dev);
> @@ -218,8 +231,6 @@ static int rockchip_drm_bind(struct device *dev)
>   	if (ret)
>   		goto err_fbdev_fini;
>   
> -	if (is_support_iommu)
> -		arm_iommu_release_mapping(mapping);
>   	return 0;
>   err_fbdev_fini:
>   	rockchip_drm_fbdev_fini(drm_dev);
> @@ -228,12 +239,8 @@ err_vblank_cleanup:
>   err_kms_helper_poll_fini:
>   	drm_kms_helper_poll_fini(drm_dev);
>   	component_unbind_all(dev, drm_dev);
> -err_detach_device:
> -	if (is_support_iommu)
> -		arm_iommu_detach_device(dev);
> -err_release_mapping:
> -	if (is_support_iommu)
> -		arm_iommu_release_mapping(mapping);
> +err_iommu_cleanup:
> +	rockchip_iommu_cleanup(drm_dev);
>   err_config_cleanup:
>   	drm_mode_config_cleanup(drm_dev);
>   	drm_dev->dev_private = NULL;
> @@ -250,8 +257,7 @@ static void rockchip_drm_unbind(struct device *dev)
>   	drm_vblank_cleanup(drm_dev);
>   	drm_kms_helper_poll_fini(drm_dev);
>   	component_unbind_all(dev, drm_dev);
> -	if (is_support_iommu)
> -		arm_iommu_detach_device(dev);
> +	rockchip_iommu_cleanup(drm_dev);
>   	drm_mode_config_cleanup(drm_dev);
>   	drm_dev->dev_private = NULL;
>   	drm_dev_unregister(drm_dev);


-- 
?ark Yao




From mark.yao at rock-chips.com  Thu Jul  7 17:54:53 2016
From: mark.yao at rock-chips.com (Mark yao)
Date: Fri, 8 Jul 2016 08:54:53 +0800
Subject: [PATCH v5 6/8] drm/rockchip: Do not use DMA mapping API if
 attached to IOMMU domain
In-Reply-To: <1466734413-7453-7-git-send-email-zhengsq@rock-chips.com>
References: <1466734413-7453-1-git-send-email-zhengsq@rock-chips.com>
 <1466734413-7453-7-git-send-email-zhengsq@rock-chips.com>
Message-ID: <577EF9DD.1010506@rock-chips.com>

On 2016?06?24? 10:13, Shunqian Zheng wrote:
> From: Tomasz Figa <tfiga at chromium.org>
>
> The API is not suitable for subsystems consisting of multiple devices
> and requires severe hacks to use it. To mitigate this, this patch
> implements allocation and address space management locally by using
> helpers provided by DRM framework, like other DRM drivers do, e.g.
> Tegra.
>
> This patch should not introduce any functional changes until the driver
> is made to attach subdevices into an IOMMU domain with the generic IOMMU
> API, which will happen in following patch. Based heavily on GEM
> implementation of Tegra DRM driver.

Acked-by: Mark Yao <mark.yao at rock-chips.com>
>
> Signed-off-by: Tomasz Figa <tfiga at chromium.org>
> Signed-off-by: Shunqian Zheng <zhengsq at rock-chips.com>
> ---
>   drivers/gpu/drm/rockchip/rockchip_drm_drv.h |   3 +
>   drivers/gpu/drm/rockchip/rockchip_drm_gem.c | 221 ++++++++++++++++++++++++++--
>   drivers/gpu/drm/rockchip/rockchip_drm_gem.h |   9 ++
>   3 files changed, 222 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> index ea39329..5ab1223 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> @@ -30,6 +30,7 @@
>   
>   struct drm_device;
>   struct drm_connector;
> +struct iommu_domain;
>   
>   /*
>    * Rockchip drm private crtc funcs.
> @@ -61,6 +62,8 @@ struct rockchip_drm_private {
>   	struct drm_gem_object *fbdev_bo;
>   	const struct rockchip_crtc_funcs *crtc_funcs[ROCKCHIP_MAX_CRTC];
>   	struct drm_atomic_state *state;
> +	struct iommu_domain *domain;
> +	struct drm_mm mm;
>   };
>   
>   int rockchip_register_crtc_funcs(struct drm_crtc *crtc,
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
> index 394f92b..e7cd93d 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
> @@ -19,11 +19,135 @@
>   #include <drm/rockchip_drm.h>
>   
>   #include <linux/dma-attrs.h>
> +#include <linux/iommu.h>
>   
>   #include "rockchip_drm_drv.h"
>   #include "rockchip_drm_gem.h"
>   
> -static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj,
> +static int rockchip_gem_iommu_map(struct rockchip_gem_object *rk_obj)
> +{
> +	struct drm_device *drm = rk_obj->base.dev;
> +	struct rockchip_drm_private *private = drm->dev_private;
> +	int prot = IOMMU_READ | IOMMU_WRITE;
> +	ssize_t ret;
> +
> +	ret = drm_mm_insert_node_generic(&private->mm, &rk_obj->mm,
> +					 rk_obj->base.size, PAGE_SIZE,
> +					 0, 0, 0);
> +	if (ret < 0) {
> +		DRM_ERROR("out of I/O virtual memory: %zd\n", ret);
> +		return ret;
> +	}
> +
> +	rk_obj->dma_addr = rk_obj->mm.start;
> +
> +	ret = iommu_map_sg(private->domain, rk_obj->dma_addr, rk_obj->sgt->sgl,
> +			   rk_obj->sgt->nents, prot);
> +	if (ret < 0) {
> +		DRM_ERROR("failed to map buffer: %zd\n", ret);
> +		goto err_remove_node;
> +	}
> +
> +	rk_obj->size = ret;
> +
> +	return 0;
> +
> +err_remove_node:
> +	drm_mm_remove_node(&rk_obj->mm);
> +
> +	return ret;
> +}
> +
> +static int rockchip_gem_iommu_unmap(struct rockchip_gem_object *rk_obj)
> +{
> +	struct drm_device *drm = rk_obj->base.dev;
> +	struct rockchip_drm_private *private = drm->dev_private;
> +
> +	iommu_unmap(private->domain, rk_obj->dma_addr, rk_obj->size);
> +	drm_mm_remove_node(&rk_obj->mm);
> +
> +	return 0;
> +}
> +
> +static int rockchip_gem_get_pages(struct rockchip_gem_object *rk_obj)
> +{
> +	struct drm_device *drm = rk_obj->base.dev;
> +	int ret, i;
> +	struct scatterlist *s;
> +
> +	rk_obj->pages = drm_gem_get_pages(&rk_obj->base);
> +	if (IS_ERR(rk_obj->pages))
> +		return PTR_ERR(rk_obj->pages);
> +
> +	rk_obj->num_pages = rk_obj->base.size >> PAGE_SHIFT;
> +
> +	rk_obj->sgt = drm_prime_pages_to_sg(rk_obj->pages, rk_obj->num_pages);
> +	if (IS_ERR(rk_obj->sgt)) {
> +		ret = PTR_ERR(rk_obj->sgt);
> +		goto err_put_pages;
> +	}
> +
> +	/*
> +	 * Fake up the SG table so that dma_sync_sg_for_device() can be used
> +	 * to flush the pages associated with it.
> +	 *
> +	 * TODO: Replace this by drm_clflush_sg() once it can be implemented
> +	 * without relying on symbols that are not exported.
> +	 */
> +	for_each_sg(rk_obj->sgt->sgl, s, rk_obj->sgt->nents, i)
> +		sg_dma_address(s) = sg_phys(s);
> +
> +	dma_sync_sg_for_device(drm->dev, rk_obj->sgt->sgl, rk_obj->sgt->nents,
> +			       DMA_TO_DEVICE);
> +
> +	return 0;
> +
> +err_put_pages:
> +	drm_gem_put_pages(&rk_obj->base, rk_obj->pages, false, false);
> +	return ret;
> +}
> +
> +static void rockchip_gem_put_pages(struct rockchip_gem_object *rk_obj)
> +{
> +	sg_free_table(rk_obj->sgt);
> +	kfree(rk_obj->sgt);
> +	drm_gem_put_pages(&rk_obj->base, rk_obj->pages, false, false);
> +}
> +
> +static int rockchip_gem_alloc_iommu(struct rockchip_gem_object *rk_obj,
> +				    bool alloc_kmap)
> +{
> +	int ret;
> +
> +	ret = rockchip_gem_get_pages(rk_obj);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = rockchip_gem_iommu_map(rk_obj);
> +	if (ret < 0)
> +		goto err_free;
> +
> +	if (alloc_kmap) {
> +		rk_obj->kvaddr = vmap(rk_obj->pages, rk_obj->num_pages, VM_MAP,
> +				      pgprot_writecombine(PAGE_KERNEL));
> +		if (!rk_obj->kvaddr) {
> +			DRM_ERROR("failed to vmap() buffer\n");
> +			ret = -ENOMEM;
> +			goto err_unmap;
> +		}
> +	}
> +
> +	return 0;
> +
> +err_unmap:
> +	rockchip_gem_iommu_unmap(rk_obj);
> +err_free:
> +	rockchip_gem_put_pages(rk_obj);
> +
> +	return ret;
> +}
> +
> +static int rockchip_gem_alloc_dma(struct rockchip_gem_object *rk_obj,
>   				  bool alloc_kmap)
>   {
>   	struct drm_gem_object *obj = &rk_obj->base;
> @@ -46,32 +170,93 @@ static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj,
>   	return 0;
>   }
>   
> -static void rockchip_gem_free_buf(struct rockchip_gem_object *rk_obj)
> +static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj,
> +				  bool alloc_kmap)
>   {
>   	struct drm_gem_object *obj = &rk_obj->base;
>   	struct drm_device *drm = obj->dev;
> +	struct rockchip_drm_private *private = drm->dev_private;
>   
> -	dma_free_attrs(drm->dev, obj->size, rk_obj->kvaddr, rk_obj->dma_addr,
> -		       &rk_obj->dma_attrs);
> +	if (private->domain)
> +		return rockchip_gem_alloc_iommu(rk_obj, alloc_kmap);
> +	else
> +		return rockchip_gem_alloc_dma(rk_obj, alloc_kmap);
>   }
>   
> -static int rockchip_drm_gem_object_mmap(struct drm_gem_object *obj,
> -					struct vm_area_struct *vma)
> +static void rockchip_gem_free_iommu(struct rockchip_gem_object *rk_obj)
> +{
> +	vunmap(rk_obj->kvaddr);
> +	rockchip_gem_iommu_unmap(rk_obj);
> +	rockchip_gem_put_pages(rk_obj);
> +}
>   
> +static void rockchip_gem_free_dma(struct rockchip_gem_object *rk_obj)
>   {
> +	struct drm_gem_object *obj = &rk_obj->base;
> +	struct drm_device *drm = obj->dev;
> +
> +	dma_free_attrs(drm->dev, obj->size, rk_obj->kvaddr,
> +		       rk_obj->dma_addr, &rk_obj->dma_attrs);
> +}
> +
> +static void rockchip_gem_free_buf(struct rockchip_gem_object *rk_obj)
> +{
> +	if (rk_obj->pages)
> +		rockchip_gem_free_iommu(rk_obj);
> +	else
> +		rockchip_gem_free_dma(rk_obj);
> +}
> +
> +static int rockchip_drm_gem_object_mmap_iommu(struct drm_gem_object *obj,
> +					      struct vm_area_struct *vma)
> +{
> +	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
> +	unsigned int i, count = obj->size >> PAGE_SHIFT;
> +	unsigned long user_count = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
> +	unsigned long uaddr = vma->vm_start;
>   	int ret;
> +
> +	if (user_count == 0 || user_count > count)
> +		return -ENXIO;
> +
> +	for (i = 0; i < user_count; i++) {
> +		ret = vm_insert_page(vma, uaddr, rk_obj->pages[i]);
> +		if (ret)
> +			return ret;
> +		uaddr += PAGE_SIZE;
> +	}
> +
> +	return 0;
> +}
> +
> +static int rockchip_drm_gem_object_mmap_dma(struct drm_gem_object *obj,
> +					    struct vm_area_struct *vma)
> +{
>   	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
>   	struct drm_device *drm = obj->dev;
>   
> +	return dma_mmap_attrs(drm->dev, vma, rk_obj->kvaddr, rk_obj->dma_addr,
> +			      obj->size, &rk_obj->dma_attrs);
> +}
> +
> +static int rockchip_drm_gem_object_mmap(struct drm_gem_object *obj,
> +					struct vm_area_struct *vma)
> +{
> +	int ret;
> +	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
> +
>   	/*
> -	 * dma_alloc_attrs() allocated a struct page table for rk_obj, so clear
> +	 * We allocated a struct page table for rk_obj, so clear
>   	 * VM_PFNMAP flag that was set by drm_gem_mmap_obj()/drm_gem_mmap().
>   	 */
>   	vma->vm_flags &= ~VM_PFNMAP;
>   	vma->vm_pgoff = 0;
>   
> -	ret = dma_mmap_attrs(drm->dev, vma, rk_obj->kvaddr, rk_obj->dma_addr,
> -			     obj->size, &rk_obj->dma_attrs);
> +	if (rk_obj->pages)
> +		ret = rockchip_drm_gem_object_mmap_iommu(obj, vma);
> +	else
> +		ret = rockchip_drm_gem_object_mmap_dma(obj, vma);
> +
>   	if (ret)
>   		drm_gem_vm_close(vma);
>   
> @@ -121,7 +306,7 @@ struct rockchip_gem_object *
>   
>   	obj = &rk_obj->base;
>   
> -	drm_gem_private_object_init(drm, obj, size);
> +	drm_gem_object_init(drm, obj, size);
>   
>   	ret = rockchip_gem_alloc_buf(rk_obj, alloc_kmap);
>   	if (ret)
> @@ -277,6 +462,9 @@ struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj)
>   	struct sg_table *sgt;
>   	int ret;
>   
> +	if (rk_obj->pages)
> +		return drm_prime_pages_to_sg(rk_obj->pages, rk_obj->num_pages);
> +
>   	sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
>   	if (!sgt)
>   		return ERR_PTR(-ENOMEM);
> @@ -297,6 +485,10 @@ void *rockchip_gem_prime_vmap(struct drm_gem_object *obj)
>   {
>   	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
>   
> +	if (rk_obj->pages)
> +		return vmap(rk_obj->pages, rk_obj->num_pages, VM_MAP,
> +			    pgprot_writecombine(PAGE_KERNEL));
> +
>   	if (dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, &rk_obj->dma_attrs))
>   		return NULL;
>   
> @@ -305,5 +497,12 @@ void *rockchip_gem_prime_vmap(struct drm_gem_object *obj)
>   
>   void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
>   {
> -	/* Nothing to do */
> +	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
> +
> +	if (rk_obj->pages) {
> +		vunmap(vaddr);
> +		return;
> +	}
> +
> +	/* Nothing to do if allocated by DMA mapping API. */
>   }
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
> index 3584b94..6aa61b2 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
> @@ -23,7 +23,16 @@ struct rockchip_gem_object {
>   
>   	void *kvaddr;
>   	dma_addr_t dma_addr;
> +
> +	/* Used when IOMMU is disabled */
>   	struct dma_attrs dma_attrs;
> +
> +	/* Used when IOMMU is enabled */
> +	struct drm_mm_node mm;
> +	unsigned long num_pages;
> +	struct page **pages;
> +	struct sg_table *sgt;
> +	size_t size;
>   };
>   
>   struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj);


-- 
?ark Yao




From mark.yao at rock-chips.com  Thu Jul  7 18:01:58 2016
From: mark.yao at rock-chips.com (Mark yao)
Date: Fri, 8 Jul 2016 09:01:58 +0800
Subject: iommu/rockchip: Fix bugs and enable on ARM64
In-Reply-To: <20160627125751.GN11432@8bytes.org>
References: <1466734413-7453-1-git-send-email-zhengsq@rock-chips.com>
 <20160627125751.GN11432@8bytes.org>
Message-ID: <577EFB86.1060502@rock-chips.com>

On 2016?06?27? 20:57, Joerg Roedel wrote:
> On Fri, Jun 24, 2016 at 10:13:25AM +0800, Shunqian Zheng wrote:
>>   drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 100 +++++++------
>>   drivers/gpu/drm/rockchip/rockchip_drm_drv.h |   3 +
>>   drivers/gpu/drm/rockchip/rockchip_drm_gem.c | 221 ++++++++++++++++++++++++++--
>>   drivers/gpu/drm/rockchip/rockchip_drm_gem.h |   9 ++
>>   drivers/iommu/Kconfig                       |   2 +-
>>   drivers/iommu/rockchip-iommu.c              | 181 +++++++++++++++++------
>>   6 files changed, 413 insertions(+), 103 deletions(-)
> Applied the IOMMU patches, thanks.
>
>
> 	Joerg
>
>
>
>

Hi Joerg

The drm rockchip patches are dependent on iommu/rockchip patches, can 
you also apply these patches together? So that can avoid compile problem.

Thanks.

-- 
?ark Yao




From ykk at rock-chips.com  Thu Jul  7 19:12:34 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 8 Jul 2016 10:12:34 +0800
Subject: [PATCH v3 2/4] drm/rockchip: add an common abstracted PSR driver
In-Reply-To: <CAOw6vbKMWEEDEqw0_ba_FGO-2mOBg3uanQzk5nbnCtowWdLu9w@mail.gmail.com>
References: <1467364685-21390-1-git-send-email-ykk@rock-chips.com>
 <1467364754-21674-1-git-send-email-ykk@rock-chips.com>
 <CAOw6vbKMWEEDEqw0_ba_FGO-2mOBg3uanQzk5nbnCtowWdLu9w@mail.gmail.com>
Message-ID: <577F0C12.60208@rock-chips.com>

Sean,

On 07/02/2016 02:00 AM, Sean Paul wrote:
> On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk at rock-chips.com> wrote:
>> The PSR driver have exported four symbols for specific device driver:
>> - rockchip_drm_psr_register()
>> - rockchip_drm_psr_unregister()
>> - rockchip_drm_psr_enable()
>> - rockchip_drm_psr_disable()
>> - rockchip_drm_psr_flush()
>>
>> Encoder driver should call the register/unregister interfaces to hook
>> itself into common PSR driver, encoder have implement the 'psr_set'
>> callback which use the set PSR state in hardware side.
>>
>> Crtc driver would call the enable/disable interfaces when vblank is
>> enable/disable, after that the common PSR driver would call the encoder
>> registered callback to set the PSR state.
>>
> This feels overly complicated. It seems like you could cut out a bunch
> of code by just coding the psr functions into vop and
> analogix_dp-rockchip. I suppose the only reason to keep it abstracted
> would be if you plan on supporting psr in a different encoder or crtc
> in rockchip, or if you're planning on moving this into drm core.

I split the psr code into a separate driver, cause it seems make things 
more clearly.

'rockchip_drm_vop.c' need to call the PSR enable/disable interfaces, and 
'rockchip_drm_drv.c' need to call the PSR flush interface. If I put the 
whole psr code into analogix_dp-rockchip driver, then the Rockchip drm 
core driver would tie to analogix_dp-rockchip driver very deeply, and 
hard to expand if there are two PSR device in further.

> Perhaps others will disagree with this sentiment and this is the right
> thing to do.
>
>> Fb driver would call the flush interface in 'fb->dirty' callback, this
>> helper function would force all PSR enabled encoders to exit from PSR
>> for 3 seconds.
>>
>> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
>> ---
>> Changes in v3:
>> - split the psr flow into an common abstracted PSR driver
>> - implement the 'fb->dirty' callback function (Daniel)
>> - avoid to use notify to acqiure for vact event (Daniel)
>> - remove psr_active() callback which introduce in v2
>>
>> Changes in v2: None
>>
>>   drivers/gpu/drm/rockchip/Makefile           |   2 +-
>>   drivers/gpu/drm/rockchip/rockchip_drm_fb.c  |  12 ++
>>   drivers/gpu/drm/rockchip/rockchip_drm_psr.c | 200 ++++++++++++++++++++++++++++
>>   drivers/gpu/drm/rockchip/rockchip_drm_psr.h |  12 ++
>>   drivers/gpu/drm/rockchip/rockchip_drm_vop.c |  24 ++++
>>   5 files changed, 249 insertions(+), 1 deletion(-)
>>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.c
>>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.h
>>
>> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
>> index 05d0713..9746365 100644
>> --- a/drivers/gpu/drm/rockchip/Makefile
>> +++ b/drivers/gpu/drm/rockchip/Makefile
>> @@ -3,7 +3,7 @@
>>   # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
>>
>>   rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
>> -               rockchip_drm_gem.o rockchip_drm_vop.o
>> +               rockchip_drm_gem.o rockchip_drm_psr.o rockchip_drm_vop.o
>>   rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
>>
>>   obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>> index 20f12bc..0fec18f 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>> @@ -21,6 +21,7 @@
>>
>>   #include "rockchip_drm_drv.h"
>>   #include "rockchip_drm_gem.h"
>> +#include "rockchip_drm_psr.h"
>>
>>   #define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
>>
>> @@ -66,9 +67,20 @@ static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
>>                                       rockchip_fb->obj[0], handle);
>>   }
>>
>> +static int rockchip_drm_fb_dirty(struct drm_framebuffer *fb,
>> +                                struct drm_file *file,
>> +                                unsigned int flags, unsigned int color,
>> +                                struct drm_clip_rect *clips,
>> +                                unsigned int num_clips)
>> +{
>> +       rockchip_drm_psr_flush();
>> +       return 0;
>> +}
>> +
>>   static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
>>          .destroy        = rockchip_drm_fb_destroy,
>>          .create_handle  = rockchip_drm_fb_create_handle,
>> +       .dirty          = rockchip_drm_fb_dirty,
>>   };
>>
>>   static struct rockchip_drm_fb *
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
>> new file mode 100644
>> index 0000000..c044443
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
>> @@ -0,0 +1,200 @@
>> +#include <drm/drm_crtc_helper.h>
>> +
>> +#include "rockchip_drm_psr.h"
>> +
>> +#define PSR_FLUSH_TIMEOUT      msecs_to_jiffies(3000) /* 3 seconds */
>> +
>> +static LIST_HEAD(psr_list);
>> +static DEFINE_MUTEX(psr_list_mutex);
> I'm not crazy about these globals. Perhaps you can initialize them
> with the rockchip driver and tuck them in a driver-level struct
> (rockchip_drm_private or something).

Okay, done.

>
>> +
>> +enum psr_state {
>> +       PSR_FLUSH,
>> +       PSR_ENABLE,
>> +       PSR_DISABLE,
>> +};
>> +
>> +struct psr_drv {
>> +       struct list_head list;
>> +       enum psr_state state;
>> +       struct mutex state_mutex;
>> +
>> +       struct timer_list flush_timer;
>> +
>> +       struct drm_encoder *encoder;
>> +       int (*set)(struct drm_encoder *encoder, bool enable);
>> +};
>> +
>> +static struct psr_drv *find_psr_by_crtc(struct drm_crtc *crtc)
>> +{
>> +       struct psr_drv *psr;
>> +
>> +       mutex_lock(&psr_list_mutex);
>> +       list_for_each_entry(psr, &psr_list, list) {
>> +               if (psr->encoder->crtc == crtc) {
>> +                       mutex_unlock(&psr_list_mutex);
>> +                       return psr;
>> +               }
>> +       }
>> +       mutex_unlock(&psr_list_mutex);
>> +
>> +       return ERR_PTR(-ENODEV);
>> +}
>> +
>> +static void psr_enable(struct psr_drv *psr)
>> +{
>> +       if (psr->state == PSR_ENABLE)
>> +               return;
> Should you be worried about races by accessing this outside of the state mutex?
>

Yes, this should under the protect of state_mutex.

>> +
>> +       mutex_lock(&psr->state_mutex);
>> +       psr->state = PSR_ENABLE;
>> +       psr->set(psr->encoder, true);
>> +       mutex_unlock(&psr->state_mutex);
>> +}
>> +
>> +static void psr_disable(struct psr_drv *psr)
>> +{
>> +       if (psr->state == PSR_DISABLE)
>> +               return;
>> +
>> +       mutex_lock(&psr->state_mutex);
>> +       psr->state = PSR_DISABLE;
>> +       psr->set(psr->encoder, false);
>> +       mutex_unlock(&psr->state_mutex);
>> +}
>> +
>> +static void psr_flush_handler(unsigned long data)
>> +{
>> +       struct psr_drv *psr = (struct psr_drv *)data;
>> +
>> +       if (!psr || psr->state != PSR_FLUSH)
>> +               return;
>> +
>> +       psr_enable(psr);
>> +}
>> +
>> +/**
>> + * rockchip_drm_psr_enable - enable the encoder PSR which bind to given CRTC
>> + * @crtc: CRTC to obtain the PSR encoder
>> + *
>> + * Returns:
>> + * Zero on success, negative errno on failure.
>> + */
>> +int rockchip_drm_psr_enable(struct drm_crtc *crtc)
>> +{
>> +       struct psr_drv *psr = find_psr_by_crtc(crtc);
>> +
>> +       if (IS_ERR(psr))
>> +               return PTR_ERR(psr);
>> +
>> +       psr_enable(psr);
>> +       return 0;
>> +}
>> +EXPORT_SYMBOL(rockchip_drm_psr_enable);
>> +
>> +/**
>> + * rockchip_drm_psr_disable - disable the encoder PSR which bind to given CRTC
>> + * @crtc: CRTC to obtain the PSR encoder
>> + *
>> + * Returns:
>> + * Zero on success, negative errno on failure.
>> + */
>> +int rockchip_drm_psr_disable(struct drm_crtc *crtc)
>> +{
>> +       struct psr_drv *psr = find_psr_by_crtc(crtc);
>> +
>> +       if (IS_ERR(psr))
>> +               return PTR_ERR(psr);
>> +
>> +       psr_disable(psr);
>> +       return 0;
>> +}
>> +EXPORT_SYMBOL(rockchip_drm_psr_disable);
>> +
>> +/**
>> + * rockchip_drm_psr_flush - force to flush all registered PSR encoders
>> + *
>> + * Disable the PSR function for all registered encoders, and then enable the
>> + * PSR function back after 5 second. If encoder PSR state have been changed
> s/5 second/PSR_FLUSH_TIMEOUT/

Done

>
>> + * during flush time, then keep the state no change after flush timeout.
>> + *
>> + * Returns:
>> + * Zero on success, negative errno on failure.
>> + */
>> +void rockchip_drm_psr_flush(void)
>> +{
>> +       struct psr_drv *psr;
>> +
>> +       mutex_lock(&psr_list_mutex);
>> +       list_for_each_entry(psr, &psr_list, list) {
>> +               if (psr->state == PSR_DISABLE)
>> +                       continue;
>> +
>> +               mod_timer(&psr->flush_timer,
>> +                         round_jiffies_up(jiffies + PSR_FLUSH_TIMEOUT));
>> +
>> +               psr_disable(psr);
>> +               psr->state = PSR_FLUSH;
> This is set outside of state_mutex, is that intentional?

No, I just don't want to create another interface like psr_flush(). But 
it's a bad idea to let 'state' change outside the protect of 'state_mutex'.

Thanks,
- Yakir

>> +       }
>> +       mutex_unlock(&psr_list_mutex);
>> +}
>> +EXPORT_SYMBOL(rockchip_drm_psr_flush);
>> +
>> +/**
>> + * rockchip_drm_psr_register - register encoder to psr driver
>> + * @encoder: encoder that obtain the PSR function
>> + * @psr_set: call back to set PSR state
>> + *
>> + * Returns:
>> + * Zero on success, negative errno on failure.
>> + */
>> +int rockchip_drm_psr_register(struct drm_encoder *encoder,
>> +                             int (*psr_set)(struct drm_encoder *, bool enable))
>> +{
>> +       struct psr_drv *psr;
>> +
>> +       if (!encoder || !psr_set)
>> +               return -EINVAL;
>> +
>> +       psr = kzalloc(sizeof(struct psr_drv), GFP_KERNEL);
>> +       if (!psr)
>> +               return -ENOMEM;
>> +
>> +       setup_timer(&psr->flush_timer, psr_flush_handler, (unsigned long)psr);
>> +
>> +       mutex_init(&psr->state_mutex);
>> +
>> +       psr->state = PSR_DISABLE;
>> +       psr->encoder = encoder;
>> +       psr->set = psr_set;
>> +
>> +       mutex_lock(&psr_list_mutex);
>> +       list_add_tail(&psr->list, &psr_list);
>> +       mutex_unlock(&psr_list_mutex);
>> +
>> +       return 0;
>> +}
>> +EXPORT_SYMBOL(rockchip_drm_psr_register);
>> +
>> +/**
>> + * rockchip_drm_psr_unregister - unregister encoder to psr driver
>> + * @encoder: encoder that obtain the PSR function
>> + * @psr_set: call back to set PSR state
>> + *
>> + * Returns:
>> + * Zero on success, negative errno on failure.
>> + */
>> +void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
>> +{
>> +       struct psr_drv *psr;
>> +
>> +       mutex_lock(&psr_list_mutex);
>> +       list_for_each_entry(psr, &psr_list, list) {
>> +               if (psr->encoder == encoder) {
>> +                       del_timer(&psr->flush_timer);
>> +                       list_del(&psr->list);
>> +                       kfree(psr);
>> +               }
>> +       }
>> +       mutex_unlock(&psr_list_mutex);
>> +}
>> +EXPORT_SYMBOL(rockchip_drm_psr_unregister);
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.h b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
>> new file mode 100644
>> index 0000000..622f605
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
>> @@ -0,0 +1,12 @@
>> +#ifndef __ROCKCHIP_DRM_PSR___
>> +#define __ROCKCHIP_DRM_PSR___
>> +
>> +void rockchip_drm_psr_flush(void);
>> +int rockchip_drm_psr_enable(struct drm_crtc *crtc);
>> +int rockchip_drm_psr_disable(struct drm_crtc *crtc);
>> +
>> +int rockchip_drm_psr_register(struct drm_encoder *encoder,
>> +                           int (*psr_set)(struct drm_encoder *, bool enable));
>> +void rockchip_drm_psr_unregister(struct drm_encoder *encoder);
>> +
>> +#endif /* __ROCKCHIP_DRM_PSR__ */
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> index cd3cac5..3c6dfc5 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> @@ -34,6 +34,7 @@
>>   #include "rockchip_drm_drv.h"
>>   #include "rockchip_drm_gem.h"
>>   #include "rockchip_drm_fb.h"
>> +#include "rockchip_drm_psr.h"
>>   #include "rockchip_drm_vop.h"
>>
>>   #define __REG_SET_RELAXED(x, off, mask, shift, v, write_mask) \
>> @@ -121,6 +122,9 @@ struct vop {
>>          /* protected by dev->event_lock */
>>          struct drm_pending_vblank_event *event;
>>
>> +       bool psr_enabled;
>> +       struct delayed_work psr_work;
>> +
>>          struct completion line_flag_completion;
>>
>>          const struct vop_data *data;
>> @@ -911,6 +915,16 @@ static const struct drm_plane_funcs vop_plane_funcs = {
>>          .atomic_destroy_state = vop_atomic_plane_destroy_state,
>>   };
>>
>> +static void vop_psr_work(struct work_struct *work)
>> +{
>> +       struct vop *vop = container_of(work, typeof(*vop), psr_work.work);
>> +
>> +       if (vop->psr_enabled)
>> +               rockchip_drm_psr_enable(&vop->crtc);
>> +       else
>> +               rockchip_drm_psr_disable(&vop->crtc);
>> +}
>> +
>>   static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
>>   {
>>          struct vop *vop = to_vop(crtc);
>> @@ -925,6 +939,9 @@ static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
>>
>>          spin_unlock_irqrestore(&vop->irq_lock, flags);
>>
>> +       vop->psr_enabled = false;
>> +       schedule_delayed_work(&vop->psr_work, msecs_to_jiffies(10));
>> +
>>          return 0;
>>   }
>>
>> @@ -941,6 +958,9 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc)
>>          VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0);
>>
>>          spin_unlock_irqrestore(&vop->irq_lock, flags);
>> +
>> +       vop->psr_enabled = true;
>> +       schedule_delayed_work(&vop->psr_work, msecs_to_jiffies(10));
>>   }
>>
>>   static void vop_crtc_wait_for_update(struct drm_crtc *crtc)
>> @@ -1582,6 +1602,10 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
>>                  return ret;
>>
>>          pm_runtime_enable(&pdev->dev);
>> +
>> +       vop->psr_enabled = false;
>> +       INIT_DELAYED_WORK(&vop->psr_work, vop_psr_work);
>> +
>>          return 0;
>>   }
>>
>> --
>> 1.9.1
>>
>>
>
>




From ykk at rock-chips.com  Thu Jul  7 19:26:27 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 8 Jul 2016 10:26:27 +0800
Subject: [PATCH v3 3/4] drm/bridge: analogix_dp: add the PSR function
 support
In-Reply-To: <CAOw6vbJ4H9xzeaA-2OKsG+pGwdvehS91TZDPfUyq8R-=e70bPQ@mail.gmail.com>
References: <1467364685-21390-1-git-send-email-ykk@rock-chips.com>
 <1467364768-21741-1-git-send-email-ykk@rock-chips.com>
 <CAOw6vbJ4H9xzeaA-2OKsG+pGwdvehS91TZDPfUyq8R-=e70bPQ@mail.gmail.com>
Message-ID: <577F0F53.9060404@rock-chips.com>

Sean,

Thanks for your review.

On 07/02/2016 03:46 AM, Sean Paul wrote:
> On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk at rock-chips.com> wrote:
>> The full name of PSR is Panel Self Refresh, panel device could refresh
>> itself with the hardware framebuffer in panel, this would make lots of
>> sense to save the power consumption.
>>
>> This patch have exported two symbols for platform driver to implement
>> the PSR function in hardware side:
>> - analogix_dp_active_psr()
>> - analogix_dp_inactive_psr()
>>
>> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
>> ---
>> Changes in v3:
>> - split analogix_dp_enable_psr(), make it more clearly
>>      analogix_dp_detect_sink_psr()
>>      analogix_dp_enable_sink_psr()
>> - remove some nosie register setting comments
>>
>> Changes in v2:
>> - introduce in v2, splite the common Analogix DP changes out
>>
>>   drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 64 ++++++++++++++++++++++
>>   drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |  4 ++
>>   drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  | 54 ++++++++++++++++++
>>   drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h  | 28 ++++++++++
>>   include/drm/bridge/analogix_dp.h                   |  3 +
>>   5 files changed, 153 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>> index 32715da..b557097 100644
>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>> @@ -97,6 +97,66 @@ static int analogix_dp_detect_hpd(struct analogix_dp_device *dp)
>>          return 0;
>>   }
>>
>> +int analogix_dp_active_psr(struct device *dev)
>> +{
>> +       struct analogix_dp_device *dp = dev_get_drvdata(dev);
>> +
>> +       if (!dp->psr_support)
>> +               return -EINVAL;
>> +
>> +       analogix_dp_send_psr_spd(dp, EDP_VSC_PSR_STATE_ACTIVE |
>> +                                EDP_VSC_PSR_CRC_VALUES_VALID);
>> +       return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(analogix_dp_active_psr);
>> +
>> +int analogix_dp_inactive_psr(struct device *dev)
>> +{
>> +       struct analogix_dp_device *dp = dev_get_drvdata(dev);
>> +
>> +       if (!dp->psr_support)
>> +               return -EINVAL;
>> +
>> +       analogix_dp_send_psr_spd(dp, 0);
>> +       return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(analogix_dp_inactive_psr);
>> +
>> +static bool analogix_dp_detect_sink_psr(struct analogix_dp_device *dp)
>> +{
>> +       unsigned char psr_version;
>> +
>> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_SUPPORT, &psr_version);
>> +       dev_info(dp->dev, "Panel PSR version : %x\n", psr_version);
>> +
> This info message is likely to be spammy since it's printed everytime
> the panel toggle on. Perhaps downgrade to debug level.

Okay, done.

>> +       return (psr_version & DP_PSR_IS_SUPPORTED) ? true : false;
>> +}
>> +
>> +static int analogix_dp_enable_sink_psr(struct analogix_dp_device *dp)
> Return type is int, but the function never fails and you don't check
> the return value when calling it. Seems like this should be void.

Done.

>> +{
>> +       unsigned char psr_en;
>> +
>> +       /* Disable psr function */
>> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
>> +       psr_en &= ~DP_PSR_ENABLE;
>> +       analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
>> +
>> +       /* Main-Link transmitter remains active during PSR active states */
>> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
>> +       psr_en = DP_PSR_MAIN_LINK_ACTIVE | DP_PSR_CRC_VERIFICATION;
> Why read psr_en if you're just going to overwrite it? Perhaps you meant |= here.
>

Yes, it's my mistaken, no need to read the DP_PSR_EN_CFG, just configure 
it directly is enough.

>> +       analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
>> +
>> +       /* Enable psr function */
>> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
>> +       psr_en = DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE |
>> +                DP_PSR_CRC_VERIFICATION;
> Again, no need to read if you're just overwriting.

Yes, ditto

>> +       analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
>> +
>> +       analogix_dp_enable_psr_crc(dp);
>> +
>> +       return 0;
>> +}
>> +
>>   static unsigned char analogix_dp_calc_edid_check_sum(unsigned char *edid_data)
>>   {
>>          int i;
>> @@ -921,6 +981,10 @@ static void analogix_dp_commit(struct analogix_dp_device *dp)
>>
>>          /* Enable video */
>>          analogix_dp_start_video(dp);
>> +
>> +       dp->psr_support = analogix_dp_detect_sink_psr(dp);
>> +       if (dp->psr_support)
>> +               analogix_dp_enable_sink_psr(dp);
>>   }
>>
>>   int analogix_dp_get_modes(struct drm_connector *connector)
>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
>> index b456380..6ca5dde 100644
>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
>> @@ -177,6 +177,7 @@ struct analogix_dp_device {
>>          int                     hpd_gpio;
>>          bool                    force_hpd;
>>          unsigned char           edid[EDID_BLOCK_LENGTH * 2];
>> +       bool                    psr_support;
>>
>>          struct analogix_dp_plat_data *plat_data;
>>   };
>> @@ -278,4 +279,7 @@ int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp);
>>   void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp);
>>   void analogix_dp_enable_scrambling(struct analogix_dp_device *dp);
>>   void analogix_dp_disable_scrambling(struct analogix_dp_device *dp);
>> +void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp);
>> +void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, int db1);
>> +
>>   #endif /* _ANALOGIX_DP_CORE_H */
>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
>> index 48030f0..e8372c7 100644
>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
>> @@ -1322,3 +1322,57 @@ void analogix_dp_disable_scrambling(struct analogix_dp_device *dp)
>>          reg |= SCRAMBLING_DISABLE;
>>          writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
>>   }
>> +
>> +void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp)
>> +{
>> +       writel(PSR_VID_CRC_FLUSH | PSR_VID_CRC_ENABLE,
>> +              dp->reg_base + ANALOGIX_DP_CRC_CON);
>> +
>> +       usleep_range(10, 20);
> Is this sleep arbitrary, or documented somewhere? Could you add a
> comment explaining how this was arrived at?
>

Yes, this sleep is arbitrary, there is no document about the

>> +
>> +       writel(PSR_VID_CRC_ENABLE, dp->reg_base + ANALOGIX_DP_CRC_CON);
>> +}
>> +
>> +void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, int db1)
>> +{
>> +       unsigned int val;
>> +
>> +       /* don't send info frame */
>> +       val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>> +       val &= ~IF_EN;
>> +       writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>> +
>> +       /* configure single frame update mode */
>> +       writel(0x3, dp->reg_base + ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL);
>> +
>> +       /* configure VSC HB0 ~ HB3 */
>> +       writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_HB0);
>> +       writel(0x07, dp->reg_base + ANALOGIX_DP_SPD_HB1);
>> +       writel(0x02, dp->reg_base + ANALOGIX_DP_SPD_HB2);
>> +       writel(0x08, dp->reg_base + ANALOGIX_DP_SPD_HB3);
>> +
>> +       /* configure VSC HB0 ~ HB3 */
>> +       writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_PB0);
>> +       writel(0x16, dp->reg_base + ANALOGIX_DP_SPD_PB1);
>> +       writel(0xCE, dp->reg_base + ANALOGIX_DP_SPD_PB2);
>> +       writel(0x5D, dp->reg_base + ANALOGIX_DP_SPD_PB3);
>> +
>> +       /* configure DB0 / DB1 values */
>> +       writel(0x00, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB0);
> Lots of hardcoded values here. I think this could be cleaned up.

For "HB0~HB3", "PB0~PB3" and "DB1", I don't understand very well. Those 
seems to be a kind of head number, I got those magic values from our IC 
side. So I think those should be okay to keep the hardcode values here :-D

But for the "0x3" in "ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL", I would fix it 
with suitable macro.

>> +       writel(db1, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB1);
>> +
>> +       /* set reuse spd inforframe */
>> +       val = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
>> +       val |= REUSE_SPD_EN;
>> +       writel(val, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
>> +
>> +       /* mark info frame update */
>> +       val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>> +       val = (val | IF_UP) & ~IF_EN;
>> +       writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>> +
>> +       /* send info frame */
>> +       val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>> +       val |= IF_EN;
>> +       writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>> +}
>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
>> index cdcc6c5..a2698e4 100644
>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
>> @@ -22,6 +22,8 @@
>>   #define ANALOGIX_DP_VIDEO_CTL_8                        0x3C
>>   #define ANALOGIX_DP_VIDEO_CTL_10               0x44
>>
>> +#define ANALOGIX_DP_SPDIF_AUDIO_CTL_0          0xD8
>> +
>>   #define ANALOGIX_DP_PLL_REG_1                  0xfc
>>   #define ANALOGIX_DP_PLL_REG_2                  0x9e4
>>   #define ANALOGIX_DP_PLL_REG_3                  0x9e8
>> @@ -30,6 +32,21 @@
>>
>>   #define ANALOGIX_DP_PD                         0x12c
>>
>> +#define ANALOGIX_DP_IF_TYPE                    0x244
>> +#define ANALOGIX_DP_IF_PKT_DB1                 0x254
>> +#define ANALOGIX_DP_IF_PKT_DB2                 0x258
>> +#define ANALOGIX_DP_SPD_HB0                    0x2F8
>> +#define ANALOGIX_DP_SPD_HB1                    0x2FC
>> +#define ANALOGIX_DP_SPD_HB2                    0x300
>> +#define ANALOGIX_DP_SPD_HB3                    0x304
>> +#define ANALOGIX_DP_SPD_PB0                    0x308
>> +#define ANALOGIX_DP_SPD_PB1                    0x30C
>> +#define ANALOGIX_DP_SPD_PB2                    0x310
>> +#define ANALOGIX_DP_SPD_PB3                    0x314
>> +#define ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL      0x318
>> +#define ANALOGIX_DP_VSC_SHADOW_DB0             0x31C
>> +#define ANALOGIX_DP_VSC_SHADOW_DB1             0x320
>> +
>>   #define ANALOGIX_DP_LANE_MAP                   0x35C
>>
>>   #define ANALOGIX_DP_ANALOG_CTL_1               0x370
>> @@ -103,6 +120,8 @@
>>
>>   #define ANALOGIX_DP_SOC_GENERAL_CTL            0x800
>>
>> +#define ANALOGIX_DP_CRC_CON                    0x890
>> +
>>   /* ANALOGIX_DP_TX_SW_RESET */
>>   #define RESET_DP_TX                            (0x1 << 0)
>>
>> @@ -151,6 +170,7 @@
>>   #define VID_CHK_UPDATE_TYPE_SHIFT              (4)
>>   #define VID_CHK_UPDATE_TYPE_1                  (0x1 << 4)
>>   #define VID_CHK_UPDATE_TYPE_0                  (0x0 << 4)
>> +#define REUSE_SPD_EN                           (0x1 << 3)
>>
>>   /* ANALOGIX_DP_VIDEO_CTL_8 */
>>   #define VID_HRES_TH(x)                         (((x) & 0xf) << 4)
>> @@ -376,4 +396,12 @@
>>   #define VIDEO_MODE_SLAVE_MODE                  (0x1 << 0)
>>   #define VIDEO_MODE_MASTER_MODE                 (0x0 << 0)
>>
>> +/* ANALOGIX_DP_PKT_SEND_CTL */
>> +#define IF_UP                                  (0x1 << 4)
>> +#define IF_EN                                  (0x1 << 0)
>> +
>> +/* ANALOGIX_DP_CRC_CON */
>> +#define PSR_VID_CRC_FLUSH                      (0x1 << 2)
>> +#define PSR_VID_CRC_ENABLE                     (0x1 << 0)
>> +
>>   #endif /* _ANALOGIX_DP_REG_H */
>> diff --git a/include/drm/bridge/analogix_dp.h b/include/drm/bridge/analogix_dp.h
>> index 261b86d..183a336 100644
>> --- a/include/drm/bridge/analogix_dp.h
>> +++ b/include/drm/bridge/analogix_dp.h
>> @@ -38,6 +38,9 @@ struct analogix_dp_plat_data {
>>                           struct drm_connector *);
>>   };
>>
>> +int analogix_dp_active_psr(struct device *dev);
>> +int analogix_dp_inactive_psr(struct device *dev);
> Why active/inactive instead of enable/disable, which is used everywhere else?

Done

Thanks,
- Yakir

>> +
>>   int analogix_dp_resume(struct device *dev);
>>   int analogix_dp_suspend(struct device *dev);
>>
>> --
>> 1.9.1
>>
>>
>
>




From ykk at rock-chips.com  Thu Jul  7 19:30:10 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 8 Jul 2016 10:30:10 +0800
Subject: [PATCH v3 1/4] drm/rockchip: vop: export line flag function
In-Reply-To: <CAOw6vbKuy5wCb_J0ENP5OGf7B31ut-FDzBzE5NLiFzEY5sWqsg@mail.gmail.com>
References: <1467364685-21390-1-git-send-email-ykk@rock-chips.com>
 <1467364745-21621-1-git-send-email-ykk@rock-chips.com>
 <CAOw6vb+vS3O+Db1_1RSGup7Y2tNjRkxyz7ah817R6nXceSHeFQ@mail.gmail.com>
 <CAOw6vbKuy5wCb_J0ENP5OGf7B31ut-FDzBzE5NLiFzEY5sWqsg@mail.gmail.com>
Message-ID: <577F1032.5040908@rock-chips.com>

Sean,

On 07/01/2016 11:32 PM, Sean Paul wrote:
> On Fri, Jul 1, 2016 at 11:30 AM, Sean Paul <seanpaul at chromium.org> wrote:
>> On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk at rock-chips.com> wrote:
>>> VOP have integrated a hardware counter which indicate the exact display
>>> line that vop is scanning. And if we're interested in a specific line,
>>> we can set the line number to vop line_flag register, and then vop would
>>> generate a line_flag interrupt for it.
>>>
>>> For example eDP PSR function is interested in the vertical blanking
>>> period, then driver could set the line number to zero.
>>>
>>> This patch have exported a symbol that allow other driver to listen the
>>> line flag event with given timeout limit:
>>> -  rockchip_drm_wait_line_flag()
>>>
>>> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
>>> ---
>>> Changes in v3:
>>> - Export the 'rockchip_drm_wait_line_flag' symbol, and document it.
>>> - Add 'line_flag_num_0' for RK3288/RK3036
>>> - Remove the notify for waiting line_flag event (Daniel)
>>>
>>> Changes in v2:
>>> - Introduce in v2, split VOP line flag changes out
>>>
>>>   drivers/gpu/drm/rockchip/rockchip_drm_drv.h |   3 +
>>>   drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 103 ++++++++++++++++++++++++++++
>>>   drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   3 +
>>>   drivers/gpu/drm/rockchip/rockchip_vop_reg.c |   4 ++
>>>   4 files changed, 113 insertions(+)
>>>
>>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>>> index ea39329..239b830 100644
>>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>>> @@ -70,4 +70,7 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
>>>                                     struct device *dev);
>>>   void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
>>>                                      struct device *dev);
>>> +int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
>>> +                               unsigned int mstimeout);
>>> +
>>>   #endif /* _ROCKCHIP_DRM_DRV_H_ */
>>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>>> index c8a62a8..cd3cac5 100644
>>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>>> @@ -121,6 +121,8 @@ struct vop {
>>>          /* protected by dev->event_lock */
>>>          struct drm_pending_vblank_event *event;
>>>
>>> +       struct completion line_flag_completion;
>>> +
>>>          const struct vop_data *data;
>>>
>>>          uint32_t *regsbak;
>>> @@ -431,6 +433,59 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop)
>>>          spin_unlock_irqrestore(&vop->irq_lock, flags);
>>>   }
>>>
>>> +/*
>>> + * (1) each frame starts at the start of the Vsync pulse which is signaled by
>>> + *     the "FRAME_SYNC" interrupt.
>>> + * (2) the active data region of each frame ends at dsp_vact_end
>>> + * (3) we should program this same number (dsp_vact_end) into dsp_line_frag_num,
>>> + *      to get "LINE_FLAG" interrupt at the end of the active on screen data.
>>> + *
>>> + * VOP_INTR_CTRL0.dsp_line_frag_num = VOP_DSP_VACT_ST_END.dsp_vact_end
>>> + * Interrupts
>>> + * LINE_FLAG -------------------------------+
>>> + * FRAME_SYNC ----+                         |
>>> + *                |                         |
>>> + *                v                         v
>>> + *                | Vsync | Vbp |  Vactive  | Vfp |
>>> + *                        ^     ^           ^     ^
>>> + *                        |     |           |     |
>>> + *                        |     |           |     |
>>> + * dsp_vs_end ------------+     |           |     |   VOP_DSP_VTOTAL_VS_END
>>> + * dsp_vact_start --------------+           |     |   VOP_DSP_VACT_ST_END
>>> + * dsp_vact_end ----------------------------+     |   VOP_DSP_VACT_ST_END
>>> + * dsp_total -------------------------------------+   VOP_DSP_VTOTAL_VS_END
>>> + */
>>> +
>>> +static void vop_line_flag_irq_enable(struct vop *vop, int line_num)
>>> +{
>>> +       unsigned long flags;
>>> +
>>> +       if (WARN_ON(!vop->is_enabled))
>>> +               return;
>>> +
>>> +       spin_lock_irqsave(&vop->irq_lock, flags);
>>> +
>>> +       VOP_CTRL_SET(vop, line_flag_num_0, line_num);
>>> +       VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1);
>>> +       vop_cfg_done(vop);
>>> +
>>> +       spin_unlock_irqrestore(&vop->irq_lock, flags);
>>> +}
>>> +
>>> +static void vop_line_flag_irq_disable(struct vop *vop)
>>> +{
>>> +       unsigned long flags;
>>> +
>>> +       if (WARN_ON(!vop->is_enabled))
>>> +               return;
>>> +
>>> +       spin_lock_irqsave(&vop->irq_lock, flags);
>>> +
>>> +       VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 0);
>>> +
>>> +       spin_unlock_irqrestore(&vop->irq_lock, flags);
>>> +}
>>> +
>>>   static void vop_enable(struct drm_crtc *crtc)
>>>   {
>>>          struct vop *vop = to_vop(crtc);
>>> @@ -1157,6 +1212,13 @@ static irqreturn_t vop_isr(int irq, void *data)
>>>                  ret = IRQ_HANDLED;
>>>          }
>>>
>>> +       if (active_irqs & LINE_FLAG_INTR) {
>>> +               if (!completion_done(&vop->line_flag_completion))
>>> +                       complete(&vop->line_flag_completion);
>> I think there's potential to miss flags here if the timing is just
>> right because the completion_done and complete are not atomic wrt the
>> wait_line_flag function. I think you need some locking around the
>> completion operations to ensure they're properly sequenced.
>>
>> FWIW, if you're reinitializing the completion every time in
>> drm_wait_line_flag, you should be able to just call complete (or
>> complete_all) without checking if there are any waiters.
>>
> I should add, if you just call complete/complete_all() here, you
> shouldn't need to add locking (assuming only one waiter).

Yes, only one waiter.

>>> +               active_irqs &= ~LINE_FLAG_INTR;
>>> +               ret = IRQ_HANDLED;
>>> +       }
>>> +
>>>          if (active_irqs & FS_INTR) {
>>>                  drm_crtc_handle_vblank(crtc);
>>>                  vop_handle_vblank(vop);
>>> @@ -1255,6 +1317,7 @@ static int vop_create_crtc(struct vop *vop)
>>>
>>>          init_completion(&vop->dsp_hold_completion);
>>>          init_completion(&vop->wait_update_complete);
>>> +       init_completion(&vop->line_flag_completion);
>>>          crtc->port = port;
>>>          rockchip_register_crtc_funcs(crtc, &private_crtc_funcs);
>>>
>>> @@ -1411,6 +1474,46 @@ static void vop_win_init(struct vop *vop)
>>>          }
>>>   }
>>>
>>> +/**
>>> + * rockchip_drm_wait_line_flag - acqiure the give line flag event
>>> + * @crtc: CRTC to enable line flag
>>> + * @line_num: interested line number
>>> + * @mstimeout: millisecond for timeout
>>> + *
>>> + * Driver would hold here until the interested line flag interrupt have
>>> + * happened or timeout to wait.
>>> + *
>>> + * Returns:
>>> + * Zero on success, negative errno on failure.
>>> + */
>>> +int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
>>> +                               unsigned int mstimeout)
>>> +{
>>> +       struct vop *vop = to_vop(crtc);
>>> +       unsigned long jiffies_left;
>>> +
>>> +       if (!crtc || !vop->is_enabled)
>>> +               return -ENODEV;
>>> +
>>> +       if (line_num > crtc->mode.vtotal || mstimeout <= 0)
>>> +               return -EINVAL;
>>> +
>>> +       reinit_completion(&vop->line_flag_completion);
>>> +       vop_line_flag_irq_enable(vop, line_num);
>>> +
>> This will only work for one waiter per vop. Is it worth enforcing this
>> explicitly to avoid weird behavior when there are more than one?

Yes, you're right, need to avoid the weird behavior here.

>>
>>> +       jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion,
>>> +                                                  msecs_to_jiffies(mstimeout));
>>> +       vop_line_flag_irq_disable(vop);
>>> +
>>> +       if (jiffies_left == 0) {
>>> +               dev_err(vop->dev, "Timeout waiting for IRQ\n");
>>> +               return -ETIMEDOUT;
>>> +       }
>>> +
>>> +       return 0;
>>> +}
>>> +EXPORT_SYMBOL(rockchip_drm_wait_line_flag);
>>> +
>>>   static int vop_bind(struct device *dev, struct device *master, void *data)
>>>   {
>>>          struct platform_device *pdev = to_platform_device(dev);
>>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>>> index ff4f52e..34fcd03 100644
>>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>>> @@ -61,6 +61,9 @@ struct vop_ctrl {
>>>          struct vop_reg hpost_st_end;
>>>          struct vop_reg vpost_st_end;
>>>
>>> +       struct vop_reg line_flag_num_0;
>>> +       struct vop_reg line_flag_num_1;
>>> +
>> nit: you could make this an array:
>>
>> struct vop_reg line_flag_num[2];

Done,

>>
>>>          struct vop_reg cfg_done;
>>>   };
>>>
>>> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>>> index 6f42e56..e9211c9 100644
>>> --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>>> +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>>> @@ -122,6 +122,7 @@ static const struct vop_ctrl rk3036_ctrl_data = {
>>>          .hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0),
>>>          .vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
>>>          .vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0),
>>> +       .line_flag_num_0 = VOP_REG(RK3036_INT_STATUS, 0xfff, 12),
>>>          .cfg_done = VOP_REG(RK3036_REG_CFG_DONE, 0x1, 0),
>>>   };
>>>
>>> @@ -221,6 +222,7 @@ static const struct vop_ctrl rk3288_ctrl_data = {
>>>          .vact_st_end = VOP_REG(RK3288_DSP_VACT_ST_END, 0x1fff1fff, 0),
>>>          .hpost_st_end = VOP_REG(RK3288_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
>>>          .vpost_st_end = VOP_REG(RK3288_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
>>> +       .line_flag_num_0 = VOP_REG(RK3288_INTR_CTRL0, 0x1fff, 12),
>>>          .cfg_done = VOP_REG(RK3288_REG_CFG_DONE, 0x1, 0),
>>>   };
>>>
>>> @@ -299,6 +301,8 @@ static const struct vop_ctrl rk3399_ctrl_data = {
>>>          .vact_st_end = VOP_REG(RK3399_DSP_VACT_ST_END, 0x1fff1fff, 0),
>>>          .hpost_st_end = VOP_REG(RK3399_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
>>>          .vpost_st_end = VOP_REG(RK3399_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
>>> +       .line_flag_num_0 = VOP_REG(RK3399_LINE_FLAG, 0xffff, 0),
>>> +       .line_flag_num_1 = VOP_REG(RK3399_LINE_FLAG, 0xffff, 16),
>>>          .cfg_done = VOP_REG_MASK(RK3399_REG_CFG_DONE, 0x1, 0),
>>>   };
>>>
>>> --
>>> 1.9.1
>>>
>>>
>
>




From ykk at rock-chips.com  Thu Jul  7 19:32:06 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 8 Jul 2016 10:32:06 +0800
Subject: [PATCH v3 4/4] drm/rockchip: analogix_dp: implement PSR function
In-Reply-To: <CAOw6vbL=GsAL+XDive6g4ettZrdrW6BZ-OkVbPp54E5gxn_HsQ@mail.gmail.com>
References: <1467364685-21390-1-git-send-email-ykk@rock-chips.com>
 <1467364776-21794-1-git-send-email-ykk@rock-chips.com>
 <CAOw6vbL=GsAL+XDive6g4ettZrdrW6BZ-OkVbPp54E5gxn_HsQ@mail.gmail.com>
Message-ID: <577F10A6.6040206@rock-chips.com>

Sean,

On 07/02/2016 04:05 AM, Sean Paul wrote:
> On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk at rock-chips.com> wrote:
>> Alway enable the PSR function for Rockchip analogix_dp driver. If panel
>> don't support PSR, then the core analogix_dp would ignore this setting.
>>
>> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
>> ---
>> Changes in v3:
>> - split the common psr logic into a seperate driver, make this to a
>>    simple sub-psr device driver.
>>
>> Changes in v2:
>> - remove vblank notify out (Daniel)
>> - create a psr_active() callback in vop data struct.
>>
>>   drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 52 +++++++++++++++++++++++++
>>   1 file changed, 52 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
>> index e81e19a..80a60a6 100644
>> --- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
>> +++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
>> @@ -32,6 +32,7 @@
>>   #include <drm/bridge/analogix_dp.h>
>>
>>   #include "rockchip_drm_drv.h"
>> +#include "rockchip_drm_psr.h"
>>   #include "rockchip_drm_vop.h"
>>
>>   #define RK3288_GRF_SOC_CON6            0x25c
>> @@ -68,11 +69,53 @@ struct rockchip_dp_device {
>>          struct regmap            *grf;
>>          struct reset_control     *rst;
>>
>> +       struct delayed_work      psr_work;
>> +       unsigned int             psr_state;
>> +
>>          const struct rockchip_dp_chip_data *data;
>>
>>          struct analogix_dp_plat_data plat_data;
>>   };
>>
>> +static int analogix_dp_psr_set(struct drm_encoder *encoder, bool enabled)
> Again, this function doesn't fail, but its return type is int.
> Fortunately you don't check the return in rockchip_drm_psr.c, so this
> also seems like a good void candidate.

Okay, done.

>> +{
>> +       struct rockchip_dp_device *dp = to_dp(encoder);
>> +
>> +       dev_dbg(dp->dev, "%s PSR...\n", enabled ? "Entry" : "Exit");
>> +
>> +       if (enabled)
>> +               dp->psr_state = EDP_VSC_PSR_STATE_ACTIVE;
>> +       else
>> +               dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE;
>> +
>> +       schedule_delayed_work(&dp->psr_work, msecs_to_jiffies(10));
> Pull 10 out into a #define

Done

>> +
>> +       return 0;
>> +}
>> +
>> +static void analogix_dp_psr_work(struct work_struct *work)
>> +{
>> +       struct rockchip_dp_device *dp =
>> +                               container_of(work, typeof(*dp), psr_work.work);
>> +       struct drm_crtc *crtc = dp->encoder.crtc;
>> +       int psr_state = dp->psr_state;
>> +       int vact_end;
>> +       int ret;
>> +
>> +       if (!crtc)
>> +               return;
>> +
>> +       vact_end = crtc->mode.vtotal - crtc->mode.vsync_start + crtc->mode.vdisplay;
>> +
>> +       ret = rockchip_drm_wait_line_flag(dp->encoder.crtc, vact_end, 100);
>
> Pull 100 out into a #define
>

Done.

>> +       if (ret == 0) {
> if (ret) {
>      dev_err(... "line flag interrupt did not arrive");
>      return;
> }

Done.

Thanks
- Yakir

>> +               if (psr_state == EDP_VSC_PSR_STATE_ACTIVE)
>> +                       analogix_dp_active_psr(dp->dev);
>> +               else
>> +                       analogix_dp_inactive_psr(dp->dev);
>> +       }
>> +}
>> +
>>   static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
>>   {
>>          reset_control_assert(dp->rst);
>> @@ -340,12 +383,21 @@ static int rockchip_dp_bind(struct device *dev, struct device *master,
>>          dp->plat_data.power_off = rockchip_dp_powerdown;
>>          dp->plat_data.get_modes = rockchip_dp_get_modes;
>>
>> +       dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE;
>> +       INIT_DELAYED_WORK(&dp->psr_work, analogix_dp_psr_work);
>> +
>> +       rockchip_drm_psr_register(&dp->encoder, analogix_dp_psr_set);
>> +
>>          return analogix_dp_bind(dev, dp->drm_dev, &dp->plat_data);
>>   }
>>
>>   static void rockchip_dp_unbind(struct device *dev, struct device *master,
>>                                 void *data)
>>   {
>> +       struct rockchip_dp_device *dp = dev_get_drvdata(dev);
>> +
>> +       rockchip_drm_psr_unregister(&dp->encoder);
>> +
>>          return analogix_dp_unbind(dev, master, data);
>>   }
>>
>> --
>> 1.9.1
>>
>>
>
>




From ykk at rock-chips.com  Thu Jul  7 19:39:14 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 8 Jul 2016 10:39:14 +0800
Subject: [PATCH v3 3/4] drm/bridge: analogix_dp: add the PSR function
 support
In-Reply-To: <577F0F53.9060404@rock-chips.com>
References: <1467364685-21390-1-git-send-email-ykk@rock-chips.com>
 <1467364768-21741-1-git-send-email-ykk@rock-chips.com>
 <CAOw6vbJ4H9xzeaA-2OKsG+pGwdvehS91TZDPfUyq8R-=e70bPQ@mail.gmail.com>
 <577F0F53.9060404@rock-chips.com>
Message-ID: <577F1252.9090000@rock-chips.com>



On 07/08/2016 10:26 AM, Yakir Yang wrote:
> Sean,
>
> Thanks for your review.
>
> On 07/02/2016 03:46 AM, Sean Paul wrote:
>> On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk at rock-chips.com> wrote:
>>> The full name of PSR is Panel Self Refresh, panel device could refresh
>>> itself with the hardware framebuffer in panel, this would make lots of
>>> sense to save the power consumption.
>>>
>>> This patch have exported two symbols for platform driver to implement
>>> the PSR function in hardware side:
>>> - analogix_dp_active_psr()
>>> - analogix_dp_inactive_psr()
>>>
>>> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
>>> ---
>>> Changes in v3:
>>> - split analogix_dp_enable_psr(), make it more clearly
>>>      analogix_dp_detect_sink_psr()
>>>      analogix_dp_enable_sink_psr()
>>> - remove some nosie register setting comments
>>>
>>> Changes in v2:
>>> - introduce in v2, splite the common Analogix DP changes out
>>>
>>>   drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 64 
>>> ++++++++++++++++++++++
>>>   drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |  4 ++
>>>   drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  | 54 
>>> ++++++++++++++++++
>>>   drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h  | 28 ++++++++++
>>>   include/drm/bridge/analogix_dp.h                   |  3 +
>>>   5 files changed, 153 insertions(+)
>>>
>>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c 
>>> b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>>> index 32715da..b557097 100644
>>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>>> @@ -97,6 +97,66 @@ static int analogix_dp_detect_hpd(struct 
>>> analogix_dp_device *dp)
>>>          return 0;
>>>   }
>>>
>>> +int analogix_dp_active_psr(struct device *dev)
>>> +{
>>> +       struct analogix_dp_device *dp = dev_get_drvdata(dev);
>>> +
>>> +       if (!dp->psr_support)
>>> +               return -EINVAL;
>>> +
>>> +       analogix_dp_send_psr_spd(dp, EDP_VSC_PSR_STATE_ACTIVE |
>>> + EDP_VSC_PSR_CRC_VALUES_VALID);
>>> +       return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(analogix_dp_active_psr);
>>> +
>>> +int analogix_dp_inactive_psr(struct device *dev)
>>> +{
>>> +       struct analogix_dp_device *dp = dev_get_drvdata(dev);
>>> +
>>> +       if (!dp->psr_support)
>>> +               return -EINVAL;
>>> +
>>> +       analogix_dp_send_psr_spd(dp, 0);
>>> +       return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(analogix_dp_inactive_psr);
>>> +
>>> +static bool analogix_dp_detect_sink_psr(struct analogix_dp_device *dp)
>>> +{
>>> +       unsigned char psr_version;
>>> +
>>> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_SUPPORT, 
>>> &psr_version);
>>> +       dev_info(dp->dev, "Panel PSR version : %x\n", psr_version);
>>> +
>> This info message is likely to be spammy since it's printed everytime
>> the panel toggle on. Perhaps downgrade to debug level.
>
> Okay, done.
>
>>> +       return (psr_version & DP_PSR_IS_SUPPORTED) ? true : false;
>>> +}
>>> +
>>> +static int analogix_dp_enable_sink_psr(struct analogix_dp_device *dp)
>> Return type is int, but the function never fails and you don't check
>> the return value when calling it. Seems like this should be void.
>
> Done.
>
>>> +{
>>> +       unsigned char psr_en;
>>> +
>>> +       /* Disable psr function */
>>> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
>>> +       psr_en &= ~DP_PSR_ENABLE;
>>> +       analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
>>> +
>>> +       /* Main-Link transmitter remains active during PSR active 
>>> states */
>>> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
>>> +       psr_en = DP_PSR_MAIN_LINK_ACTIVE | DP_PSR_CRC_VERIFICATION;
>> Why read psr_en if you're just going to overwrite it? Perhaps you 
>> meant |= here.
>>
>
> Yes, it's my mistaken, no need to read the DP_PSR_EN_CFG, just 
> configure it directly is enough.
>
>>> + analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
>>> +
>>> +       /* Enable psr function */
>>> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
>>> +       psr_en = DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE |
>>> +                DP_PSR_CRC_VERIFICATION;
>> Again, no need to read if you're just overwriting.
>
> Yes, ditto
>
>>> + analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
>>> +
>>> +       analogix_dp_enable_psr_crc(dp);
>>> +
>>> +       return 0;
>>> +}
>>> +
>>>   static unsigned char analogix_dp_calc_edid_check_sum(unsigned char 
>>> *edid_data)
>>>   {
>>>          int i;
>>> @@ -921,6 +981,10 @@ static void analogix_dp_commit(struct 
>>> analogix_dp_device *dp)
>>>
>>>          /* Enable video */
>>>          analogix_dp_start_video(dp);
>>> +
>>> +       dp->psr_support = analogix_dp_detect_sink_psr(dp);
>>> +       if (dp->psr_support)
>>> +               analogix_dp_enable_sink_psr(dp);
>>>   }
>>>
>>>   int analogix_dp_get_modes(struct drm_connector *connector)
>>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h 
>>> b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
>>> index b456380..6ca5dde 100644
>>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
>>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
>>> @@ -177,6 +177,7 @@ struct analogix_dp_device {
>>>          int                     hpd_gpio;
>>>          bool                    force_hpd;
>>>          unsigned char           edid[EDID_BLOCK_LENGTH * 2];
>>> +       bool                    psr_support;
>>>
>>>          struct analogix_dp_plat_data *plat_data;
>>>   };
>>> @@ -278,4 +279,7 @@ int analogix_dp_is_video_stream_on(struct 
>>> analogix_dp_device *dp);
>>>   void analogix_dp_config_video_slave_mode(struct analogix_dp_device 
>>> *dp);
>>>   void analogix_dp_enable_scrambling(struct analogix_dp_device *dp);
>>>   void analogix_dp_disable_scrambling(struct analogix_dp_device *dp);
>>> +void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp);
>>> +void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, int db1);
>>> +
>>>   #endif /* _ANALOGIX_DP_CORE_H */
>>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c 
>>> b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
>>> index 48030f0..e8372c7 100644
>>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
>>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
>>> @@ -1322,3 +1322,57 @@ void analogix_dp_disable_scrambling(struct 
>>> analogix_dp_device *dp)
>>>          reg |= SCRAMBLING_DISABLE;
>>>          writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
>>>   }
>>> +
>>> +void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp)
>>> +{
>>> +       writel(PSR_VID_CRC_FLUSH | PSR_VID_CRC_ENABLE,
>>> +              dp->reg_base + ANALOGIX_DP_CRC_CON);
>>> +
>>> +       usleep_range(10, 20);
>> Is this sleep arbitrary, or documented somewhere? Could you add a
>> comment explaining how this was arrived at?
>>
>
> Yes, this sleep is arbitrary, there is no document about the

Oh, implement reply. I double check the datesheet, oh, i make a mistaken 
here. The CRC_FLUSH wasn't a trigger, but a enable gate.

 >PSR Video CRC flush enable. The PSR video CRC value is initialized at 
every v-sync leading edge.

>>>
>>> +       writel(PSR_VID_CRC_ENABLE, dp->reg_base + ANALOGIX_DP_CRC_CON);

So I need to delete this register setting.

Thanks,
- Yakir

>>> +}
>>> +
>>> +void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, int db1)
>>> +{
>>> +       unsigned int val;
>>> +
>>> +       /* don't send info frame */
>>> +       val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>>> +       val &= ~IF_EN;
>>> +       writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>>> +
>>> +       /* configure single frame update mode */
>>> +       writel(0x3, dp->reg_base + ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL);
>>> +
>>> +       /* configure VSC HB0 ~ HB3 */
>>> +       writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_HB0);
>>> +       writel(0x07, dp->reg_base + ANALOGIX_DP_SPD_HB1);
>>> +       writel(0x02, dp->reg_base + ANALOGIX_DP_SPD_HB2);
>>> +       writel(0x08, dp->reg_base + ANALOGIX_DP_SPD_HB3);
>>> +
>>> +       /* configure VSC HB0 ~ HB3 */
>>> +       writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_PB0);
>>> +       writel(0x16, dp->reg_base + ANALOGIX_DP_SPD_PB1);
>>> +       writel(0xCE, dp->reg_base + ANALOGIX_DP_SPD_PB2);
>>> +       writel(0x5D, dp->reg_base + ANALOGIX_DP_SPD_PB3);
>>> +
>>> +       /* configure DB0 / DB1 values */
>>> +       writel(0x00, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB0);
>> Lots of hardcoded values here. I think this could be cleaned up.
>
> For "HB0~HB3", "PB0~PB3" and "DB1", I don't understand very well. 
> Those seems to be a kind of head number, I got those magic values from 
> our IC side. So I think those should be okay to keep the hardcode 
> values here :-D
>
> But for the "0x3" in "ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL", I would fix 
> it with suitable macro.
>
>>> +       writel(db1, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB1);
>>> +
>>> +       /* set reuse spd inforframe */
>>> +       val = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
>>> +       val |= REUSE_SPD_EN;
>>> +       writel(val, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
>>> +
>>> +       /* mark info frame update */
>>> +       val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>>> +       val = (val | IF_UP) & ~IF_EN;
>>> +       writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>>> +
>>> +       /* send info frame */
>>> +       val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>>> +       val |= IF_EN;
>>> +       writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>>> +}
>>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h 
>>> b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
>>> index cdcc6c5..a2698e4 100644
>>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
>>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
>>> @@ -22,6 +22,8 @@
>>>   #define ANALOGIX_DP_VIDEO_CTL_8                        0x3C
>>>   #define ANALOGIX_DP_VIDEO_CTL_10               0x44
>>>
>>> +#define ANALOGIX_DP_SPDIF_AUDIO_CTL_0          0xD8
>>> +
>>>   #define ANALOGIX_DP_PLL_REG_1                  0xfc
>>>   #define ANALOGIX_DP_PLL_REG_2                  0x9e4
>>>   #define ANALOGIX_DP_PLL_REG_3                  0x9e8
>>> @@ -30,6 +32,21 @@
>>>
>>>   #define ANALOGIX_DP_PD                         0x12c
>>>
>>> +#define ANALOGIX_DP_IF_TYPE                    0x244
>>> +#define ANALOGIX_DP_IF_PKT_DB1                 0x254
>>> +#define ANALOGIX_DP_IF_PKT_DB2                 0x258
>>> +#define ANALOGIX_DP_SPD_HB0                    0x2F8
>>> +#define ANALOGIX_DP_SPD_HB1                    0x2FC
>>> +#define ANALOGIX_DP_SPD_HB2                    0x300
>>> +#define ANALOGIX_DP_SPD_HB3                    0x304
>>> +#define ANALOGIX_DP_SPD_PB0                    0x308
>>> +#define ANALOGIX_DP_SPD_PB1                    0x30C
>>> +#define ANALOGIX_DP_SPD_PB2                    0x310
>>> +#define ANALOGIX_DP_SPD_PB3                    0x314
>>> +#define ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL      0x318
>>> +#define ANALOGIX_DP_VSC_SHADOW_DB0             0x31C
>>> +#define ANALOGIX_DP_VSC_SHADOW_DB1             0x320
>>> +
>>>   #define ANALOGIX_DP_LANE_MAP                   0x35C
>>>
>>>   #define ANALOGIX_DP_ANALOG_CTL_1               0x370
>>> @@ -103,6 +120,8 @@
>>>
>>>   #define ANALOGIX_DP_SOC_GENERAL_CTL            0x800
>>>
>>> +#define ANALOGIX_DP_CRC_CON                    0x890
>>> +
>>>   /* ANALOGIX_DP_TX_SW_RESET */
>>>   #define RESET_DP_TX                            (0x1 << 0)
>>>
>>> @@ -151,6 +170,7 @@
>>>   #define VID_CHK_UPDATE_TYPE_SHIFT              (4)
>>>   #define VID_CHK_UPDATE_TYPE_1                  (0x1 << 4)
>>>   #define VID_CHK_UPDATE_TYPE_0                  (0x0 << 4)
>>> +#define REUSE_SPD_EN                           (0x1 << 3)
>>>
>>>   /* ANALOGIX_DP_VIDEO_CTL_8 */
>>>   #define VID_HRES_TH(x)                         (((x) & 0xf) << 4)
>>> @@ -376,4 +396,12 @@
>>>   #define VIDEO_MODE_SLAVE_MODE                  (0x1 << 0)
>>>   #define VIDEO_MODE_MASTER_MODE                 (0x0 << 0)
>>>
>>> +/* ANALOGIX_DP_PKT_SEND_CTL */
>>> +#define IF_UP                                  (0x1 << 4)
>>> +#define IF_EN                                  (0x1 << 0)
>>> +
>>> +/* ANALOGIX_DP_CRC_CON */
>>> +#define PSR_VID_CRC_FLUSH                      (0x1 << 2)
>>> +#define PSR_VID_CRC_ENABLE                     (0x1 << 0)
>>> +
>>>   #endif /* _ANALOGIX_DP_REG_H */
>>> diff --git a/include/drm/bridge/analogix_dp.h 
>>> b/include/drm/bridge/analogix_dp.h
>>> index 261b86d..183a336 100644
>>> --- a/include/drm/bridge/analogix_dp.h
>>> +++ b/include/drm/bridge/analogix_dp.h
>>> @@ -38,6 +38,9 @@ struct analogix_dp_plat_data {
>>>                           struct drm_connector *);
>>>   };
>>>
>>> +int analogix_dp_active_psr(struct device *dev);
>>> +int analogix_dp_inactive_psr(struct device *dev);
>> Why active/inactive instead of enable/disable, which is used 
>> everywhere else?
>
> Done
>
> Thanks,
> - Yakir
>
>>> +
>>>   int analogix_dp_resume(struct device *dev);
>>>   int analogix_dp_suspend(struct device *dev);
>>>
>>> -- 
>>> 1.9.1
>>>
>>>
>>
>>
>




From broonie at kernel.org  Fri Jul  8 01:53:38 2016
From: broonie at kernel.org (Mark Brown)
Date: Fri, 8 Jul 2016 10:53:38 +0200
Subject: [PATCH v2] regulator: pwm: Fix regulator ramp delay for
 continuous mode
In-Reply-To: <364098370f214216ad14eda6487687e9@HQMAIL106.nvidia.com>
References: <1467830521-15300-1-git-send-email-dianders@chromium.org>
 <577E1487.9010000@nvidia.com>
 <CAD=FV=UQw7ttMECtO0aomx56G6xGWZh96NRug6Y4QuPJfXq8Tw@mail.gmail.com>
 <47095a54f7b44f8a98bdfe05cdea7865@HQMAIL106.nvidia.com>
 <CAD=FV=UZrpSsOAxsuEWuMeie3NHQUY3RQyaUF0MK9mHDqPhRzQ@mail.gmail.com>
 <364098370f214216ad14eda6487687e9@HQMAIL106.nvidia.com>
Message-ID: <20160708085338.GT6247@sirena.org.uk>

On Thu, Jul 07, 2016 at 06:43:33PM +0000, Aleksandr Frid wrote:

I'm not entirely sure what's wrong with your mail client here but your
mails are essentially illegible.  There appears to be some combination
of top posting, reflowing quoted content to remove line breaks and extra
levels of quoting.

> >>
> In the case that you don't need multiple steps then don't specify a regulator-ramp-delay, just a pwm-regulator-settle-us.
> >>
> Agreed
> 
> -----Original Message-----
> From: dianders at google.com [mailto:dianders at google.com] On Behalf Of Doug Anderson
> Sent: Thursday, July 07, 2016 11:32 AM
> To: Aleksandr Frid
> Cc: Laxman Dewangan; Mark Brown; Boris Brezillon; Lee Jones; Brian Norris; open list:ARM/Rockchip SoC...; Heiko Stuebner; Thierry Reding; Liam Girdwood; linux-kernel at vger.kernel.org
> Subject: Re: [PATCH v2] regulator: pwm: Fix regulator ramp delay for continuous mode
> 
> Hi,
> 
> On Thu, Jul 7, 2016 at 11:23 AM, Aleksandr Frid <afrid at nvidia.com> wrote:
> > Hi,
> >
> >>>
> > In that case we should probably add a new PWM regulator property and not abuse the existing one.  Maybe you use "pwm-regulator-settle-us"
> > or something?
> >>>
> > Looks reasonable to me.
> >
> >>>
> > actually the right thing is probably to implement 
> > 'regulator-ramp-delay' as doing several small steps in that case
> >>>
> > Ramp delay uV/us  is not a "real" metric for some PWM regulators with exponential transition -- as opposite to fixed slew-rate linear transition on other regulators. So splitting transition into multiple steps to implement artificial  (in this case) metric seems questionable.
> 
> In the case that you don't need multiple steps then don't specify a regulator-ramp-delay, just a pwm-regulator-settle-us.
> 
> ...the suggestion for multiple steps is because (so I'm told) it helps avoid overshoot or undershoot problems.  In general the whole point of ramping a regulator slowly is to avoid overshoot or undershoot problems.  The "regulator-ramp-delay" property in Linux is a little odd because it sort of "describes" the ramp delay and sort of "sets"
> the ramp delay.  Many PMICs allow you to set how fast the regulator will ramp and this property is used to specify how the register in the PMIC should be set.  However, it is also used as the actual delay in Linux.
> 
> 
> -Doug
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160708/581f45c6/attachment-0001.sig>

From zyw at rock-chips.com  Fri Jul  8 02:04:54 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Fri,  8 Jul 2016 17:04:54 +0800
Subject: [PATCH 0/7] Rockchip dw-mipi-dsi driver
Message-ID: <1467968701-15620-1-git-send-email-zyw@rock-chips.com>


Hi all

This is a bunch of dw-mipi-dsi driver for RK3399 and RK3288, they have
been tested on rk3399 and rk3288 evb board.

This series is based on Mark Yao's branch:
https://github.com/markyzq/kernel-drm-rockchip/tree/drm-rockchip-next-2016-05-23



Chris Zhong (7):
  dt-bindings: add rk3399 support for dw-mipi-rockchip
  DRM: mipi: support rk3399 mipi dsi
  dt-bindings: add power domain node for dw-mipi-rockchip
  drm/rockchip: dw-mipi: add dw-mipi power domain support
  drm/rockchip: dw-mipi: support HPD poll
  drm/rockchip: dw-mipi: fix phy clk lane stop state timeout
  drm/rockchip: dw-mipi: fix insufficient bandwidth of some panel

 .../display/rockchip/dw_mipi_dsi_rockchip.txt      |   6 +
 drivers/gpu/drm/rockchip/dw-mipi-dsi.c             | 135 +++++++++++++++++----
 2 files changed, 120 insertions(+), 21 deletions(-)

-- 
2.6.3



From zyw at rock-chips.com  Fri Jul  8 02:04:55 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Fri,  8 Jul 2016 17:04:55 +0800
Subject: [PATCH 1/7] dt-bindings: add rk3399 support for dw-mipi-rockchip
In-Reply-To: <1467968701-15620-1-git-send-email-zyw@rock-chips.com>
References: <1467968701-15620-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1467968701-15620-2-git-send-email-zyw@rock-chips.com>

The dw-mipi-dsi of rk3399 is almost the same as rk3288, the rk3399 has
additional phy config clock.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
---

 .../devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt    | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt
index 1753f0c..4d59df3 100644
--- a/Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt
+++ b/Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt
@@ -5,6 +5,7 @@ Required properties:
 - #address-cells: Should be <1>.
 - #size-cells: Should be <0>.
 - compatible: "rockchip,rk3288-mipi-dsi", "snps,dw-mipi-dsi".
+	      "rockchip,rk3399-mipi-dsi", "snps,dw-mipi-dsi".
 - reg: Represent the physical address range of the controller.
 - interrupts: Represent the controller's interrupt to the CPU(s).
 - clocks, clock-names: Phandles to the controller's pll reference
@@ -13,6 +14,10 @@ Required properties:
 - ports: contain a port node with endpoint definitions as defined in [2].
   For vopb,set the reg = <0> and set the reg = <1> for vopl.
 
+Optional properties:
+- clocks, clock-names: phandle to the dw-mipi phy clock, name should be
+		       "phy_cfg".
+
 [1] Documentation/devicetree/bindings/clock/clock-bindings.txt
 [2] Documentation/devicetree/bindings/media/video-interfaces.txt
 
-- 
2.6.3



From zyw at rock-chips.com  Fri Jul  8 02:04:56 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Fri,  8 Jul 2016 17:04:56 +0800
Subject: [PATCH 2/7] DRM: mipi: support rk3399 mipi dsi
In-Reply-To: <1467968701-15620-1-git-send-email-zyw@rock-chips.com>
References: <1467968701-15620-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1467968701-15620-3-git-send-email-zyw@rock-chips.com>

The vopb/vopl switch register of rk3399 mipi is different from rk3288,
the default setting for mipi dsi mode is different too, so add a
of_device_id structure to distinguish them, and make sure set the
correct mode before mipi phy init.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
Signed-off-by: Mark Yao <mark.yao at rock-chips.com>
---

 drivers/gpu/drm/rockchip/dw-mipi-dsi.c | 70 ++++++++++++++++++++++++++++------
 1 file changed, 59 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
index dedc65b..100da01 100644
--- a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
+++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
@@ -28,9 +28,17 @@
 
 #define DRIVER_NAME    "dw-mipi-dsi"
 
-#define GRF_SOC_CON6                    0x025c
-#define DSI0_SEL_VOP_LIT                (1 << 6)
-#define DSI1_SEL_VOP_LIT                (1 << 9)
+#define RK3288_GRF_SOC_CON6		0x025c
+#define RK3288_DSI0_SEL_VOP_LIT		BIT(6)
+#define RK3288_DSI1_SEL_VOP_LIT		BIT(9)
+
+#define RK3399_GRF_SOC_CON19		0x6250
+#define RK3399_DSI0_SEL_VOP_LIT		BIT(0)
+#define RK3399_DSI1_SEL_VOP_LIT		BIT(4)
+
+/* disable turnrequest, turndisable, forcetxstopmode, forcerxmode */
+#define RK3399_GRF_SOC_CON22		0x6258
+#define RK3399_GRF_DSI_MODE		0xffff0000
 
 #define DSI_VERSION			0x00
 #define DSI_PWR_UP			0x04
@@ -147,7 +155,6 @@
 #define LPRX_TO_CNT(p)			((p) & 0xffff)
 
 #define DSI_BTA_TO_CNT			0x8c
-
 #define DSI_LPCLK_CTRL			0x94
 #define AUTO_CLKLANE_CTRL		BIT(1)
 #define PHY_TXREQUESTCLKHS		BIT(0)
@@ -263,6 +270,11 @@ enum {
 };
 
 struct dw_mipi_dsi_plat_data {
+	u32 dsi0_en_bit;
+	u32 dsi1_en_bit;
+	u32 grf_switch_reg;
+	u32 grf_dsi0_mode;
+	u32 grf_dsi0_mode_reg;
 	unsigned int max_data_lanes;
 	enum drm_mode_status (*mode_valid)(struct drm_connector *connector,
 					   struct drm_display_mode *mode);
@@ -279,6 +291,7 @@ struct dw_mipi_dsi {
 
 	struct clk *pllref_clk;
 	struct clk *pclk;
+	struct clk *phy_cfg_clk;
 
 	unsigned int lane_mbps; /* per lane */
 	u32 channel;
@@ -400,6 +413,14 @@ static int dw_mipi_dsi_phy_init(struct dw_mipi_dsi *dsi)
 
 	dsi_write(dsi, DSI_PWR_UP, POWERUP);
 
+	if (!IS_ERR(dsi->phy_cfg_clk)) {
+		ret = clk_prepare_enable(dsi->phy_cfg_clk);
+		if (ret) {
+			dev_err(dsi->dev, "Failed to enable phy_cfg_clk\n");
+			return ret;
+		}
+	}
+
 	dw_mipi_dsi_phy_write(dsi, 0x10, BYPASS_VCO_RANGE |
 					 VCO_RANGE_CON_SEL(vco) |
 					 VCO_IN_CAP_CON_LOW |
@@ -444,17 +465,19 @@ static int dw_mipi_dsi_phy_init(struct dw_mipi_dsi *dsi)
 				 val, val & LOCK, 1000, PHY_STATUS_TIMEOUT_US);
 	if (ret < 0) {
 		dev_err(dsi->dev, "failed to wait for phy lock state\n");
-		return ret;
+		goto phy_init_end;
 	}
 
 	ret = readx_poll_timeout(readl, dsi->base + DSI_PHY_STATUS,
 				 val, val & STOP_STATE_CLK_LANE, 1000,
 				 PHY_STATUS_TIMEOUT_US);
-	if (ret < 0) {
+	if (ret < 0)
 		dev_err(dsi->dev,
 			"failed to wait for phy clk lane stop state\n");
-		return ret;
-	}
+
+phy_init_end:
+	if (!IS_ERR(dsi->phy_cfg_clk))
+		clk_disable_unprepare(dsi->phy_cfg_clk);
 
 	return ret;
 }
@@ -878,6 +901,7 @@ static void dw_mipi_dsi_encoder_disable(struct drm_encoder *encoder)
 static void dw_mipi_dsi_encoder_commit(struct drm_encoder *encoder)
 {
 	struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
+	const struct dw_mipi_dsi_plat_data *pdata = dsi->pdata;
 	int mux = drm_of_encoder_active_endpoint_id(dsi->dev->of_node, encoder);
 	u32 val;
 
@@ -886,6 +910,10 @@ static void dw_mipi_dsi_encoder_commit(struct drm_encoder *encoder)
 		return;
 	}
 
+	if (pdata->grf_dsi0_mode_reg)
+		regmap_write(dsi->grf_regmap, pdata->grf_dsi0_mode_reg,
+			     pdata->grf_dsi0_mode);
+
 	dw_mipi_dsi_phy_init(dsi);
 	dw_mipi_dsi_wait_for_two_frames(dsi);
 
@@ -895,11 +923,11 @@ static void dw_mipi_dsi_encoder_commit(struct drm_encoder *encoder)
 	clk_disable_unprepare(dsi->pclk);
 
 	if (mux)
-		val = DSI0_SEL_VOP_LIT | (DSI0_SEL_VOP_LIT << 16);
+		val = pdata->dsi0_en_bit | (pdata->dsi0_en_bit << 16);
 	else
-		val = DSI0_SEL_VOP_LIT << 16;
+		val = pdata->dsi0_en_bit << 16;
 
-	regmap_write(dsi->grf_regmap, GRF_SOC_CON6, val);
+	regmap_write(dsi->grf_regmap, pdata->grf_switch_reg, val);
 	dev_dbg(dsi->dev, "vop %s output to dsi0\n", (mux) ? "LIT" : "BIG");
 }
 
@@ -1075,6 +1103,19 @@ static enum drm_mode_status rk3288_mipi_dsi_mode_valid(
 }
 
 static struct dw_mipi_dsi_plat_data rk3288_mipi_dsi_drv_data = {
+	.dsi0_en_bit = RK3288_DSI0_SEL_VOP_LIT,
+	.dsi1_en_bit = RK3288_DSI1_SEL_VOP_LIT,
+	.grf_switch_reg = RK3288_GRF_SOC_CON6,
+	.max_data_lanes = 4,
+	.mode_valid = rk3288_mipi_dsi_mode_valid,
+};
+
+static struct dw_mipi_dsi_plat_data rk3399_mipi_dsi_drv_data = {
+	.dsi0_en_bit = RK3399_DSI0_SEL_VOP_LIT,
+	.dsi1_en_bit = RK3399_DSI1_SEL_VOP_LIT,
+	.grf_switch_reg = RK3399_GRF_SOC_CON19,
+	.grf_dsi0_mode = RK3399_GRF_DSI_MODE,
+	.grf_dsi0_mode_reg = RK3399_GRF_SOC_CON22,
 	.max_data_lanes = 4,
 	.mode_valid = rk3288_mipi_dsi_mode_valid,
 };
@@ -1083,6 +1124,9 @@ static const struct of_device_id dw_mipi_dsi_dt_ids[] = {
 	{
 	 .compatible = "rockchip,rk3288-mipi-dsi",
 	 .data = &rk3288_mipi_dsi_drv_data,
+	}, {
+	 .compatible = "rockchip,rk3399-mipi-dsi",
+	 .data = &rk3399_mipi_dsi_drv_data,
 	},
 	{ /* sentinel */ }
 };
@@ -1133,6 +1177,10 @@ static int dw_mipi_dsi_bind(struct device *dev, struct device *master,
 		return ret;
 	}
 
+	dsi->phy_cfg_clk = devm_clk_get(dev, "phy_cfg");
+	if (IS_ERR(dsi->phy_cfg_clk))
+		dev_dbg(dev, "have not phy_cfg_clk\n");
+
 	ret = clk_prepare_enable(dsi->pllref_clk);
 	if (ret) {
 		dev_err(dev, "%s: Failed to enable pllref_clk\n", __func__);
-- 
2.6.3



From zyw at rock-chips.com  Fri Jul  8 02:04:57 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Fri,  8 Jul 2016 17:04:57 +0800
Subject: [PATCH 3/7] dt-bindings: add power domain node for dw-mipi-rockchip
In-Reply-To: <1467968701-15620-1-git-send-email-zyw@rock-chips.com>
References: <1467968701-15620-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1467968701-15620-4-git-send-email-zyw@rock-chips.com>

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
---

 .../devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt        | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt
index 4d59df3..e433ba5 100644
--- a/Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt
+++ b/Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt
@@ -17,6 +17,7 @@ Required properties:
 Optional properties:
 - clocks, clock-names: phandle to the dw-mipi phy clock, name should be
 		       "phy_cfg".
+- power-domains: a phandle to mipi dsi power domain node.
 
 [1] Documentation/devicetree/bindings/clock/clock-bindings.txt
 [2] Documentation/devicetree/bindings/media/video-interfaces.txt
-- 
2.6.3



From zyw at rock-chips.com  Fri Jul  8 02:04:58 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Fri,  8 Jul 2016 17:04:58 +0800
Subject: [PATCH 4/7] drm/rockchip: dw-mipi: add dw-mipi power domain support
In-Reply-To: <1467968701-15620-1-git-send-email-zyw@rock-chips.com>
References: <1467968701-15620-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1467968701-15620-5-git-send-email-zyw@rock-chips.com>

Reference the power domain incase dw-mipi power down when
in use.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
---

 drivers/gpu/drm/rockchip/dw-mipi-dsi.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
index 100da01..15ba796 100644
--- a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
+++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
@@ -12,6 +12,7 @@
 #include <linux/math64.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
+#include <linux/pm_runtime.h>
 #include <linux/regmap.h>
 #include <linux/mfd/syscon.h>
 #include <drm/drm_atomic_helper.h>
@@ -293,6 +294,7 @@ struct dw_mipi_dsi {
 	struct clk *pclk;
 	struct clk *phy_cfg_clk;
 
+	int dpms_mode;
 	unsigned int lane_mbps; /* per lane */
 	u32 channel;
 	u32 lanes;
@@ -844,6 +846,11 @@ static void dw_mipi_dsi_encoder_mode_set(struct drm_encoder *encoder,
 	struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
 	int ret;
 
+	if (dsi->dpms_mode == DRM_MODE_DPMS_ON)
+		return;
+
+	pm_runtime_get_sync(dsi->dev);
+
 	dsi->mode = adjusted_mode;
 
 	ret = dw_mipi_dsi_get_lane_bps(dsi);
@@ -876,6 +883,9 @@ static void dw_mipi_dsi_encoder_disable(struct drm_encoder *encoder)
 {
 	struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder);
 
+	if (dsi->dpms_mode != DRM_MODE_DPMS_ON)
+		return;
+
 	drm_panel_disable(dsi->panel);
 
 	if (clk_prepare_enable(dsi->pclk)) {
@@ -896,6 +906,8 @@ static void dw_mipi_dsi_encoder_disable(struct drm_encoder *encoder)
 	dw_mipi_dsi_set_mode(dsi, DW_MIPI_DSI_CMD_MODE);
 	dw_mipi_dsi_disable(dsi);
 	clk_disable_unprepare(dsi->pclk);
+	pm_runtime_put(dsi->dev);
+	dsi->dpms_mode = DRM_MODE_DPMS_OFF;
 }
 
 static void dw_mipi_dsi_encoder_commit(struct drm_encoder *encoder)
@@ -929,6 +941,7 @@ static void dw_mipi_dsi_encoder_commit(struct drm_encoder *encoder)
 
 	regmap_write(dsi->grf_regmap, pdata->grf_switch_reg, val);
 	dev_dbg(dsi->dev, "vop %s output to dsi0\n", (mux) ? "LIT" : "BIG");
+	dsi->dpms_mode = DRM_MODE_DPMS_ON;
 }
 
 static int
@@ -1150,6 +1163,7 @@ static int dw_mipi_dsi_bind(struct device *dev, struct device *master,
 
 	dsi->dev = dev;
 	dsi->pdata = pdata;
+	dsi->dpms_mode = DRM_MODE_DPMS_OFF;
 
 	ret = rockchip_mipi_parse_dt(dsi);
 	if (ret)
@@ -1195,6 +1209,8 @@ static int dw_mipi_dsi_bind(struct device *dev, struct device *master,
 
 	dev_set_drvdata(dev, dsi);
 
+	pm_runtime_enable(dev);
+
 	dsi->dsi_host.ops = &dw_mipi_dsi_host_ops;
 	dsi->dsi_host.dev = dev;
 	return mipi_dsi_host_register(&dsi->dsi_host);
@@ -1210,6 +1226,7 @@ static void dw_mipi_dsi_unbind(struct device *dev, struct device *master,
 	struct dw_mipi_dsi *dsi = dev_get_drvdata(dev);
 
 	mipi_dsi_host_unregister(&dsi->dsi_host);
+	pm_runtime_disable(dev);
 	clk_disable_unprepare(dsi->pllref_clk);
 }
 
-- 
2.6.3



From zyw at rock-chips.com  Fri Jul  8 02:05:00 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Fri,  8 Jul 2016 17:05:00 +0800
Subject: [PATCH 6/7] drm/rockchip: dw-mipi: fix phy clk lane stop state timeout
In-Reply-To: <1467968701-15620-1-git-send-email-zyw@rock-chips.com>
References: <1467968701-15620-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1467968701-15620-7-git-send-email-zyw@rock-chips.com>

Before phy init, the detection of phy state should be controlled
manually. After that, we can switch the detection to hardward,
it is automatic. Hence move PHY_TXREQUESTCLKHS setting to the end
of phy init.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
---

 drivers/gpu/drm/rockchip/dw-mipi-dsi.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
index 72d7f48..8401185 100644
--- a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
+++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
@@ -477,6 +477,8 @@ static int dw_mipi_dsi_phy_init(struct dw_mipi_dsi *dsi)
 		dev_err(dsi->dev,
 			"failed to wait for phy clk lane stop state\n");
 
+	dsi_write(dsi, DSI_LPCLK_CTRL, PHY_TXREQUESTCLKHS);
+
 phy_init_end:
 	if (!IS_ERR(dsi->phy_cfg_clk))
 		clk_disable_unprepare(dsi->phy_cfg_clk);
@@ -714,7 +716,6 @@ static void dw_mipi_dsi_init(struct dw_mipi_dsi *dsi)
 		  | PHY_RSTZ | PHY_SHUTDOWNZ);
 	dsi_write(dsi, DSI_CLKMGR_CFG, TO_CLK_DIVIDSION(10) |
 		  TX_ESC_CLK_DIVIDSION(7));
-	dsi_write(dsi, DSI_LPCLK_CTRL, PHY_TXREQUESTCLKHS);
 }
 
 static void dw_mipi_dsi_dpi_config(struct dw_mipi_dsi *dsi,
-- 
2.6.3



From zyw at rock-chips.com  Fri Jul  8 02:04:59 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Fri,  8 Jul 2016 17:04:59 +0800
Subject: [PATCH 5/7] drm/rockchip: dw-mipi: support HPD poll
In-Reply-To: <1467968701-15620-1-git-send-email-zyw@rock-chips.com>
References: <1467968701-15620-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1467968701-15620-6-git-send-email-zyw@rock-chips.com>

At the first time of bind, there is no any panel attach in mipi. Add a
DRM_CONNECTOR_POLL_HPD porperty to detect the panel status, when panel
probe, the dw_mipi_dsi_host_attach would be called, then mipi-dsi will
trigger a event to notify the drm framework.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
---

 drivers/gpu/drm/rockchip/dw-mipi-dsi.c | 41 ++++++++++++++++++++++++++++------
 1 file changed, 34 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
index 15ba796..72d7f48 100644
--- a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
+++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
@@ -285,6 +285,7 @@ struct dw_mipi_dsi {
 	struct drm_encoder encoder;
 	struct drm_connector connector;
 	struct mipi_dsi_host dsi_host;
+	struct device_node *panel_node;
 	struct drm_panel *panel;
 	struct device *dev;
 	struct regmap *grf_regmap;
@@ -462,7 +463,6 @@ static int dw_mipi_dsi_phy_init(struct dw_mipi_dsi *dsi)
 	dsi_write(dsi, DSI_PHY_RSTZ, PHY_ENFORCEPLL | PHY_ENABLECLK |
 				     PHY_UNRSTZ | PHY_UNSHUTDOWNZ);
 
-
 	ret = readx_poll_timeout(readl, dsi->base + DSI_PHY_STATUS,
 				 val, val & LOCK, 1000, PHY_STATUS_TIMEOUT_US);
 	if (ret < 0) {
@@ -550,11 +550,11 @@ static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host,
 	dsi->lanes = device->lanes;
 	dsi->channel = device->channel;
 	dsi->format = device->format;
-	dsi->panel = of_drm_find_panel(device->dev.of_node);
-	if (dsi->panel)
-		return drm_panel_attach(dsi->panel, &dsi->connector);
+	dsi->panel_node = device->dev.of_node;
+	if (dsi->connector.dev)
+		drm_helper_hpd_irq_event(dsi->connector.dev);
 
-	return -EINVAL;
+	return 0;
 }
 
 static int dw_mipi_dsi_host_detach(struct mipi_dsi_host *host,
@@ -562,7 +562,10 @@ static int dw_mipi_dsi_host_detach(struct mipi_dsi_host *host,
 {
 	struct dw_mipi_dsi *dsi = host_to_dsi(host);
 
-	drm_panel_detach(dsi->panel);
+	dsi->panel_node = NULL;
+
+	if (dsi->connector.dev)
+		drm_helper_hpd_irq_event(dsi->connector.dev);
 
 	return 0;
 }
@@ -1022,13 +1025,33 @@ static struct drm_connector_helper_funcs dw_mipi_dsi_connector_helper_funcs = {
 static enum drm_connector_status
 dw_mipi_dsi_detect(struct drm_connector *connector, bool force)
 {
-	return connector_status_connected;
+	struct dw_mipi_dsi *dsi = con_to_dsi(connector);
+
+
+	if (!dsi->panel) {
+		dsi->panel = of_drm_find_panel(dsi->panel_node);
+		if (dsi->panel)
+			drm_panel_attach(dsi->panel, &dsi->connector);
+	} else if (!dsi->panel_node) {
+		struct drm_encoder *encoder;
+
+		encoder = platform_get_drvdata(to_platform_device(dsi->dev));
+		dw_mipi_dsi_encoder_disable(encoder);
+		drm_panel_detach(dsi->panel);
+		dsi->panel = NULL;
+	}
+
+	if (dsi->panel)
+		return connector_status_connected;
+
+	return connector_status_disconnected;
 }
 
 static void dw_mipi_dsi_drm_connector_destroy(struct drm_connector *connector)
 {
 	drm_connector_unregister(connector);
 	drm_connector_cleanup(connector);
+	connector->dev = NULL;
 }
 
 static struct drm_connector_funcs dw_mipi_dsi_atomic_connector_funcs = {
@@ -1069,6 +1092,8 @@ static int dw_mipi_dsi_register(struct drm_device *drm,
 		return ret;
 	}
 
+	connector->polled = DRM_CONNECTOR_POLL_HPD;
+
 	drm_connector_helper_add(connector,
 			&dw_mipi_dsi_connector_helper_funcs);
 
@@ -1225,6 +1250,8 @@ static void dw_mipi_dsi_unbind(struct device *dev, struct device *master,
 {
 	struct dw_mipi_dsi *dsi = dev_get_drvdata(dev);
 
+	dw_mipi_dsi_encoder_disable(&dsi->encoder);
+
 	mipi_dsi_host_unregister(&dsi->dsi_host);
 	pm_runtime_disable(dev);
 	clk_disable_unprepare(dsi->pllref_clk);
-- 
2.6.3



From zyw at rock-chips.com  Fri Jul  8 02:05:01 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Fri,  8 Jul 2016 17:05:01 +0800
Subject: [PATCH 7/7] drm/rockchip: dw-mipi: fix insufficient bandwidth of some
 panel
In-Reply-To: <1467968701-15620-1-git-send-email-zyw@rock-chips.com>
References: <1467968701-15620-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1467968701-15620-8-git-send-email-zyw@rock-chips.com>

Set the lanes bps to 1 / 0.9 times of pclk, the margin is not enough
for some panel, it will cause the screen display is not normal, so
increases the badnwidth to 1 / 0.8.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>

---

 drivers/gpu/drm/rockchip/dw-mipi-dsi.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
index 8401185..fca7dde 100644
--- a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
+++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
@@ -503,8 +503,8 @@ static int dw_mipi_dsi_get_lane_bps(struct dw_mipi_dsi *dsi)
 
 	mpclk = DIV_ROUND_UP(dsi->mode->clock, MSEC_PER_SEC);
 	if (mpclk) {
-		/* take 1 / 0.9, since mbps must big than bandwidth of RGB */
-		tmp = mpclk * (bpp / dsi->lanes) * 10 / 9;
+		/* take 1 / 0.8, since mbps must big than bandwidth of RGB */
+		tmp = mpclk * (bpp / dsi->lanes) * 10 / 8;
 		if (tmp < max_mbps)
 			target_mbps = tmp;
 		else
-- 
2.6.3



From heiko at sntech.de  Fri Jul  8 05:33:09 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Fri, 08 Jul 2016 14:33:09 +0200
Subject: [PATCH v6 3/5] usb: dwc3: add phyif_utmi_quirk
In-Reply-To: <1467860066-15142-4-git-send-email-william.wu@rock-chips.com>
References: <1467860066-15142-1-git-send-email-william.wu@rock-chips.com>
 <1467860066-15142-4-git-send-email-william.wu@rock-chips.com>
Message-ID: <2213342.0Nx5tnEW8p@phil>

Hi William,

Am Donnerstag, 7. Juli 2016, 10:54:24 schrieb William Wu:
> Add a quirk to configure the core to support the
> UTMI+ PHY with an 8- or 16-bit interface. UTMI+ PHY
> interface is hardware property, and it's platform
> dependent. Normall, the PHYIf can be configured
> during coreconsultant. But for some specific usb
> cores(e.g. rk3399 soc dwc3), the default PHYIf
> configuration value is fault, so we need to
> reconfigure it by software.
> 
> And refer to the dwc3 databook, the GUSB2PHYCFG.USBTRDTIM
> must be set to the corresponding value according to
> the UTMI+ PHY interface.
> 
> Signed-off-by: William Wu <william.wu at rock-chips.com>
> ---
[...]
> diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt
> b/Documentation/devicetree/bindings/usb/dwc3.txt index 020b0e9..8d7317d
> 100644
> --- a/Documentation/devicetree/bindings/usb/dwc3.txt
> +++ b/Documentation/devicetree/bindings/usb/dwc3.txt
> @@ -42,6 +42,10 @@ Optional properties:
>   - snps,dis-u2-freeclk-exists-quirk: when set, clear the
> u2_freeclk_exists in GUSB2PHYCFG, specify that USB2 PHY doesn't provide
>  			a free-running PHY clock.
> + - snps,phyif-utmi-quirk: when set core will set phyif UTMI+ interface.
> + - snps,phyif-utmi: the value to configure the core to support a UTMI+
> PHY +			with an 8- or 16-bit interface. Value 0 select 8-bit
> +			interface, value 1 select 16-bit interface.

maybe
	snps,phyif-utmi-width = <8> or <16>;

devicetree is about describing the hardware, not the things that get written 
to registers :-) . The conversion from the described width to the register 
value can easily be done in the driver.


Also I don't think you need two properties for this. If the snps,phyif-utmi 
property is specified it indicates that you want to manually set the width 
and if it is absent you want to use the IC default. All functions reading 
property-values indicate if the property is missing.

But it looks like there is already a precendence in 
snps,tx_de_emphasis(_quirk), so maybe Felipe has a different opinion here?



>   - snps,is-utmi-l1-suspend: true when DWC3 asserts output signal
>  			utmi_l1_suspend_n, false when asserts utmi_sleep_n
>   - snps,hird-threshold: HIRD threshold
> diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
> index 0b7bfd2..94036b1 100644
> --- a/drivers/usb/dwc3/core.c
> +++ b/drivers/usb/dwc3/core.c
> @@ -408,6 +408,7 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
>  static int dwc3_phy_setup(struct dwc3 *dwc)
>  {
>  	u32 reg;
> +	u32 usbtrdtim;
>  	int ret;
> 
>  	reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
> @@ -503,6 +504,15 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
>  	if (dwc->dis_u2_freeclk_exists_quirk)
>  		reg &= ~DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS;
> 
> +	if (dwc->phyif_utmi_quirk) {
> +		reg &= ~(DWC3_GUSB2PHYCFG_PHYIF_MASK |
> +		       DWC3_GUSB2PHYCFG_USBTRDTIM_MASK);
> +		usbtrdtim = dwc->phyif_utmi ? USBTRDTIM_UTMI_16_BIT :
> +			    USBTRDTIM_UTMI_8_BIT;
> +		reg |= DWC3_GUSB2PHYCFG_PHYIF(dwc->phyif_utmi) |
> +		       DWC3_GUSB2PHYCFG_USBTRDTIM(usbtrdtim);
> +	}
> +
>  	dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
> 
>  	return 0;
> @@ -834,6 +844,7 @@ static int dwc3_probe(struct platform_device *pdev)
>  	struct resource		*res;
>  	struct dwc3		*dwc;
>  	u8			lpm_nyet_threshold;
> +	u8			phyif_utmi;
>  	u8			tx_de_emphasis;
>  	u8			hird_threshold;
> 
> @@ -880,6 +891,9 @@ static int dwc3_probe(struct platform_device *pdev)
>  	/* default to highest possible threshold */
>  	lpm_nyet_threshold = 0xff;
> 
> +	/* default to UTMI+ 8-bit interface */
> +	phyif_utmi = 0;
> +
>  	/* default to -3.5dB de-emphasis */
>  	tx_de_emphasis = 1;
> 
> @@ -929,6 +943,10 @@ static int dwc3_probe(struct platform_device *pdev)
>  				"snps,dis_rxdet_inp3_quirk");
>  	dwc->dis_u2_freeclk_exists_quirk = device_property_read_bool(dev,
>  				"snps,dis-u2-freeclk-exists-quirk");
> +	dwc->phyif_utmi_quirk = device_property_read_bool(dev,
> +				"snps,phyif-utmi-quirk");
> +	 device_property_read_u8(dev, "snps,phyif-utmi",
> +				 &phyif_utmi);


As described above device_property_read_u8 will return an error if the 
property is not present, so you could fill your dwc->phyif_utmi_quirk from 
that:

	ret = device_property_read_u8(dev, "snps,phyif-utmi",
				 &phyif_utmi);
	dwc->phyif_utmi_quirk = (ret == 0) ? true : false;


Heiko


From balbi at kernel.org  Fri Jul  8 06:29:44 2016
From: balbi at kernel.org (Felipe Balbi)
Date: Fri, 08 Jul 2016 16:29:44 +0300
Subject: [PATCH v6 3/5] usb: dwc3: add phyif_utmi_quirk
In-Reply-To: <2213342.0Nx5tnEW8p@phil>
References: <1467860066-15142-1-git-send-email-william.wu@rock-chips.com>
 <1467860066-15142-4-git-send-email-william.wu@rock-chips.com>
 <2213342.0Nx5tnEW8p@phil>
Message-ID: <87d1moi7g7.fsf@linux.intel.com>


Hi,

Heiko Stuebner <heiko at sntech.de> writes:
> Am Donnerstag, 7. Juli 2016, 10:54:24 schrieb William Wu:
>> Add a quirk to configure the core to support the
>> UTMI+ PHY with an 8- or 16-bit interface. UTMI+ PHY
>> interface is hardware property, and it's platform
>> dependent. Normall, the PHYIf can be configured
>> during coreconsultant. But for some specific usb
>> cores(e.g. rk3399 soc dwc3), the default PHYIf
>> configuration value is fault, so we need to
>> reconfigure it by software.
>> 
>> And refer to the dwc3 databook, the GUSB2PHYCFG.USBTRDTIM
>> must be set to the corresponding value according to
>> the UTMI+ PHY interface.
>> 
>> Signed-off-by: William Wu <william.wu at rock-chips.com>
>> ---
> [...]
>> diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt
>> b/Documentation/devicetree/bindings/usb/dwc3.txt index 020b0e9..8d7317d
>> 100644
>> --- a/Documentation/devicetree/bindings/usb/dwc3.txt
>> +++ b/Documentation/devicetree/bindings/usb/dwc3.txt
>> @@ -42,6 +42,10 @@ Optional properties:
>>   - snps,dis-u2-freeclk-exists-quirk: when set, clear the
>> u2_freeclk_exists in GUSB2PHYCFG, specify that USB2 PHY doesn't provide
>>  			a free-running PHY clock.
>> + - snps,phyif-utmi-quirk: when set core will set phyif UTMI+ interface.
>> + - snps,phyif-utmi: the value to configure the core to support a UTMI+
>> PHY +			with an 8- or 16-bit interface. Value 0 select 8-bit
>> +			interface, value 1 select 16-bit interface.
>
> maybe
> 	snps,phyif-utmi-width = <8> or <16>;
>
> devicetree is about describing the hardware, not the things that get written 
> to registers :-) . The conversion from the described width to the register 
> value can easily be done in the driver.
>
>
> Also I don't think you need two properties for this. If the snps,phyif-utmi 
> property is specified it indicates that you want to manually set the width 
> and if it is absent you want to use the IC default. All functions reading 
> property-values indicate if the property is missing.
>
> But it looks like there is already a precendence in 
> snps,tx_de_emphasis(_quirk), so maybe Felipe has a different opinion here?
>
>
>
>>   - snps,is-utmi-l1-suspend: true when DWC3 asserts output signal
>>  			utmi_l1_suspend_n, false when asserts utmi_sleep_n
>>   - snps,hird-threshold: HIRD threshold
>> diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
>> index 0b7bfd2..94036b1 100644
>> --- a/drivers/usb/dwc3/core.c
>> +++ b/drivers/usb/dwc3/core.c
>> @@ -408,6 +408,7 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
>>  static int dwc3_phy_setup(struct dwc3 *dwc)
>>  {
>>  	u32 reg;
>> +	u32 usbtrdtim;
>>  	int ret;
>> 
>>  	reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
>> @@ -503,6 +504,15 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
>>  	if (dwc->dis_u2_freeclk_exists_quirk)
>>  		reg &= ~DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS;
>> 
>> +	if (dwc->phyif_utmi_quirk) {
>> +		reg &= ~(DWC3_GUSB2PHYCFG_PHYIF_MASK |
>> +		       DWC3_GUSB2PHYCFG_USBTRDTIM_MASK);
>> +		usbtrdtim = dwc->phyif_utmi ? USBTRDTIM_UTMI_16_BIT :
>> +			    USBTRDTIM_UTMI_8_BIT;
>> +		reg |= DWC3_GUSB2PHYCFG_PHYIF(dwc->phyif_utmi) |
>> +		       DWC3_GUSB2PHYCFG_USBTRDTIM(usbtrdtim);
>> +	}
>> +
>>  	dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
>> 
>>  	return 0;
>> @@ -834,6 +844,7 @@ static int dwc3_probe(struct platform_device *pdev)
>>  	struct resource		*res;
>>  	struct dwc3		*dwc;
>>  	u8			lpm_nyet_threshold;
>> +	u8			phyif_utmi;
>>  	u8			tx_de_emphasis;
>>  	u8			hird_threshold;
>> 
>> @@ -880,6 +891,9 @@ static int dwc3_probe(struct platform_device *pdev)
>>  	/* default to highest possible threshold */
>>  	lpm_nyet_threshold = 0xff;
>> 
>> +	/* default to UTMI+ 8-bit interface */
>> +	phyif_utmi = 0;
>> +
>>  	/* default to -3.5dB de-emphasis */
>>  	tx_de_emphasis = 1;
>> 
>> @@ -929,6 +943,10 @@ static int dwc3_probe(struct platform_device *pdev)
>>  				"snps,dis_rxdet_inp3_quirk");
>>  	dwc->dis_u2_freeclk_exists_quirk = device_property_read_bool(dev,
>>  				"snps,dis-u2-freeclk-exists-quirk");
>> +	dwc->phyif_utmi_quirk = device_property_read_bool(dev,
>> +				"snps,phyif-utmi-quirk");
>> +	 device_property_read_u8(dev, "snps,phyif-utmi",
>> +				 &phyif_utmi);
>
>
> As described above device_property_read_u8 will return an error if the 
> property is not present, so you could fill your dwc->phyif_utmi_quirk from 
> that:
>
> 	ret = device_property_read_u8(dev, "snps,phyif-utmi",
> 				 &phyif_utmi);
> 	dwc->phyif_utmi_quirk = (ret == 0) ? true : false;

I like this much better :-) Unfortunately can't fix tx_deemphasis due to
backwards compatibility :-s

-- 
balbi
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 818 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160708/c0dc54fe/attachment-0001.sig>

From john at metanate.com  Fri Jul  8 06:52:11 2016
From: john at metanate.com (John Keeping)
Date: Fri, 8 Jul 2016 14:52:11 +0100
Subject: [PATCH 5/7] drm/rockchip: dw-mipi: support HPD poll
In-Reply-To: <1467968701-15620-6-git-send-email-zyw@rock-chips.com>
References: <1467968701-15620-1-git-send-email-zyw@rock-chips.com>
 <1467968701-15620-6-git-send-email-zyw@rock-chips.com>
Message-ID: <20160708145211.6bf83d8f.john@metanate.com>

On Fri,  8 Jul 2016 17:04:59 +0800, Chris Zhong wrote:

> At the first time of bind, there is no any panel attach in mipi. Add a
> DRM_CONNECTOR_POLL_HPD porperty to detect the panel status, when panel
> probe, the dw_mipi_dsi_host_attach would be called, then mipi-dsi will
> trigger a event to notify the drm framework.
> 
> Signed-off-by: Chris Zhong <zyw at rock-chips.com>

Can we do something like this instead?  We know that the panel must
always be attached and this has the advantage that the display size will
be known when the framebuffer console loads.

-- >8 --
Subject: [PATCH] drm/rockchip: dw-mipi-dsi: defer probe if panel is not loaded

This ensures that the output resolution is known before fbcon loads.

Signed-off-by: John Keeping <john at metanate.com>
---
 drivers/gpu/drm/rockchip/dw-mipi-dsi.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
index 6ef5f3be8468..c0499266d116 100644
--- a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
+++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
@@ -1154,10 +1154,17 @@ static int dw_mipi_dsi_bind(struct device *dev, struct device *master,
 
 	dsi->dsi_host.ops = &dw_mipi_dsi_host_ops;
 	dsi->dsi_host.dev = dev;
-	return mipi_dsi_host_register(&dsi->dsi_host);
+	ret = mipi_dsi_host_register(&dsi->dsi_host);
+	if (!ret && !dsi->panel) {
+		mipi_dsi_host_unregister(&dsi->dsi_host);
+		drm_encoder_cleanup(&dsi->encoder);
+		drm_connector_cleanup(&dsi->connector);
+		ret = -EPROBE_DEFER;
+	}
 
 err_pllref:
-	clk_disable_unprepare(dsi->pllref_clk);
+	if (ret)
+		clk_disable_unprepare(dsi->pllref_clk);
 	return ret;
 }
 


From thierry.reding at gmail.com  Fri Jul  8 08:43:02 2016
From: thierry.reding at gmail.com (Thierry Reding)
Date: Fri, 8 Jul 2016 17:43:02 +0200
Subject: [PATCH v3 12/14] regulator: pwm: Retrieve correct voltage
In-Reply-To: <1465895602-31008-13-git-send-email-boris.brezillon@free-electrons.com>
References: <1465895602-31008-1-git-send-email-boris.brezillon@free-electrons.com>
 <1465895602-31008-13-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <20160708154302.GC1151@ulmo.ba.sec>

On Tue, Jun 14, 2016 at 11:13:20AM +0200, Boris Brezillon wrote:
> The continuous PWM voltage regulator is caching the voltage value in
> the ->volt_uV field. While most of the time this value should reflect the
> real voltage, sometime it can be sightly different if the PWM device
> rounded the set_duty_cycle request.
> Moreover, this value is not valid until someone has modified the regulator
> output.
> 
> Remove the ->volt_uV field and always rely on the PWM state to calculate
> the regulator output.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
> Reviewed-by: Brian Norris <briannorris at chromium.org>
> Tested-by: Brian Norris <briannorris at chromium.org>
> Tested-by: Heiko Stuebner <heiko at sntech.de>
> ---
> Mark,
> 
> I know you already added your Tested-by/Acked-by tags on this patch
> but this version has slightly change and is now making use of the
> pwm_get_relative_duty_cycle() helper instead of manually converting
> the absolute duty_cycle value into a relative one.
> ---
>  drivers/regulator/pwm-regulator.c | 12 ++++++------
>  1 file changed, 6 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/regulator/pwm-regulator.c b/drivers/regulator/pwm-regulator.c
> index 2000118..80d083f 100644
> --- a/drivers/regulator/pwm-regulator.c
> +++ b/drivers/regulator/pwm-regulator.c
> @@ -35,9 +35,6 @@ struct pwm_regulator_data {
>  	struct regulator_ops ops;
>  
>  	int state;
> -
> -	/* Continuous voltage */
> -	int volt_uV;
>  };
>  
>  struct pwm_voltages {
> @@ -135,8 +132,13 @@ static int pwm_regulator_is_enabled(struct regulator_dev *dev)
>  static int pwm_regulator_get_voltage(struct regulator_dev *rdev)
>  {
>  	struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
> +	int min_uV = rdev->constraints->min_uV;
> +	int diff = rdev->constraints->max_uV - min_uV;
> +	struct pwm_state pstate;
>  
> -	return drvdata->volt_uV;
> +	pwm_get_state(drvdata->pwm, &pstate);
> +
> +	return min_uV + pwm_get_relative_duty_cycle(&pstate, diff);
>  }
>  
>  static int pwm_regulator_set_voltage(struct regulator_dev *rdev,
> @@ -162,8 +164,6 @@ static int pwm_regulator_set_voltage(struct regulator_dev *rdev,
>  		return ret;
>  	}
>  
> -	drvdata->volt_uV = min_uV;
> -
>  	/* Delay required by PWM regulator to settle to the new voltage */
>  	usleep_range(ramp_delay, ramp_delay + 1000);
>  

This hunk has a minor conflict with the regulator tree and the commit
830583004e61 ("regulator: pwm: Drop unneeded pwm_enable() call") that
it contains.

Mark, do you want me to provide a stable branch with the PWM regulator
patches and resolve that conflict in your tree? Or would you rather take
the whole set based on a stable branch from the PWM tree? Or maybe yet
another possibility would be to base the PWM tree on a stable branch
from the regulator tree containing the above commit.

Or we can let Linus sort out the conflict, it's really quite trivial.

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160708/5532d28a/attachment.sig>

From thierry.reding at gmail.com  Fri Jul  8 09:35:42 2016
From: thierry.reding at gmail.com (Thierry Reding)
Date: Fri, 8 Jul 2016 18:35:42 +0200
Subject: [PATCH v3 00/14] regulator: pwm: various improvements
In-Reply-To: <1465895602-31008-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1465895602-31008-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <20160708163542.GD1151@ulmo.ba.sec>

On Tue, Jun 14, 2016 at 11:13:08AM +0200, Boris Brezillon wrote:
> Hello,
> 
> This patch series series aims at adding two important features to the
> pwm-regulator driver.
> 
> The first one is the support for 'smooth handover' between the
> bootloader and the kernel. This is mainly solving problems we have when
> the PWM is controlling a critical regulator (like the one powering the
> DDR chip). Currently, when the PWM regulator acquire the PWM device it
> assumes it was off and it's safe to change the configuration before
> enabling it, which can generate glitches on the PWM signal which in turn
> generated glitches on the output voltage.
> To solve that we've introduced support for hardware readout to the
> PWM framework, so that the PWM regulator driver can adjust the PWM
> a probe time and avoid glitches.
> Atomic update is also helping in this regard.
> 
> Patch 1 is adding convenient helpers (at the PWM level) that will be
> used by the PWM regulator driver.
> Patches 2 to 7 are preparing everything on the PWM driver side to make
> hardware readout available to all platforms using the PWM regulator
> driver (rockchip and sti).
> Patches 8 to 11 are making use of the atomic update and hardware readout
> features to implement this smooth handover.
> 
> The second feature we add to the driver is the capability of using
> a sub duty_cycle range in continuous mode. By default the regulator
> is assuming that min_uV is achieved with a 0% dutycyle and max_uV
> with a 100% dutycycle, but this is not necessarily true.
> Moreover, in some cases (when the PWM device does not support
> polarity inversion), we might have min_uV at 100% and max_uV at 0%.
> Hence the addition of new properties to the existing DT bindings.
> The feature is added in patch 12 and 13.
> 
> Best Regards,
> 
> Boris
> 
> Changes since v2:
> - add Heiko's Tested-by
> - split patch 1 in 2 patches
> - rework the documentation
> - rename pwm_prepare_new_state() into pwm_init_state()
> - make pwm_set_relative_duty_cycle() return an error code when scale
>   or duty_cycle are inconsistent
> 
> Changes since v1:
> - dropped already applied patches
> - added R-b/A-b/T-b tags
> - s/readl/readl_relaxed/ in patch 3 (as suggested by Brian)
> - fixed pwm-regulator DT binding doc
> - added some comments in the code
> - replaced pwm_get_state() + if (state.enabled) by if (pwm_is_enabled())
> 
> Boris Brezillon (14):
>   pwm: Add an helper to prepare a new PWM state
>   pwm: Add two helpers to ease relative duty cycle manipulation
>   pwm: rockchip: Fix period and duty_cycle approximation
>   pwm: rockchip: Add support for hardware readout
>   pwm: rockchip: Avoid glitches on already running PWMs
>   pwm: rockchip: Add support for atomic update
>   pwm: sti: Add support for hardware readout
>   pwm: sti: Avoid glitches on already running PWMs
>   regulator: pwm: Adjust PWM config at probe time
>   regulator: pwm: Switch to the atomic PWM API
>   regulator: pwm: Properly initialize the ->state field
>   regulator: pwm: Retrieve correct voltage
>   regulator: pwm: Support extra continuous mode cases
>   regulator: pwm: Document pwm-dutycycle-unit and pwm-dutycycle-range

I've applied everything in this series to the PWM tree, except for the
two pwm-sti driver patches, which I'd like to get an Acked-by from one
of the maintainers.

Mark, let me know how you'd like to handle the miniature conflict in the
regulator-pwm driver that I mentioned in one of the patches.

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160708/7fb53553/attachment.sig>

From linux at roeck-us.net  Fri Jul  8 09:35:11 2016
From: linux at roeck-us.net (Guenter Roeck)
Date: Fri, 8 Jul 2016 09:35:11 -0700
Subject: [v6,2/2] PCI: Rockchip: Add Rockchip PCIe controller support
In-Reply-To: <1467789398-13501-2-git-send-email-shawn.lin@rock-chips.com>
References: <1467789398-13501-2-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <20160708163511.GA23348@roeck-us.net>

On Wed, Jul 06, 2016 at 03:16:38PM +0800, Shawn Lin wrote:
> This patch adds Rockchip PCIe controller support found
> on RK3399 Soc platform.
> 
> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
> ---

[ ... ]

> +	/* Get the I/O and memory ranges from DT */
> +	resource_list_for_each_entry(win, &res) {
> +		switch (resource_type(win->res)) {
> +		case IORESOURCE_IO:
> +			io = win->res;
> +			io->name = "I/O";
> +			io_size = resource_size(io);
> +			io_bus_addr = io->start - win->offset;
> +			err = pci_remap_iospace(io, io_base);

This function is not exported. Either it will have to be exported
(which may be difficult since it is declared as __weak), or the
driver must only be built into the kernel, or the function can
not be used.

Guenter


From thierry.reding at gmail.com  Fri Jul  8 09:41:49 2016
From: thierry.reding at gmail.com (Thierry Reding)
Date: Fri, 8 Jul 2016 18:41:49 +0200
Subject: [PATCH] regulator: pwm: Fix regulator ramp delay for continuous
 mode
In-Reply-To: <20160704091344.GY6247@sirena.org.uk>
References: <1467089591-7631-1-git-send-email-dianders@chromium.org>
 <20160701080614.GA9196@sirena.org.uk>
 <CAD=FV=WKR_Fq6o34886RCY-T-6AKdRqFz9H1aTiRq7Zu=o8TWA@mail.gmail.com>
 <20160702174752.7e893ac2@bbrezillon>
 <20160704085336.GT6247@sirena.org.uk>
 <20160704110504.10a15c1c@bbrezillon>
 <20160704091344.GY6247@sirena.org.uk>
Message-ID: <20160708164149.GE1151@ulmo.ba.sec>

On Mon, Jul 04, 2016 at 11:13:44AM +0200, Mark Brown wrote:
> On Mon, Jul 04, 2016 at 11:05:04AM +0200, Boris Brezillon wrote:
> 
> > I understand, but can we still expect to have it in 4.8 (I mean,
> > assuming Thierry is okay taking the first 2 patching for 4.8 too)?
> 
> Possibly - we are getting very close to the merge window now, I don't
> know what Thierry's policies are on applying things as the merge window
> gets near.

I don't have hard deadlines, especially not for new code that has zero
potential for regressing. I've now pulled that whole series into the PWM
tree.

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160708/54d9d58b/attachment-0001.sig>

From arnd at arndb.de  Fri Jul  8 13:10:14 2016
From: arnd at arndb.de (Arnd Bergmann)
Date: Fri, 08 Jul 2016 22:10:14 +0200
Subject: [v6,2/2] PCI: Rockchip: Add Rockchip PCIe controller support
In-Reply-To: <20160708163511.GA23348@roeck-us.net>
References: <1467789398-13501-2-git-send-email-shawn.lin@rock-chips.com>
 <20160708163511.GA23348@roeck-us.net>
Message-ID: <2013221.obfd8Z8cde@wuerfel>

On Friday, July 8, 2016 9:35:11 AM CEST Guenter Roeck wrote:
> On Wed, Jul 06, 2016 at 03:16:38PM +0800, Shawn Lin wrote:
> > This patch adds Rockchip PCIe controller support found
> > on RK3399 Soc platform.
> > 
> > Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
> > ---
> 
> [ ... ]
> 
> > +     /* Get the I/O and memory ranges from DT */
> > +     resource_list_for_each_entry(win, &res) {
> > +             switch (resource_type(win->res)) {
> > +             case IORESOURCE_IO:
> > +                     io = win->res;
> > +                     io->name = "I/O";
> > +                     io_size = resource_size(io);
> > +                     io_bus_addr = io->start - win->offset;
> > +                     err = pci_remap_iospace(io, io_base);
> 
> This function is not exported. Either it will have to be exported
> (which may be difficult since it is declared as __weak), or the
> driver must only be built into the kernel, or the function can
> not be used.
> 

I think we can simply drop the __weak annotation, there is no other
implementation of this, and I doubt there will ever be. Even if it
was needed, we could do it in a different way.

	Arnd



From William.wu at rock-chips.com  Fri Jul  8 20:38:00 2016
From: William.wu at rock-chips.com (William.wu)
Date: Sat, 9 Jul 2016 11:38:00 +0800
Subject: [PATCH v6 3/5] usb: dwc3: add phyif_utmi_quirk
In-Reply-To: <87d1moi7g7.fsf@linux.intel.com>
References: <1467860066-15142-1-git-send-email-william.wu@rock-chips.com>
 <1467860066-15142-4-git-send-email-william.wu@rock-chips.com>
 <2213342.0Nx5tnEW8p@phil> <87d1moi7g7.fsf@linux.intel.com>
Message-ID: <f5daa697-3125-cfbf-7318-d4900f666362@rock-chips.com>

Dear Heiko & Balbi?


On 2016/7/8 21:29, Felipe Balbi wrote:
> Hi,
>
> Heiko Stuebner <heiko at sntech.de> writes:
>> Am Donnerstag, 7. Juli 2016, 10:54:24 schrieb William Wu:
>>> Add a quirk to configure the core to support the
>>> UTMI+ PHY with an 8- or 16-bit interface. UTMI+ PHY
>>> interface is hardware property, and it's platform
>>> dependent. Normall, the PHYIf can be configured
>>> during coreconsultant. But for some specific usb
>>> cores(e.g. rk3399 soc dwc3), the default PHYIf
>>> configuration value is fault, so we need to
>>> reconfigure it by software.
>>>
>>> And refer to the dwc3 databook, the GUSB2PHYCFG.USBTRDTIM
>>> must be set to the corresponding value according to
>>> the UTMI+ PHY interface.
>>>
>>> Signed-off-by: William Wu <william.wu at rock-chips.com>
>>> ---
>> [...]
>>> diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt
>>> b/Documentation/devicetree/bindings/usb/dwc3.txt index 020b0e9..8d7317d
>>> 100644
>>> --- a/Documentation/devicetree/bindings/usb/dwc3.txt
>>> +++ b/Documentation/devicetree/bindings/usb/dwc3.txt
>>> @@ -42,6 +42,10 @@ Optional properties:
>>>    - snps,dis-u2-freeclk-exists-quirk: when set, clear the
>>> u2_freeclk_exists in GUSB2PHYCFG, specify that USB2 PHY doesn't provide
>>>   			a free-running PHY clock.
>>> + - snps,phyif-utmi-quirk: when set core will set phyif UTMI+ interface.
>>> + - snps,phyif-utmi: the value to configure the core to support a UTMI+
>>> PHY +			with an 8- or 16-bit interface. Value 0 select 8-bit
>>> +			interface, value 1 select 16-bit interface.
>> maybe
>> 	snps,phyif-utmi-width = <8> or <16>;
>>
>> devicetree is about describing the hardware, not the things that get written
>> to registers :-) . The conversion from the described width to the register
>> value can easily be done in the driver.
Thanks for your suggestion:-)
Yes? ?snps,phyif-utmi-width = <8> or <16>? is much clearer and easier to 
understand.
And I have considered the same dts property for phyif-utmi, but I have 
no good idea about
the conversion from described width to the registers value for the time 
being.

About phyif utmi width configuration? we need to set two places in 
GUSB2PHYCFG register?
according to DWC3 USB3.0 controller databook version3.00a?6.3.46 
GUSB2PHYCFG

----------------------------------------------------------------------------------------------
     Bits   |  Name                 |     Description
----------------------------------------------------------------------------------------------
   13:10  |   USBTRDTIM       |     Sets the turnaround time in PHY clocks.
             |                            |     4'h5: When the MAC 
interface is 16-bit UTMI+
             |                            |     4'h9: When the MAC 
interface is 8-bit UTMI+/ULPI.
----------------------------------------------------------------------------------------------
   3        |   PHYIF                |    If UTMI+ is selected, the 
application uses this bit to configure
             |                            |    core to support a UTMI+ 
PHY with an 8- or 16-bit interface.
             |                            |    1'b0: 8 bits
             |                            |    1'b1: 16 bits
----------------------------------------------------------------------------------------------

And I think maybe I can try to do this:
change it in dts:
         snps,phyif-utmi-width = <8> or <16>;

Then convert to register value like this?
        device_property_read_u8(dev, "snps,phyif-utmi-width",
                                              &phyif_utmi_width);

        dwc->phyif_utmi = phyif_utmi_width >> 4;

  Ater the conversion? dwc->phyif_utmi value 0 means 8 bits, value 1 
means 16 bits,
  and it's easier for us to config GUSB2PHYCFG.

Is it OK?

>>
>>
>> Also I don't think you need two properties for this. If the snps,phyif-utmi
>> property is specified it indicates that you want to manually set the width
>> and if it is absent you want to use the IC default. All functions reading
>> property-values indicate if the property is missing.
Ah, it seems very good to me. One dts property "snps,phyif-utmi" can help to
reconfig phyif utmi width. And it seems that Felipe likes it very much 
too. :-D
>> But it looks like there is already a precendence in
>> snps,tx_de_emphasis(_quirk), so maybe Felipe has a different opinion here?
>>
>>
>>
>>>    - snps,is-utmi-l1-suspend: true when DWC3 asserts output signal
>>>   			utmi_l1_suspend_n, false when asserts utmi_sleep_n
>>>    - snps,hird-threshold: HIRD threshold
>>> diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
>>> index 0b7bfd2..94036b1 100644
>>> --- a/drivers/usb/dwc3/core.c
>>> +++ b/drivers/usb/dwc3/core.c
>>> @@ -408,6 +408,7 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
>>>   static int dwc3_phy_setup(struct dwc3 *dwc)
>>>   {
>>>   	u32 reg;
>>> +	u32 usbtrdtim;
>>>   	int ret;
>>>
>>>   	reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
>>> @@ -503,6 +504,15 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
>>>   	if (dwc->dis_u2_freeclk_exists_quirk)
>>>   		reg &= ~DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS;
>>>
>>> +	if (dwc->phyif_utmi_quirk) {
>>> +		reg &= ~(DWC3_GUSB2PHYCFG_PHYIF_MASK |
>>> +		       DWC3_GUSB2PHYCFG_USBTRDTIM_MASK);
>>> +		usbtrdtim = dwc->phyif_utmi ? USBTRDTIM_UTMI_16_BIT :
>>> +			    USBTRDTIM_UTMI_8_BIT;
>>> +		reg |= DWC3_GUSB2PHYCFG_PHYIF(dwc->phyif_utmi) |
>>> +		       DWC3_GUSB2PHYCFG_USBTRDTIM(usbtrdtim);
>>> +	}
>>> +
>>>   	dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
>>>
>>>   	return 0;
>>> @@ -834,6 +844,7 @@ static int dwc3_probe(struct platform_device *pdev)
>>>   	struct resource		*res;
>>>   	struct dwc3		*dwc;
>>>   	u8			lpm_nyet_threshold;
>>> +	u8			phyif_utmi;
>>>   	u8			tx_de_emphasis;
>>>   	u8			hird_threshold;
>>>
>>> @@ -880,6 +891,9 @@ static int dwc3_probe(struct platform_device *pdev)
>>>   	/* default to highest possible threshold */
>>>   	lpm_nyet_threshold = 0xff;
>>>
>>> +	/* default to UTMI+ 8-bit interface */
>>> +	phyif_utmi = 0;
>>> +
>>>   	/* default to -3.5dB de-emphasis */
>>>   	tx_de_emphasis = 1;
>>>
>>> @@ -929,6 +943,10 @@ static int dwc3_probe(struct platform_device *pdev)
>>>   				"snps,dis_rxdet_inp3_quirk");
>>>   	dwc->dis_u2_freeclk_exists_quirk = device_property_read_bool(dev,
>>>   				"snps,dis-u2-freeclk-exists-quirk");
>>> +	dwc->phyif_utmi_quirk = device_property_read_bool(dev,
>>> +				"snps,phyif-utmi-quirk");
>>> +	 device_property_read_u8(dev, "snps,phyif-utmi",
>>> +				 &phyif_utmi);
>>
>> As described above device_property_read_u8 will return an error if the
>> property is not present, so you could fill your dwc->phyif_utmi_quirk from
>> that:
>>
>> 	ret = device_property_read_u8(dev, "snps,phyif-utmi",
>> 				 &phyif_utmi);
>> 	dwc->phyif_utmi_quirk = (ret == 0) ? true : false;
> I like this much better :-) Unfortunately can't fix tx_deemphasis due to
> backwards compatibility :-s
I'll try to follow Heiko's suggestion and fix the dwc->phyif_utmi_quirk 
next patch.

Best regards,
        William.wu
>





From broonie at kernel.org  Sat Jul  9 02:47:18 2016
From: broonie at kernel.org (Mark Brown)
Date: Sat, 9 Jul 2016 11:47:18 +0200
Subject: [PATCH v3 12/14] regulator: pwm: Retrieve correct voltage
In-Reply-To: <20160708154302.GC1151@ulmo.ba.sec>
References: <1465895602-31008-1-git-send-email-boris.brezillon@free-electrons.com>
 <1465895602-31008-13-git-send-email-boris.brezillon@free-electrons.com>
 <20160708154302.GC1151@ulmo.ba.sec>
Message-ID: <20160709094718.GB6247@sirena.org.uk>

On Fri, Jul 08, 2016 at 05:43:02PM +0200, Thierry Reding wrote:

> Mark, do you want me to provide a stable branch with the PWM regulator
> patches and resolve that conflict in your tree? Or would you rather take
> the whole set based on a stable branch from the PWM tree? Or maybe yet
> another possibility would be to base the PWM tree on a stable branch
> from the regulator tree containing the above commit.

Probably easiest to use this signed tag and resolve it in your tree:

The following changes since commit 1a695a905c18548062509178b98bc91e67510864:

  Linux 4.7-rc1 (2016-05-29 09:29:24 -0700)

are available in the git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator.git tags/pwm-modernization

for you to fetch changes up to c2588393e6315ab68207323d37d2a73713d6bc81:

  regulator: pwm: Fix regulator ramp delay for continuous mode (2016-07-07 11:45:06 +0200)

----------------------------------------------------------------
regulator: Provide a branch for moderninzation of the PWM code

There's a new, improved PWM API which allows a lot of improvements in
the PWM regulator driver.  Since the bulk of the changes are in the PWM
API this is being managed in the PWM tree, merge pending regulator API
changes to allow this to be resolved more easily.

----------------------------------------------------------------
Alexandre Courbot (1):
      regulator: pwm: Support for enable GPIO

Boris Brezillon (1):
      regulator: pwm: Drop unneeded pwm_enable() call

Douglas Anderson (1):
      regulator: pwm: Fix regulator ramp delay for continuous mode

 .../bindings/regulator/pwm-regulator.txt           |  7 +++-
 drivers/regulator/pwm-regulator.c                  | 40 ++++++++++++++++++----
 2 files changed, 39 insertions(+), 8 deletions(-)
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160709/44648b05/attachment.sig>

From ayaka at soulik.info  Sat Jul  9 08:42:28 2016
From: ayaka at soulik.info (ayaka)
Date: Sat, 9 Jul 2016 23:42:28 +0800
Subject: [PATCH 0/3] ARM: dts: the dts support for rk3288 firefly reload
In-Reply-To: <2686572.mni1Mn7R6t@phil>
References: <3015470.z4L7UQJKa9@phil>
 <1467829380-16242-1-git-send-email-ayaka@soulik.info>
 <2686572.mni1Mn7R6t@phil>
Message-ID: <c7f8b96b-ceff-0449-8957-bac2779c9508@soulik.info>

Thank you for you review, HeiKo


On 07/08/2016 05:35 AM, Heiko Stuebner wrote:
> Hi Randy,
>
> Am Donnerstag, 7. Juli 2016, 02:22:57 schrieb Randy Li:
>> The rk3288 firefly reload  is a Rockchip RK3288 based board be found by
>> core board and main board. The regulators are connected in a different
>> way to the previous version of firefly boards, it is necessary to
>> move some common code to uncommon place.
>>
>> I only tested the ethernet and confirmed that works.
>> The usb in this board won't caused by the bugs in the driver.
>>
>> This version follow the suggests from Heiko Stuebner,
>> except the duplicated supply name problem, I don't think
>> it could be fixed in that way.
> I've now had a chance to look at that reload board on the firefly site.
> Firefly also is the company name, so a board named that way is not
> necessarily a "variant" :-) .
>
> And looking at the "reload" board this definitly seems to be a very
> different product with it being a system-on-module+baseboard design with
> additional peripherals like that sata bridge, camera interfaces and probably
sata bridge is just a SATA to usb bridge and the "reload" bring back the 
DVP camera interface and
a HDMI rx chip connected to the other MIPI camera interface.
> more.
>
> As you might've seen, most Rockchip boards are based on some reference-
> design, so are similar in a big part of their core layout.
Yes, from the evb. But the even the main board of evb in rockchip 
company have at lease 3 versions
as I known.
Also the evb is found by power board, main board and core board.
> So, looking at the vastly different product the reload is, I'd really like
> to have a separate dts for the reload, to not run into more confusing
> differences later on.
The main problem is that power connections are different. That is why I 
decide to make a
separate dts. If the kernel introduce the override dts, I could have a 
better way to implement
it.
> Also, when adding a new board, please also add an entry to
> Documentation/devicetree/bindingd/arm/rockchip.txt
I would send a patch set in a few days.
> Thanks
> Heiko
Thank you for you review and you patient again
Randy



From heiko at sntech.de  Sat Jul  9 16:06:08 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Sun, 10 Jul 2016 01:06:08 +0200
Subject: [PATCH 0/3] ARM: dts: the dts support for rk3288 firefly reload
In-Reply-To: <c7f8b96b-ceff-0449-8957-bac2779c9508@soulik.info>
References: <3015470.z4L7UQJKa9@phil> <2686572.mni1Mn7R6t@phil>
 <c7f8b96b-ceff-0449-8957-bac2779c9508@soulik.info>
Message-ID: <5614243.tqTyZoc945@phil>

Hi Randy,

Am Samstag, 9. Juli 2016, 23:42:28 schrieb ayaka:
> On 07/08/2016 05:35 AM, Heiko Stuebner wrote:
> > Am Donnerstag, 7. Juli 2016, 02:22:57 schrieb Randy Li:
> >> The rk3288 firefly reload  is a Rockchip RK3288 based board be found by
> >> core board and main board. The regulators are connected in a different
> >> way to the previous version of firefly boards, it is necessary to
> >> move some common code to uncommon place.
> >> 
> >> I only tested the ethernet and confirmed that works.
> >> The usb in this board won't caused by the bugs in the driver.
> >> 
> >> This version follow the suggests from Heiko Stuebner,
> >> except the duplicated supply name problem, I don't think
> >> it could be fixed in that way.
> > 
> > I've now had a chance to look at that reload board on the firefly site.
> > Firefly also is the company name, so a board named that way is not
> > necessarily a "variant" :-) .
> > 
> > And looking at the "reload" board this definitly seems to be a very
> > different product with it being a system-on-module+baseboard design with
> > additional peripherals like that sata bridge, camera interfaces and
> > probably
> sata bridge is just a SATA to usb bridge and the "reload" bring back the
> DVP camera interface and
> a HDMI rx chip connected to the other MIPI camera interface.

there are always more things to control (reset pins, regulators) and the usb 
subsystem is currently in the process of getting support for such "embedded" 
uses.


> > more.
> > 
> > As you might've seen, most Rockchip boards are based on some reference-
> > design, so are similar in a big part of their core layout.
> 
> Yes, from the evb. But the even the main board of evb in rockchip
> company have at lease 3 versions
> as I known.
> Also the evb is found by power board, main board and core board.
> 
> > So, looking at the vastly different product the reload is, I'd really
> > like to have a separate dts for the reload, to not run into more
> > confusing differences later on.
> 
> The main problem is that power connections are different. That is why I
> decide to make a
> separate dts. If the kernel introduce the override dts, I could have a
> better way to implement
> it.

Just to make sure we're not talking about different things. This was meant 
to illustrate that even though core layouts often look similar we should not 
try to connect different product board files unnecessarily, as the small 
differences will make everything more complicated.

The "reload" definitly is a completely different product that only shares 
the manufacturer (firefly) and the soc (rk3288) with the other product and 
as I wrote should get its own independent dts file.


If anything you could do a split into a reload-core dtsi for the system-on-
module part and a baseboard dts that includes that (something like what is 
done for rk3288-rock2).



> > Also, when adding a new board, please also add an entry to
> > Documentation/devicetree/bindingd/arm/rockchip.txt
> 
> I would send a patch set in a few days.
> 
> > Thanks
> > Heiko
> 
> Thank you for you review and you patient again

no problem, always nice to have more people play with Rockchip stuff on a 
mainline kernel :-)


Heiko


From heiko at sntech.de  Sat Jul  9 16:47:48 2016
From: heiko at sntech.de (Heiko Stuebner)
Date: Sun, 10 Jul 2016 01:47:48 +0200
Subject: [PATCH v6 3/5] usb: dwc3: add phyif_utmi_quirk
In-Reply-To: <f5daa697-3125-cfbf-7318-d4900f666362@rock-chips.com>
References: <1467860066-15142-1-git-send-email-william.wu@rock-chips.com>
 <87d1moi7g7.fsf@linux.intel.com>
 <f5daa697-3125-cfbf-7318-d4900f666362@rock-chips.com>
Message-ID: <4648785.SDx8KzkhqW@phil>

Am Samstag, 9. Juli 2016, 11:38:00 schrieb William.wu:
> Dear Heiko & Balbi?
> 
> On 2016/7/8 21:29, Felipe Balbi wrote:
> > Hi,
> > 
> > Heiko Stuebner <heiko at sntech.de> writes:
> >> Am Donnerstag, 7. Juli 2016, 10:54:24 schrieb William Wu:
> >>> Add a quirk to configure the core to support the
> >>> UTMI+ PHY with an 8- or 16-bit interface. UTMI+ PHY
> >>> interface is hardware property, and it's platform
> >>> dependent. Normall, the PHYIf can be configured
> >>> during coreconsultant. But for some specific usb
> >>> cores(e.g. rk3399 soc dwc3), the default PHYIf
> >>> configuration value is fault, so we need to
> >>> reconfigure it by software.
> >>> 
> >>> And refer to the dwc3 databook, the GUSB2PHYCFG.USBTRDTIM
> >>> must be set to the corresponding value according to
> >>> the UTMI+ PHY interface.
> >>> 
> >>> Signed-off-by: William Wu <william.wu at rock-chips.com>
> >>> ---
> >> 
> >> [...]
> >> 
> >>> diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt
> >>> b/Documentation/devicetree/bindings/usb/dwc3.txt index
> >>> 020b0e9..8d7317d
> >>> 100644
> >>> --- a/Documentation/devicetree/bindings/usb/dwc3.txt
> >>> +++ b/Documentation/devicetree/bindings/usb/dwc3.txt
> >>> 
> >>> @@ -42,6 +42,10 @@ Optional properties:
> >>>    - snps,dis-u2-freeclk-exists-quirk: when set, clear the
> >>> 
> >>> u2_freeclk_exists in GUSB2PHYCFG, specify that USB2 PHY doesn't
> >>> provide
> >>> 
> >>>   			a free-running PHY clock.
> >>> 
> >>> + - snps,phyif-utmi-quirk: when set core will set phyif UTMI+
> >>> interface.
> >>> + - snps,phyif-utmi: the value to configure the core to support a
> >>> UTMI+
> >>> PHY +			with an 8- or 16-bit interface. Value 0 
select 8-bit
> >>> +			interface, value 1 select 16-bit interface.
> >> 
> >> maybe
> >> 
> >> 	snps,phyif-utmi-width = <8> or <16>;
> >> 
> >> devicetree is about describing the hardware, not the things that get
> >> written to registers :-) . The conversion from the described width to
> >> the register value can easily be done in the driver.
> 
> Thanks for your suggestion:-)
> Yes? ?snps,phyif-utmi-width = <8> or <16>? is much clearer and easier to
> understand.
> And I have considered the same dts property for phyif-utmi, but I have
> no good idea about
> the conversion from described width to the registers value for the time
> being.
> 
> About phyif utmi width configuration? we need to set two places in
> GUSB2PHYCFG register?
> according to DWC3 USB3.0 controller databook version3.00a?6.3.46
> GUSB2PHYCFG
> 
> --------------------------------------------------------------------------
> -------------------- Bits   |  Name                 |     Description
> --------------------------------------------------------------------------
> -------------------- 13:10  |   USBTRDTIM       |     Sets the turnaround
> time in PHY clocks.
>              |                            |     4'h5: When the MAC
> 
> interface is 16-bit UTMI+
> 
>              |                            |     4'h9: When the MAC
> 
> interface is 8-bit UTMI+/ULPI.
> --------------------------------------------------------------------------
> -------------------- 3        |   PHYIF                |    If UTMI+ is
> selected, the application uses this bit to configure
> 
>              |                            |    core to support a UTMI+
> 
> PHY with an 8- or 16-bit interface.
> 
>              |                            |    1'b0: 8 bits
>              |                            |    1'b1: 16 bits
> 
> --------------------------------------------------------------------------
> --------------------
> 
> And I think maybe I can try to do this:
> change it in dts:
>          snps,phyif-utmi-width = <8> or <16>;
> 
> Then convert to register value like this?
>         device_property_read_u8(dev, "snps,phyif-utmi-width",
>                                               &phyif_utmi_width);
> 
>         dwc->phyif_utmi = phyif_utmi_width >> 4;
> 
>   Ater the conversion? dwc->phyif_utmi value 0 means 8 bits, value 1
> means 16 bits,
>   and it's easier for us to config GUSB2PHYCFG.
> 
> Is it OK?

or you could just store the actual width value read from the dts and make 
the core handle accordingly, making everything a bit more explicit.

I guess personally I'd do something like:

make dwc->phyif_utmi a regular unsigned int

in probe:
      	ret = device_property_read_u8(dev, "snps,phyif-utmi-width",
                                              &dwc->phyif_utmi);
	if (ret < 0) {
		dwc->phyif_utmi = 0;
	else if (dwc->phyif_utmi != 16 && dwc->phyif_utmi != 8) {
		dev_err(dev, "unsupported utmi interface width %d\n",
			dwc->phyif_utmi);
		return -EINVAL;
	}


when setting your GUSB2PHYCFG register:

       if (dwc->phyif_utmi > 0) {
               reg &= ~(DWC3_GUSB2PHYCFG_PHYIF_MASK |
                      DWC3_GUSB2PHYCFG_USBTRDTIM_MASK);
               usbtrdtim = (dwc->phyif_utmi == 16) ? USBTRDTIM_UTMI_16_BIT :
                           USBTRDTIM_UTMI_8_BIT;
		phyif = (dwc->phyif_utmi == 16) ? 1 : 0;
               reg |= DWC3_GUSB2PHYCFG_PHYIF(phyif) |
                      DWC3_GUSB2PHYCFG_USBTRDTIM(usbtrdtim) |
       }


> >> Also I don't think you need two properties for this. If the
> >> snps,phyif-utmi property is specified it indicates that you want to
> >> manually set the width and if it is absent you want to use the IC
> >> default. All functions reading property-values indicate if the
> >> property is missing.
> 
> Ah, it seems very good to me. One dts property "snps,phyif-utmi" can help
> to reconfig phyif utmi width. And it seems that Felipe likes it very much
> too. :-D

yes, but please name it snps,phyif-utmi-width :-)


Heiko


From mark.yao at rock-chips.com  Sun Jul 10 17:46:53 2016
From: mark.yao at rock-chips.com (Mark yao)
Date: Mon, 11 Jul 2016 08:46:53 +0800
Subject: [PATCH 5/7] drm/rockchip: dw-mipi: support HPD poll
In-Reply-To: <20160708145211.6bf83d8f.john@metanate.com>
References: <1467968701-15620-1-git-send-email-zyw@rock-chips.com>
 <1467968701-15620-6-git-send-email-zyw@rock-chips.com>
 <20160708145211.6bf83d8f.john@metanate.com>
Message-ID: <5782EC7D.3080202@rock-chips.com>

On 2016?07?08? 21:52, John Keeping wrote:
> On Fri,  8 Jul 2016 17:04:59 +0800, Chris Zhong wrote:
>
>> At the first time of bind, there is no any panel attach in mipi. Add a
>> DRM_CONNECTOR_POLL_HPD porperty to detect the panel status, when panel
>> probe, the dw_mipi_dsi_host_attach would be called, then mipi-dsi will
>> trigger a event to notify the drm framework.
>>
>> Signed-off-by: Chris Zhong <zyw at rock-chips.com>
> Can we do something like this instead?  We know that the panel must
> always be attached and this has the advantage that the display size will
> be known when the framebuffer console loads.
>
> -- >8 --
> Subject: [PATCH] drm/rockchip: dw-mipi-dsi: defer probe if panel is not loaded
>
> This ensures that the output resolution is known before fbcon loads.
>
> Signed-off-by: John Keeping <john at metanate.com>
> ---
>   drivers/gpu/drm/rockchip/dw-mipi-dsi.c | 11 +++++++++--
>   1 file changed, 9 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
> index 6ef5f3be8468..c0499266d116 100644
> --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
> +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
> @@ -1154,10 +1154,17 @@ static int dw_mipi_dsi_bind(struct device *dev, struct device *master,
>   
>   	dsi->dsi_host.ops = &dw_mipi_dsi_host_ops;
>   	dsi->dsi_host.dev = dev;
> -	return mipi_dsi_host_register(&dsi->dsi_host);
> +	ret = mipi_dsi_host_register(&dsi->dsi_host);
> +	if (!ret && !dsi->panel) {
> +		mipi_dsi_host_unregister(&dsi->dsi_host);
> +		drm_encoder_cleanup(&dsi->encoder);
> +		drm_connector_cleanup(&dsi->connector);
> +		ret = -EPROBE_DEFER;

Hi John

Do you verify this patch? I do the similar change before, but found 
panel can't probe.

mipi_dsi_host_register will call device_add, I think the panel probe 
need this.

Seems that mipi panel probe request mipi_dsi_host_register, 
mipi_dsi_host_register request panel on your patch, endless loop.

Thanks.

> +	}
>   
>   err_pllref:
> -	clk_disable_unprepare(dsi->pllref_clk);
> +	if (ret)
> +		clk_disable_unprepare(dsi->pllref_clk);
>   	return ret;
>   }
>   
>
>
>


-- 
?ark Yao




From zhengxing at rock-chips.com  Sun Jul 10 23:20:01 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Mon, 11 Jul 2016 14:20:01 +0800
Subject: [PATCH v6] ASoC: rockchip: Add machine driver for RK3399 GRU
 Boards
In-Reply-To: <20160620130812.GA24614@rob-hp-laptop>
References: <1466080858-29565-1-git-send-email-zhengxing@rock-chips.com>
 <20160620130812.GA24614@rob-hp-laptop>
Message-ID: <57833A91.7090109@rock-chips.com>

Hi Mark,
     In your opinion, is there anything needs to be optimized in this patch?

Thanks.

On 2016?06?20? 21:08, Rob Herring wrote:
> On Thu, Jun 16, 2016 at 08:40:58PM +0800, Xing Zheng wrote:
>> Because we need to support the multiple codecs (MAX98357A/RT5514/DA7219)
>> on the RK3399 GRU boards, this patch can help us to support these codecs.
>>
>> Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
>> ---
>>
>> Changes in v6:
>> - remove enable/disable dapm pins, and to switch codecs via user space
>> - fix "Speaker" dapm route for max98357a
>> - add SND_JACK_LINEOUT flag for da7219 jack detection
>>
>> Changes in v5:
>> - fix the build warning for missing I2C/CLKDEV_LOOKUP dependency
>>
>> Changes in v4:
>> - the compatible name needs to be based on a single reference design,
>>    and the name of the board is called "GRU"
>> - use one i2sX card "rockchip,cpu = <&i2s0>" instead of array that is
>>    based on GRU
>> - rename DOC to rockchip,rk3399-gru-sound.txt
>> - rename compatible to rockchip,rk3399-gru-sound
>> - rename source code to rk3399_gru_sound.c
>>
>> Changes in v3:
>> - rename DOC to rockchip,rk3399-max98357a-rt5514-da7219.txt
>> - rename compatible to rockchip,rk3399-max98357a-rt5514-da7219
>> - rename source code to rk3399_max98357a_rt5514_da7219.c
>>
>> Changes in v2:
>> - use the FS 256 to set mclks of the max98357a and rt5514 danamically
>> - add more sample rate for da7219
>>
>>   .../bindings/sound/rockchip,rk3399-gru-sound.txt   |   15 +
> Acked-by: Rob Herring <robh at kernel.org>
>
>>   sound/soc/rockchip/Kconfig                         |   11 +
>>   sound/soc/rockchip/Makefile                        |    2 +
>>   sound/soc/rockchip/rk3399_gru_sound.c              |  322 ++++++++++++++++++++
>>   4 files changed, 350 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/sound/rockchip,rk3399-gru-sound.txt
>>   create mode 100644 sound/soc/rockchip/rk3399_gru_sound.c
>
>

-- 
- Xing Zheng




From thierry.reding at gmail.com  Mon Jul 11 00:02:51 2016
From: thierry.reding at gmail.com (Thierry Reding)
Date: Mon, 11 Jul 2016 09:02:51 +0200
Subject: [PATCH v3 12/14] regulator: pwm: Retrieve correct voltage
In-Reply-To: <20160709094718.GB6247@sirena.org.uk>
References: <1465895602-31008-1-git-send-email-boris.brezillon@free-electrons.com>
 <1465895602-31008-13-git-send-email-boris.brezillon@free-electrons.com>
 <20160708154302.GC1151@ulmo.ba.sec>
 <20160709094718.GB6247@sirena.org.uk>
Message-ID: <20160711070251.GA5823@ulmo.ba.sec>

On Sat, Jul 09, 2016 at 11:47:18AM +0200, Mark Brown wrote:
> On Fri, Jul 08, 2016 at 05:43:02PM +0200, Thierry Reding wrote:
> 
> > Mark, do you want me to provide a stable branch with the PWM regulator
> > patches and resolve that conflict in your tree? Or would you rather take
> > the whole set based on a stable branch from the PWM tree? Or maybe yet
> > another possibility would be to base the PWM tree on a stable branch
> > from the regulator tree containing the above commit.
> 
> Probably easiest to use this signed tag and resolve it in your tree:
> 
> The following changes since commit 1a695a905c18548062509178b98bc91e67510864:
> 
>   Linux 4.7-rc1 (2016-05-29 09:29:24 -0700)
> 
> are available in the git repository at:
> 
>   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator.git tags/pwm-modernization
> 
> for you to fetch changes up to c2588393e6315ab68207323d37d2a73713d6bc81:
> 
>   regulator: pwm: Fix regulator ramp delay for continuous mode (2016-07-07 11:45:06 +0200)
> 
> ----------------------------------------------------------------
> regulator: Provide a branch for moderninzation of the PWM code
> 
> There's a new, improved PWM API which allows a lot of improvements in
> the PWM regulator driver.  Since the bulk of the changes are in the PWM
> API this is being managed in the PWM tree, merge pending regulator API
> changes to allow this to be resolved more easily.
> 
> ----------------------------------------------------------------
> Alexandre Courbot (1):
>       regulator: pwm: Support for enable GPIO
> 
> Boris Brezillon (1):
>       regulator: pwm: Drop unneeded pwm_enable() call
> 
> Douglas Anderson (1):
>       regulator: pwm: Fix regulator ramp delay for continuous mode
> 
>  .../bindings/regulator/pwm-regulator.txt           |  7 +++-
>  drivers/regulator/pwm-regulator.c                  | 40 ++++++++++++++++++----
>  2 files changed, 39 insertions(+), 8 deletions(-)

Merged into for-4.8/regulator of the PWM tree and rebased Boris'
pwm-regulator patches on top.

Boris, everything looks right to me, but can you take a quick look to
see if it all matches up with what you expect?

Thanks,
Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160711/e970b6a6/attachment.sig>

From boris.brezillon at free-electrons.com  Mon Jul 11 00:20:24 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Mon, 11 Jul 2016 09:20:24 +0200
Subject: [PATCH v3 12/14] regulator: pwm: Retrieve correct voltage
In-Reply-To: <20160711070251.GA5823@ulmo.ba.sec>
References: <1465895602-31008-1-git-send-email-boris.brezillon@free-electrons.com>
 <1465895602-31008-13-git-send-email-boris.brezillon@free-electrons.com>
 <20160708154302.GC1151@ulmo.ba.sec>
 <20160709094718.GB6247@sirena.org.uk>
 <20160711070251.GA5823@ulmo.ba.sec>
Message-ID: <20160711092024.6cbd1c88@bbrezillon>

On Mon, 11 Jul 2016 09:02:51 +0200
Thierry Reding <thierry.reding at gmail.com> wrote:

> On Sat, Jul 09, 2016 at 11:47:18AM +0200, Mark Brown wrote:
> > On Fri, Jul 08, 2016 at 05:43:02PM +0200, Thierry Reding wrote:
> >   
> > > Mark, do you want me to provide a stable branch with the PWM regulator
> > > patches and resolve that conflict in your tree? Or would you rather take
> > > the whole set based on a stable branch from the PWM tree? Or maybe yet
> > > another possibility would be to base the PWM tree on a stable branch
> > > from the regulator tree containing the above commit.  
> > 
> > Probably easiest to use this signed tag and resolve it in your tree:
> > 
> > The following changes since commit 1a695a905c18548062509178b98bc91e67510864:
> > 
> >   Linux 4.7-rc1 (2016-05-29 09:29:24 -0700)
> > 
> > are available in the git repository at:
> > 
> >   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator.git tags/pwm-modernization
> > 
> > for you to fetch changes up to c2588393e6315ab68207323d37d2a73713d6bc81:
> > 
> >   regulator: pwm: Fix regulator ramp delay for continuous mode (2016-07-07 11:45:06 +0200)
> > 
> > ----------------------------------------------------------------
> > regulator: Provide a branch for moderninzation of the PWM code
> > 
> > There's a new, improved PWM API which allows a lot of improvements in
> > the PWM regulator driver.  Since the bulk of the changes are in the PWM
> > API this is being managed in the PWM tree, merge pending regulator API
> > changes to allow this to be resolved more easily.
> > 
> > ----------------------------------------------------------------
> > Alexandre Courbot (1):
> >       regulator: pwm: Support for enable GPIO
> > 
> > Boris Brezillon (1):
> >       regulator: pwm: Drop unneeded pwm_enable() call
> > 
> > Douglas Anderson (1):
> >       regulator: pwm: Fix regulator ramp delay for continuous mode
> > 
> >  .../bindings/regulator/pwm-regulator.txt           |  7 +++-
> >  drivers/regulator/pwm-regulator.c                  | 40 ++++++++++++++++++----
> >  2 files changed, 39 insertions(+), 8 deletions(-)  
> 
> Merged into for-4.8/regulator of the PWM tree and rebased Boris'
> pwm-regulator patches on top.
> 
> Boris, everything looks right to me, but can you take a quick look to
> see if it all matches up with what you expect?

It looks good, but I won't be able to test it on real hardware until
next week.
Heiko, Brian, can you try this pwm/for-4.8/regulator branch?

Thanks,

Boris




From frank.wang at rock-chips.com  Mon Jul 11 00:43:03 2016
From: frank.wang at rock-chips.com (Frank Wang)
Date: Mon, 11 Jul 2016 15:43:03 +0800
Subject: [PATCH] arm64: dts: rockchip: support the usb2phy for rk3399 evb
In-Reply-To: <2150255.RfISFviJKP@phil>
References: <1467866800-24794-1-git-send-email-wxt@rock-chips.com>
 <2150255.RfISFviJKP@phil>
Message-ID: <cd9056c1-6fff-d02c-3fc0-3d48d9850504@rock-chips.com>

Hi Heiko,

On 2016/7/7 18:11, Heiko Stuebner wrote:
> Hi,
>
> Am Donnerstag, 7. Juli 2016, 12:46:40 schrieb Caesar Wang:
>> From: Frank Wang <frank.wang at rock-chips.com>
>>
>> This patch adds the usb2phy needed dts node information for rk3399.
>>
>> USB2.0 PHY is comprised of one Host port and one OTG port.
>> Host Port is for USB2.0 host controller; OTG port is for USB2.0 part of
>> USB3.0 OTG controller, and as a part to construct a fully feature TypeC
>> subsystem.
>>
>> The USB2.0 vbus gpio is board specific, it's no same with all rk3399
>> boards, so move it into evb voard.
>>
>> Signed-off-by: Frank Wang <frank.wang at rock-chips.com>
>> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
>> ---
>>
>>   arch/arm64/boot/dts/rockchip/rk3399-evb.dts |  4 ++++
>>   arch/arm64/boot/dts/rockchip/rk3399.dtsi    | 19 +++++++++++++++++++
>>   2 files changed, 23 insertions(+)
>>
>> diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
>> b/arch/arm64/boot/dts/rockchip/rk3399.dtsi index 4c84229..21d147f 100644
>> --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
>> +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
>> @@ -242,6 +242,25 @@
>>   		status = "disabled";
>>   	};
>>
>> +	usb2phy: usb2phy {
>> +		compatible = "rockchip,rk3399-usb-phy";
>> +		rockchip,grf = <&grf>;
>> +		#address-cells = <1>;
>> +		#size-cells = <0>;
>> +
>> +		usb2phy0: usb2-phy0 {
>> +			#phy-cells = <0>;
>> +			#clock-cells = <0>;
>> +			reg = <0xe458>;
>> +		};
>> +
>> +		usb2phy1: usb2-phy1 {
>> +			#phy-cells = <0>;
>> +			#clock-cells = <0>;
>> +			reg = <0xe468>;
>> +		};
>> +	};
>> +
> The rk3399 uses the innosilicon usb2 phy and the binding we've come up for
> it looks a lot different than the above. So I'm not sure where this is
> coming from, especially as I haven't seen the driver part for the rk3399 at
> all yet. So far Frank was always working on te rk3366 variant, which seemed
> to be the most complete.

Yeah, rk3399 is ready for using the new driver and the binding which I 
have handed out earlier, but it seems that they have not applied yet, so 
shall we send out the new patches for rk3399 until they will be applied?

BR.
Frank

> Heiko
>



From john at metanate.com  Mon Jul 11 02:20:53 2016
From: john at metanate.com (John Keeping)
Date: Mon, 11 Jul 2016 10:20:53 +0100
Subject: [PATCH 5/7] drm/rockchip: dw-mipi: support HPD poll
In-Reply-To: <5782EC7D.3080202@rock-chips.com>
References: <1467968701-15620-1-git-send-email-zyw@rock-chips.com>
 <1467968701-15620-6-git-send-email-zyw@rock-chips.com>
 <20160708145211.6bf83d8f.john@metanate.com>
 <5782EC7D.3080202@rock-chips.com>
Message-ID: <20160711102053.12b32be1.john@metanate.com>

On Mon, 11 Jul 2016 08:46:53 +0800, Mark yao wrote:

> On 2016?07?08? 21:52, John Keeping wrote:
> > On Fri,  8 Jul 2016 17:04:59 +0800, Chris Zhong wrote:
> >  
> >> At the first time of bind, there is no any panel attach in mipi. Add a
> >> DRM_CONNECTOR_POLL_HPD porperty to detect the panel status, when panel
> >> probe, the dw_mipi_dsi_host_attach would be called, then mipi-dsi will
> >> trigger a event to notify the drm framework.
> >>
> >> Signed-off-by: Chris Zhong <zyw at rock-chips.com>  
> > Can we do something like this instead?  We know that the panel must
> > always be attached and this has the advantage that the display size will
> > be known when the framebuffer console loads.
> >  
> > -- >8 --  
> > Subject: [PATCH] drm/rockchip: dw-mipi-dsi: defer probe if panel is not loaded
> >
> > This ensures that the output resolution is known before fbcon loads.
> >
> > Signed-off-by: John Keeping <john at metanate.com>
> > ---
> >   drivers/gpu/drm/rockchip/dw-mipi-dsi.c | 11 +++++++++--
> >   1 file changed, 9 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
> > index 6ef5f3be8468..c0499266d116 100644
> > --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
> > +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
> > @@ -1154,10 +1154,17 @@ static int dw_mipi_dsi_bind(struct device *dev, struct device *master,
> >   
> >   	dsi->dsi_host.ops = &dw_mipi_dsi_host_ops;
> >   	dsi->dsi_host.dev = dev;
> > -	return mipi_dsi_host_register(&dsi->dsi_host);
> > +	ret = mipi_dsi_host_register(&dsi->dsi_host);
> > +	if (!ret && !dsi->panel) {
> > +		mipi_dsi_host_unregister(&dsi->dsi_host);
> > +		drm_encoder_cleanup(&dsi->encoder);
> > +		drm_connector_cleanup(&dsi->connector);
> > +		ret = -EPROBE_DEFER;  
> 
> Do you verify this patch? I do the similar change before, but found 
> panel can't probe.
> 
> mipi_dsi_host_register will call device_add, I think the panel probe 
> need this.
> 
> Seems that mipi panel probe request mipi_dsi_host_register, 
> mipi_dsi_host_register request panel on your patch, endless loop.

Yes, I've been running this patch for months.  It's possible that I have
another patch that is needed to get this to work correctly (I have a
whole set of Rockchip MIPI patches that I need to find time to submit,
for now I've pushed a branch to Github [1]).

I don't know what you mean about the panel probe requesting
mipi_dsi_host_register, the panel probe calls mipi_dsi_attach() and is
itself called from mipi_dsi_host_register().  My patch doesn't change
this at all, the only change is that if the panel driver is not
available, then the probe fails.  Later, once the panel driver has been
registered the probe will succeed.  This is no different from the
current behaviour if the order of probing drivers happens to load the
panel before dw-mipi-dsi.

[1] https://github.com/johnkeeping/linux/commits/topic/rockchip-mipi

> > +	}
> >   
> >   err_pllref:
> > -	clk_disable_unprepare(dsi->pllref_clk);
> > +	if (ret)
> > +		clk_disable_unprepare(dsi->pllref_clk);
> >   	return ret;
> >   }
> >   
> >
> >
> >  
> 
> 


From ykk at rock-chips.com  Mon Jul 11 04:04:39 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Mon, 11 Jul 2016 19:04:39 +0800
Subject: [PATCH v1 0/6] Add RK3399 HDMI Support
Message-ID: <1468235079-29152-1-git-send-email-ykk@rock-chips.com>

Mark,

RK3399 and RK3288 shared the same HDMI IP controller, only some
light difference with GRF configure, and an external VPLL clock
need to configure.

Thanks,
- Yakir


Douglas Anderson (2):
  drm/rockchip: dw_hdmi: Set cur_ctr to 0 always
  drm/rockchip: dw_hdmi: Use auto-generated tables

Yakir Yang (4):
  drm/rockchip: dw_hdmi: adjust cklvl & txlvl for RF/EMI
  drm/rockchip: dw_hdmi: add RK3399 HDMI support
  drm/rockchip: dw_hdmi: introduce the VPLL clock setting
  drm/rockchip: dw_hdmi: introduce the pclk for grf

 .../devicetree/bindings/display/bridge/dw_hdmi.txt |   1 +
 .../bindings/display/rockchip/dw_hdmi-rockchip.txt |   7 +-
 drivers/gpu/drm/bridge/dw-hdmi.c                   |   2 +-
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c        | 238 +++++++++++++--------
 include/drm/bridge/dw_hdmi.h                       |   6 +
 5 files changed, 167 insertions(+), 87 deletions(-)

-- 
1.9.1




From ykk at rock-chips.com  Mon Jul 11 04:05:36 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Mon, 11 Jul 2016 19:05:36 +0800
Subject: [PATCH v1 1/6] drm/rockchip: dw_hdmi: Set cur_ctr to 0 always
In-Reply-To: <1468235079-29152-1-git-send-email-ykk@rock-chips.com>
References: <1468235079-29152-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1468235136-29417-1-git-send-email-ykk@rock-chips.com>

From: Douglas Anderson <dianders at chromium.org>

Jitter was improved by lowering the MPLL bandwidth to account for high
frequency noise in the rk3288 PLL.  In each case MPLL bandwidth was
lowered only enough to get us a comfortable margin.  We believe that
lowering the bandwidth like this is safe given sufficient testing.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 16 ++--------------
 1 file changed, 2 insertions(+), 14 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 801110f..a621118 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -115,20 +115,8 @@ static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
 static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = {
 	/*      pixelclk    bpp8    bpp10   bpp12 */
 	{
-		40000000,  { 0x0018, 0x0018, 0x0018 },
-	}, {
-		65000000,  { 0x0028, 0x0028, 0x0028 },
-	}, {
-		66000000,  { 0x0038, 0x0038, 0x0038 },
-	}, {
-		74250000,  { 0x0028, 0x0038, 0x0038 },
-	}, {
-		83500000,  { 0x0028, 0x0038, 0x0038 },
-	}, {
-		146250000, { 0x0038, 0x0038, 0x0038 },
-	}, {
-		148500000, { 0x0000, 0x0038, 0x0038 },
-	}, {
+		600000000, { 0x0000, 0x0000, 0x0000 },
+	},  {
 		~0UL,      { 0x0000, 0x0000, 0x0000},
 	}
 };
-- 
1.9.1




From ykk at rock-chips.com  Mon Jul 11 04:05:39 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Mon, 11 Jul 2016 19:05:39 +0800
Subject: [PATCH v1 2/6] drm/rockchip: dw_hdmi: adjust cklvl & txlvl for RF/EMI
In-Reply-To: <1468235079-29152-1-git-send-email-ykk@rock-chips.com>
References: <1468235079-29152-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1468235139-29460-1-git-send-email-ykk@rock-chips.com>

Dut to the high HDMI signal voltage driver, Mickey have meet
a serious RF/EMI problem, so we decided to reduce HDMI signal
voltage to a proper value.

The default params for phy is cklvl = 20 & txlvl = 13 (RF/EMI failed)
  ck: lvl = 13, term=100, vlo = 2.71, vhi=3.14, vswing = 0.43
  tx: lvl = 20, term=100, vlo = 2.81, vhi=3.16, vswing = 0.35

1. We decided to reduce voltage value to lower, but VSwing still
keep high, RF/EMI have been improved but still failed.
   ck: lvl =  6, term=100, vlo = 2.61, vhi=3.11, vswing = 0.50
   tx: lvl =  6, term=100, vlo = 2.61, vhi=3.11, vswing = 0.50

2. We try to keep voltage value and vswing both lower, then RF/EMI
test all passed  ;)
   ck: lvl = 11, term= 66, vlo = 2.68, vhi=3.09, vswing = 0.40
   tx: lvl = 11, term= 66, vlo = 2.68, vhi=3.09, vswing = 0.40
When we back to run HDMI different test and single-end test, we see
different test passed, but signle-end test failed. The oscilloscope
show that simgle-end clock's VL value is 1.78v (which remind LowLimit
should not lower then 2.6v).

3. That's to say there are some different between PHY document and
measure value. And according to experiment 2 results, we need to
higher clock voltage and lower data voltage, then we can keep RF/EMI
satisfied and single-end & differen test passed.
  ck: lvl =  9, term=100, vlo = 2.65, vhi=3.12, vswing = 0.47
  tx: lvl = 16, term=100, vlo = 2.75, vhi=3.15, vswing = 0.39

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
Reviewed-by: Douglas Anderson <dianders at chromium.org>
---
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index a621118..cdb63ac 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -124,7 +124,7 @@ static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = {
 static const struct dw_hdmi_phy_config rockchip_phy_config[] = {
 	/*pixelclk   symbol   term   vlev*/
 	{ 74250000,  0x8009, 0x0004, 0x0272},
-	{ 148500000, 0x802b, 0x0004, 0x028d},
+	{ 165000000, 0x802b, 0x0004, 0x0209},
 	{ 297000000, 0x8039, 0x0005, 0x028d},
 	{ ~0UL,	     0x0000, 0x0000, 0x0000}
 };
-- 
1.9.1




From ykk at rock-chips.com  Mon Jul 11 04:05:42 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Mon, 11 Jul 2016 19:05:42 +0800
Subject: [PATCH v1 3/6] drm/rockchip: dw_hdmi: Use auto-generated tables
In-Reply-To: <1468235079-29152-1-git-send-email-ykk@rock-chips.com>
References: <1468235079-29152-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1468235142-29503-1-git-send-email-ykk@rock-chips.com>

From: Douglas Anderson <dianders at chromium.org>

The previous tables for mpll_cfg and curr_ctrl were created using the
20-pages of example settings provided by the PHY vendor.  Those
example settings weren't particularly dense, so there were places
where we were guessing what the settings would be for 10-bit and
12-bit (not that we use those anyway).  It was also always a lot of
extra work every time we wanted to add a new clock rate since we had
to cross-reference several tables.

In <http://crosreview.com/285855> I've gone through the work to figure
out how to generate this table automatically.  Let's now use the
automatically generated table and then we'll never need to look at it
again.

We only support 8-bit mode right now and only support a small number
of clock rates and and I've verified that the only 8-bit rate that was
affected was 148.5.  That mode appears to have been wrong in the old
table.

Signed-off-by: Douglas Anderson <dianders at chromium.org>
Signed-off-by: Yakir Yang <ykk at rock-chips.com>
Reviewed-by: St?phane Marchesin <marcheu at chromium.org>
---
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 130 +++++++++++++++-------------
 1 file changed, 69 insertions(+), 61 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index cdb63ac..8cb9ed2 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -34,80 +34,88 @@ struct rockchip_hdmi {
 
 static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = {
 	{
-		27000000, {
-			{ 0x00b3, 0x0000},
-			{ 0x2153, 0x0000},
-			{ 0x40f3, 0x0000}
+		30666000, {
+			{ 0x00b3, 0x0000 },
+			{ 0x2153, 0x0000 },
+			{ 0x40f3, 0x0000 },
 		},
-	}, {
-		36000000, {
-			{ 0x00b3, 0x0000},
-			{ 0x2153, 0x0000},
-			{ 0x40f3, 0x0000}
+	},  {
+		36800000, {
+			{ 0x00b3, 0x0000 },
+			{ 0x2153, 0x0000 },
+			{ 0x40a2, 0x0001 },
 		},
-	}, {
-		40000000, {
-			{ 0x00b3, 0x0000},
-			{ 0x2153, 0x0000},
-			{ 0x40f3, 0x0000}
+	},  {
+		46000000, {
+			{ 0x00b3, 0x0000 },
+			{ 0x2142, 0x0001 },
+			{ 0x40a2, 0x0001 },
 		},
-	}, {
-		54000000, {
-			{ 0x0072, 0x0001},
-			{ 0x2142, 0x0001},
-			{ 0x40a2, 0x0001},
+	},  {
+		61333000, {
+			{ 0x0072, 0x0001 },
+			{ 0x2142, 0x0001 },
+			{ 0x40a2, 0x0001 },
 		},
-	}, {
-		65000000, {
-			{ 0x0072, 0x0001},
-			{ 0x2142, 0x0001},
-			{ 0x40a2, 0x0001},
+	},  {
+		73600000, {
+			{ 0x0072, 0x0001 },
+			{ 0x2142, 0x0001 },
+			{ 0x4061, 0x0002 },
 		},
-	}, {
-		66000000, {
-			{ 0x013e, 0x0003},
-			{ 0x217e, 0x0002},
-			{ 0x4061, 0x0002}
+	},  {
+		92000000, {
+			{ 0x0072, 0x0001 },
+			{ 0x2145, 0x0002 },
+			{ 0x4061, 0x0002 },
+		},
+	},  {
+		122666000, {
+			{ 0x0051, 0x0002 },
+			{ 0x2145, 0x0002 },
+			{ 0x4061, 0x0002 },
 		},
-	}, {
-		74250000, {
-			{ 0x0072, 0x0001},
-			{ 0x2145, 0x0002},
-			{ 0x4061, 0x0002}
+	},  {
+		147200000, {
+			{ 0x0051, 0x0002 },
+			{ 0x2145, 0x0002 },
+			{ 0x4064, 0x0003 },
 		},
-	}, {
-		83500000, {
-			{ 0x0072, 0x0001},
+	},  {
+		184000000, {
+			{ 0x0051, 0x0002 },
+			{ 0x214c, 0x0003 },
+			{ 0x4064, 0x0003 },
 		},
-	}, {
-		108000000, {
-			{ 0x0051, 0x0002},
-			{ 0x2145, 0x0002},
-			{ 0x4061, 0x0002}
+	},  {
+		226666000, {
+			{ 0x0040, 0x0003 },
+			{ 0x214c, 0x0003 },
+			{ 0x4064, 0x0003 },
 		},
-	}, {
-		106500000, {
-			{ 0x0051, 0x0002},
-			{ 0x2145, 0x0002},
-			{ 0x4061, 0x0002}
+	},  {
+		272000000, {
+			{ 0x0040, 0x0003 },
+			{ 0x214c, 0x0003 },
+			{ 0x5a64, 0x0003 },
 		},
-	}, {
-		146250000, {
-			{ 0x0051, 0x0002},
-			{ 0x2145, 0x0002},
-			{ 0x4061, 0x0002}
+	},  {
+		340000000, {
+			{ 0x0040, 0x0003 },
+			{ 0x3b4c, 0x0003 },
+			{ 0x5a64, 0x0003 },
 		},
-	}, {
-		148500000, {
-			{ 0x0051, 0x0003},
-			{ 0x214c, 0x0003},
-			{ 0x4064, 0x0003}
+	},  {
+		600000000, {
+			{ 0x1a40, 0x0003 },
+			{ 0x3b4c, 0x0003 },
+			{ 0x5a64, 0x0003 },
 		},
-	}, {
+	},  {
 		~0UL, {
-			{ 0x00a0, 0x000a },
-			{ 0x2001, 0x000f },
-			{ 0x4002, 0x000f },
+			{ 0x0000, 0x0000 },
+			{ 0x0000, 0x0000 },
+			{ 0x0000, 0x0000 },
 		},
 	}
 };
-- 
1.9.1




From ykk at rock-chips.com  Mon Jul 11 04:05:46 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Mon, 11 Jul 2016 19:05:46 +0800
Subject: [PATCH v1 4/6] drm/rockchip: dw_hdmi: add RK3399 HDMI support
In-Reply-To: <1468235079-29152-1-git-send-email-ykk@rock-chips.com>
References: <1468235079-29152-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1468235146-29575-1-git-send-email-ykk@rock-chips.com>

RK3399 and RK3288 shared the same HDMI IP controller, only some light
difference with GRF configure.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
 .../devicetree/bindings/display/bridge/dw_hdmi.txt |  1 +
 .../bindings/display/rockchip/dw_hdmi-rockchip.txt |  3 +-
 drivers/gpu/drm/bridge/dw-hdmi.c                   |  2 +-
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c        | 45 ++++++++++++++++++----
 include/drm/bridge/dw_hdmi.h                       |  6 +++
 5 files changed, 48 insertions(+), 9 deletions(-)

diff --git a/Documentation/devicetree/bindings/display/bridge/dw_hdmi.txt b/Documentation/devicetree/bindings/display/bridge/dw_hdmi.txt
index dc1452f..f50d4d5 100644
--- a/Documentation/devicetree/bindings/display/bridge/dw_hdmi.txt
+++ b/Documentation/devicetree/bindings/display/bridge/dw_hdmi.txt
@@ -6,6 +6,7 @@ Required properties:
    * "fsl,imx6q-hdmi"
    * "fsl,imx6dl-hdmi"
    * "rockchip,rk3288-dw-hdmi"
+   * "rockchip,rk3399-dw-hdmi"
 - reg: Physical base address and length of the controller's registers.
 - interrupts: The HDMI interrupt number
 - clocks, clock-names : must have the phandles to the HDMI iahb and isfr clocks,
diff --git a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
index 668091f..4e573d2 100644
--- a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
+++ b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
@@ -2,7 +2,8 @@ Rockchip specific extensions to the Synopsys Designware HDMI
 ================================
 
 Required properties:
-- compatible: "rockchip,rk3288-dw-hdmi";
+- compatible: "rockchip,rk3288-dw-hdmi",
+	      "rockchip,rk3399-dw-hdmi";
 - reg: Physical base address and length of the controller's registers.
 - clocks: phandle to hdmi iahb and isfr clocks.
 - clock-names: should be "iahb" "isfr"
diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c
index 70b1f7d..29f855a 100644
--- a/drivers/gpu/drm/bridge/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/dw-hdmi.c
@@ -833,7 +833,7 @@ 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 (is_rockchip(hdmi->dev_type))
 		dw_hdmi_phy_enable_spare(hdmi, 1);
 
 	/*Wait for PHY PLL lock */
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 8cb9ed2..329099b 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -21,13 +21,18 @@
 #include "rockchip_drm_drv.h"
 #include "rockchip_drm_vop.h"
 
-#define GRF_SOC_CON6                    0x025c
-#define HDMI_SEL_VOP_LIT                (1 << 4)
+#define RK3288_GRF_SOC_CON6		0x025C
+#define RK3288_HDMI_LCDC_SEL		BIT(4)
+#define RK3399_GRF_SOC_CON20		0x6250
+#define RK3399_HDMI_LCDC_SEL		BIT(6)
+
+#define HIWORD_UPDATE(val, mask)	(val | (mask) << 16)
 
 struct rockchip_hdmi {
 	struct device *dev;
 	struct regmap *regmap;
 	struct drm_encoder encoder;
+	enum dw_hdmi_devtype dev_type;
 };
 
 #define to_rockchip_hdmi(x)	container_of(x, struct rockchip_hdmi, x)
@@ -194,16 +199,30 @@ static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
 static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
 {
 	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
+	u32 lcdsel_grf_reg, lcdsel_mask;
 	u32 val;
 	int mux;
 
+	switch (hdmi->dev_type) {
+	case RK3288_HDMI:
+		lcdsel_grf_reg = RK3288_GRF_SOC_CON6;
+		lcdsel_mask = RK3288_HDMI_LCDC_SEL;
+		break;
+	case RK3399_HDMI:
+		lcdsel_grf_reg = RK3399_GRF_SOC_CON20;
+		lcdsel_mask = RK3399_HDMI_LCDC_SEL;
+		break;
+	default:
+		return;
+	};
+
 	mux = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder);
 	if (mux)
-		val = HDMI_SEL_VOP_LIT | (HDMI_SEL_VOP_LIT << 16);
+		val = HIWORD_UPDATE(lcdsel_mask, lcdsel_mask);
 	else
-		val = HDMI_SEL_VOP_LIT << 16;
+		val = HIWORD_UPDATE(0, lcdsel_mask);
 
-	regmap_write(hdmi->regmap, GRF_SOC_CON6, val);
+	regmap_write(hdmi->regmap, lcdsel_grf_reg, val);
 	dev_dbg(hdmi->dev, "vop %s output to hdmi\n",
 		(mux) ? "LIT" : "BIG");
 }
@@ -229,7 +248,7 @@ static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_fun
 	.atomic_check = dw_hdmi_rockchip_encoder_atomic_check,
 };
 
-static const struct dw_hdmi_plat_data rockchip_hdmi_drv_data = {
+static const struct dw_hdmi_plat_data rk3288_hdmi_drv_data = {
 	.mode_valid = dw_hdmi_rockchip_mode_valid,
 	.mpll_cfg   = rockchip_mpll_cfg,
 	.cur_ctr    = rockchip_cur_ctr,
@@ -237,9 +256,20 @@ static const struct dw_hdmi_plat_data rockchip_hdmi_drv_data = {
 	.dev_type   = RK3288_HDMI,
 };
 
+static const struct dw_hdmi_plat_data rk3399_hdmi_drv_data = {
+	.mode_valid = dw_hdmi_rockchip_mode_valid,
+	.mpll_cfg   = rockchip_mpll_cfg,
+	.cur_ctr    = rockchip_cur_ctr,
+	.phy_config = rockchip_phy_config,
+	.dev_type   = RK3399_HDMI,
+};
+
 static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = {
 	{ .compatible = "rockchip,rk3288-dw-hdmi",
-	  .data = &rockchip_hdmi_drv_data
+	  .data = &rk3288_hdmi_drv_data
+	},
+	{ .compatible = "rockchip,rk3399-dw-hdmi",
+	  .data = &rk3399_hdmi_drv_data
 	},
 	{},
 };
@@ -268,6 +298,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
 	match = of_match_node(dw_hdmi_rockchip_dt_ids, pdev->dev.of_node);
 	plat_data = match->data;
 	hdmi->dev = &pdev->dev;
+	hdmi->dev_type = plat_data->dev_type;
 	encoder = &hdmi->encoder;
 
 	irq = platform_get_irq(pdev, 0);
diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
index bae79f3..26c06d5 100644
--- a/include/drm/bridge/dw_hdmi.h
+++ b/include/drm/bridge/dw_hdmi.h
@@ -25,6 +25,7 @@ enum dw_hdmi_devtype {
 	IMX6Q_HDMI,
 	IMX6DL_HDMI,
 	RK3288_HDMI,
+	RK3399_HDMI,
 };
 
 struct dw_hdmi_mpll_config {
@@ -56,6 +57,11 @@ struct dw_hdmi_plat_data {
 					   struct drm_display_mode *mode);
 };
 
+static inline bool is_rockchip(enum dw_hdmi_devtype dev_type)
+{
+	return dev_type == RK3288_HDMI || dev_type == RK3399_HDMI;
+}
+
 void dw_hdmi_unbind(struct device *dev, struct device *master, void *data);
 int dw_hdmi_bind(struct device *dev, struct device *master,
 		 void *data, struct drm_encoder *encoder,
-- 
1.9.1




From ykk at rock-chips.com  Mon Jul 11 04:05:49 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Mon, 11 Jul 2016 19:05:49 +0800
Subject: [PATCH v1 5/6] drm/rockchip: dw_hdmi: introduce the VPLL clock setting
In-Reply-To: <1468235079-29152-1-git-send-email-ykk@rock-chips.com>
References: <1468235079-29152-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1468235149-29625-1-git-send-email-ykk@rock-chips.com>

For RK3399 HDMI, there is an external clock need for HDMI PHY,
and it should keep the same clock rate with VOP DCLK.

VPLL have supported the clock for HDMI PHY, but there is no
clock divider bewteen VPLL and HDMI PHY. So we need to set the
VPLL rate manually in HDMI driver.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
 .../bindings/display/rockchip/dw_hdmi-rockchip.txt |  3 ++-
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c        | 25 +++++++++++++++++++++-
 2 files changed, 26 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
index 4e573d2..4e23ca4 100644
--- a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
+++ b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
@@ -17,7 +17,8 @@ Required properties:
 
 Optional properties
 - ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing
-- clocks, clock-names: phandle to the HDMI CEC clock, name should be "cec"
+- clocks, clock-names: phandle to the HDMI CEC clock, name should be "cec",
+		       phandle to the VPLL clock, name should be "vpll".
 
 Example:
 hdmi: hdmi at ff980000 {
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 329099b..701bb73 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -7,10 +7,12 @@
  * (at your option) any later version.
  */
 
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
-#include <linux/mfd/syscon.h>
 #include <linux/regmap.h>
+
 #include <drm/drm_of.h>
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
@@ -33,6 +35,7 @@ struct rockchip_hdmi {
 	struct regmap *regmap;
 	struct drm_encoder encoder;
 	enum dw_hdmi_devtype dev_type;
+	struct clk *vpll_clk;
 };
 
 #define to_rockchip_hdmi(x)	container_of(x, struct rockchip_hdmi, x)
@@ -145,6 +148,7 @@ static const struct dw_hdmi_phy_config rockchip_phy_config[] = {
 static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 {
 	struct device_node *np = hdmi->dev->of_node;
+	int ret;
 
 	hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
 	if (IS_ERR(hdmi->regmap)) {
@@ -152,6 +156,22 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 		return PTR_ERR(hdmi->regmap);
 	}
 
+	hdmi->vpll_clk = devm_clk_get(hdmi->dev, "vpll");
+	if (PTR_ERR(hdmi->vpll_clk) == -ENOENT) {
+		hdmi->vpll_clk = NULL;
+	} else if (PTR_ERR(hdmi->vpll_clk) == -EPROBE_DEFER) {
+		return -EPROBE_DEFER;
+	} else if (IS_ERR(hdmi->vpll_clk)) {
+		dev_err(hdmi->dev, "failed to get grf clock\n");
+		return PTR_ERR(hdmi->vpll_clk);
+	}
+
+	ret = clk_prepare_enable(hdmi->vpll_clk);
+	if (ret) {
+		dev_err(hdmi->dev, "Failed to enable HDMI vpll: %d\n", ret);
+		return ret;
+	}
+
 	return 0;
 }
 
@@ -194,6 +214,9 @@ static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder,
 					      struct drm_display_mode *mode,
 					      struct drm_display_mode *adj_mode)
 {
+	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
+
+	clk_set_rate(hdmi->vpll_clk, adj_mode->clock * 1000);
 }
 
 static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
-- 
1.9.1




From ykk at rock-chips.com  Mon Jul 11 04:05:52 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Mon, 11 Jul 2016 19:05:52 +0800
Subject: [PATCH v1 6/6] drm/rockchip: dw_hdmi: introduce the pclk for grf
In-Reply-To: <1468235079-29152-1-git-send-email-ykk@rock-chips.com>
References: <1468235079-29152-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1468235152-29683-1-git-send-email-ykk@rock-chips.com>

For RK3399's GRF module, if we want to operate the graphic related grf
registers, we need to enable the pclk_vio_grf which supply power for VIO
GRF IOs, so it's better to introduce an optional grf clock in driver.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
 .../bindings/display/rockchip/dw_hdmi-rockchip.txt   |  3 ++-
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c          | 20 ++++++++++++++++++++
 2 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
index 4e23ca4..e22d70f 100644
--- a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
+++ b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
@@ -18,7 +18,8 @@ Required properties:
 Optional properties
 - ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing
 - clocks, clock-names: phandle to the HDMI CEC clock, name should be "cec",
-		       phandle to the VPLL clock, name should be "vpll".
+		       phandle to the VPLL clock, name should be "vpll",
+		       phandle to the GRF clock, name should be "grf".
 
 Example:
 hdmi: hdmi at ff980000 {
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 701bb73..69e6efb 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -36,6 +36,7 @@ struct rockchip_hdmi {
 	struct drm_encoder encoder;
 	enum dw_hdmi_devtype dev_type;
 	struct clk *vpll_clk;
+	struct clk *grf_clk;
 };
 
 #define to_rockchip_hdmi(x)	container_of(x, struct rockchip_hdmi, x)
@@ -166,6 +167,16 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi)
 		return PTR_ERR(hdmi->vpll_clk);
 	}
 
+	hdmi->grf_clk = devm_clk_get(hdmi->dev, "grf");
+	if (PTR_ERR(hdmi->grf_clk) == -ENOENT) {
+		hdmi->grf_clk = NULL;
+	} else if (PTR_ERR(hdmi->grf_clk) == -EPROBE_DEFER) {
+		return -EPROBE_DEFER;
+	} else if (IS_ERR(hdmi->grf_clk)) {
+		dev_err(hdmi->dev, "failed to get grf clock\n");
+		return PTR_ERR(hdmi->grf_clk);
+	}
+
 	ret = clk_prepare_enable(hdmi->vpll_clk);
 	if (ret) {
 		dev_err(hdmi->dev, "Failed to enable HDMI vpll: %d\n", ret);
@@ -225,6 +236,7 @@ static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
 	u32 lcdsel_grf_reg, lcdsel_mask;
 	u32 val;
 	int mux;
+	int ret;
 
 	switch (hdmi->dev_type) {
 	case RK3288_HDMI:
@@ -245,9 +257,17 @@ static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
 	else
 		val = HIWORD_UPDATE(0, lcdsel_mask);
 
+	ret = clk_prepare_enable(hdmi->grf_clk);
+	if (ret < 0) {
+		dev_err(hdmi->dev, "failed to enable grfclk %d\n", ret);
+		return;
+	}
+
 	regmap_write(hdmi->regmap, lcdsel_grf_reg, val);
 	dev_dbg(hdmi->dev, "vop %s output to hdmi\n",
 		(mux) ? "LIT" : "BIG");
+
+	clk_disable_unprepare(hdmi->grf_clk);
 }
 
 static int
-- 
1.9.1




From p.zabel at pengutronix.de  Mon Jul 11 04:51:52 2016
From: p.zabel at pengutronix.de (Philipp Zabel)
Date: Mon, 11 Jul 2016 13:51:52 +0200
Subject: [PATCH v1 4/6] drm/rockchip: dw_hdmi: add RK3399 HDMI support
In-Reply-To: <1468235146-29575-1-git-send-email-ykk@rock-chips.com>
References: <1468235079-29152-1-git-send-email-ykk@rock-chips.com>
 <1468235146-29575-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1468237912.3048.26.camel@pengutronix.de>

Am Montag, den 11.07.2016, 19:05 +0800 schrieb Yakir Yang:
> RK3399 and RK3288 shared the same HDMI IP controller, only some light
> difference with GRF configure.
> 
> Signed-off-by: Yakir Yang <ykk at rock-chips.com>

Reviewed-by: Philipp Zabel <p.zabel at pengutronix.de>

regards
Philipp



From cwchoi00 at gmail.com  Mon Jul 11 07:30:38 2016
From: cwchoi00 at gmail.com (Chanwoo Choi)
Date: Mon, 11 Jul 2016 23:30:38 +0900
Subject: [v3 PATCH 1/5] extcon: Add Type-C and DP support
In-Reply-To: <CABXOdTc27oW3xyeBZbks2r19qZRNDDAVcdOVJWgMff+59vpBPg@mail.gmail.com>
References: <1466686264-6744-1-git-send-email-zyw@rock-chips.com>
 <1466686264-6744-2-git-send-email-zyw@rock-chips.com>
 <CAGTfZH1aKUQH2VH+biyh9pFfU93pV9HH3h5s9zO0rrvXJYCMdA@mail.gmail.com>
 <CABXOdTdHhWTSF6H2D5ubaZsu65dnGsof9NWAFNeWjCir=H91Ag@mail.gmail.com>
 <CAGTfZH1ciu33btOAkwwA+8pUV=5-XDus-Q_wNiv_hwRoLK5dPw@mail.gmail.com>
 <CABXOdTc27oW3xyeBZbks2r19qZRNDDAVcdOVJWgMff+59vpBPg@mail.gmail.com>
Message-ID: <CAGTfZH0N6p4_GqZKmJfuAzpjmB88AnFR46=x5bpCafx1PaHkVw@mail.gmail.com>

Hi Guenter,

2016-07-07 5:22 GMT+09:00 Guenter Roeck <groeck at google.com>:
> Hi,
>
> On Tue, Jun 28, 2016 at 6:40 PM, Chanwoo Choi <cwchoi00 at gmail.com> wrote:
>> Hi Guenter,
>>
>> 2016? 6? 29? ???, Guenter Roeck<groeck at google.com>?? ??? ???:
>>>
>>> On Tue, Jun 28, 2016 at 5:26 AM, Chanwoo Choi <cwchoi00 at gmail.com> wrote:
>>> > Hi Chris,
>>> >
>>> > I agree to add the new EXTCON_DISP_DP connector.
>>> > But, other new definition should be discussed.
>>> > - EXTCON_DISP_DP_ALT
>>> > - EXTCON_TYPEC_POLARITY
>>> > - EXTCON_TYPEC_PIN_ASSIGN
>>> >
>>> > I think that TYPEC_POLARITY and TYPEC_PIN_ASSING are not appropriate
>>> > as the new external connector definition. These are the property or
>>> > attribute of
>>> > USB connector with C-type.
>>> >
>>> > Also, EXTCON_DISP_DP_ALT is not a new type of connector.
>>> > It is just one of the mode for DP connector.
>>> >
>>> > As I knew, DP alternative mode use the USB connector with C-type.
>>> > So, DP alternative mode can be expressed on following:
>>> >  = EXTCON_DISP_DP + EXTCON_USB + some property of USB c-type
>>> >
>>>
>>> Problem is that extcon doesn't support exchanging cable properties
>>> between cable providers (extcon drivers) and consumers, other than
>>> cable states. In order to exchange properties such as polarity and pin
>>> assignments, we would need a separate infrastructure. But then the
>>> question would be why to use extcon in the first place.
>>>
>>> If you have a solution for that puzzle, please let us know.
>>>
>>
>> You're right.
>> Current extcon don't support the cable properties.
>> The requirement about cables properties occur such as USB ID and VBUS pin.
>> So, I'll support the cable properties in extcon framework and send the
>> patches within this week.
>>
>
> Did you have time to look into this ? Any idea if/when you will send
> the patches ?

Firstly, I'm sorry for late reply.

I'm developing the suggested idea on extcon-test branch[1]. But, I
have not yet completed.
There are issues about compatibility of existing notifier of extcon.

It[1] is not final version. I need more time to make the well design
for extcon property.

[1] https://git.kernel.org/cgit/linux/kernel/git/chanwoo/extcon.git/log/?h=extcon-test
- usb: dwc3: omap: Support the changed method to get the state of connector
- usb: chipdata: Support the changed method to get the state of connector
- extcon: Add the support for extcon property according to type of connector
- extcon: Add the extcon_type to group each connector into five category

Thanks,
Chanwoo Choi


From robh at kernel.org  Mon Jul 11 07:34:14 2016
From: robh at kernel.org (Rob Herring)
Date: Mon, 11 Jul 2016 09:34:14 -0500
Subject: [PATCH 1/3] net: stmmac: dwmac-rk: add rk3366 & rk3399-specific
 data
In-Reply-To: <1467802291-6487-2-git-send-email-roger.chen@rock-chips.com>
References: <1467802291-6487-1-git-send-email-roger.chen@rock-chips.com>
 <1467802291-6487-2-git-send-email-roger.chen@rock-chips.com>
Message-ID: <20160711143414.GA8707@rob-hp-laptop>

On Wed, Jul 06, 2016 at 06:51:29PM +0800, Roger Chen wrote:
> Add constants and callback functions for the dwmac on rk3368 socs.
> As can be seen, the base structure is the same, only registers and
> the bits in them moved slightly.
> 
> Signed-off-by: Roger Chen <roger.chen at rock-chips.com>
> ---
>  .../devicetree/bindings/net/rockchip-dwmac.txt     |   3 +-

Acked-by: Rob Herring <robh at kernel.org>

>  drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c     | 226 +++++++++++++++++++++
>  2 files changed, 228 insertions(+), 1 deletion(-)


From robh at kernel.org  Mon Jul 11 07:37:16 2016
From: robh at kernel.org (Rob Herring)
Date: Mon, 11 Jul 2016 09:37:16 -0500
Subject: [PATCH v2] drivers/perf: arm-pmu: Handle per-interrupt affinity
 mask
In-Reply-To: <1467815627-4061-1-git-send-email-marc.zyngier@arm.com>
References: <1467815627-4061-1-git-send-email-marc.zyngier@arm.com>
Message-ID: <20160711143716.GA9267@rob-hp-laptop>

On Wed, Jul 06, 2016 at 03:33:47PM +0100, Marc Zyngier wrote:
> On a big-little system, PMUs can be wired to CPUs using per CPU
> interrups (PPI). In this case, it is important to make sure that
> the enable/disable do happen on the right set of CPUs.
> 
> So instead of relying on the interrupt-affinity property, we can
> use the actual percpu affinity that DT exposes as part of the
> interrupt specifier. The DT binding is also updated to reflect
> the fact that the interrupt-affinity property shouldn't be used
> in that case.
> 
> Signed-off-by: Marc Zyngier <marc.zyngier at arm.com>
> ---
> * From v1:
>   - propagate the error if irq_get_percpu_devid_partition fails
> 
>  Documentation/devicetree/bindings/arm/pmu.txt |  4 +++-

I acked v1, please add acks.

Rob

>  drivers/perf/arm_pmu.c                        | 27 ++++++++++++++++++++++-----
>  2 files changed, 25 insertions(+), 6 deletions(-)


From will.deacon at arm.com  Mon Jul 11 07:47:06 2016
From: will.deacon at arm.com (Will Deacon)
Date: Mon, 11 Jul 2016 15:47:06 +0100
Subject: [PATCH v2] drivers/perf: arm-pmu: Handle per-interrupt affinity
 mask
In-Reply-To: <20160711143716.GA9267@rob-hp-laptop>
References: <1467815627-4061-1-git-send-email-marc.zyngier@arm.com>
 <20160711143716.GA9267@rob-hp-laptop>
Message-ID: <20160711144706.GA27013@arm.com>

On Mon, Jul 11, 2016 at 09:37:16AM -0500, Rob Herring wrote:
> On Wed, Jul 06, 2016 at 03:33:47PM +0100, Marc Zyngier wrote:
> > On a big-little system, PMUs can be wired to CPUs using per CPU
> > interrups (PPI). In this case, it is important to make sure that
> > the enable/disable do happen on the right set of CPUs.
> > 
> > So instead of relying on the interrupt-affinity property, we can
> > use the actual percpu affinity that DT exposes as part of the
> > interrupt specifier. The DT binding is also updated to reflect
> > the fact that the interrupt-affinity property shouldn't be used
> > in that case.
> > 
> > Signed-off-by: Marc Zyngier <marc.zyngier at arm.com>
> > ---
> > * From v1:
> >   - propagate the error if irq_get_percpu_devid_partition fails
> > 
> >  Documentation/devicetree/bindings/arm/pmu.txt |  4 +++-
> 
> I acked v1, please add acks.

This is queued in arm64/for-next/core, but I spotted your Ack and added
it when I sent the patch to Catalin.

Will


From robh at kernel.org  Mon Jul 11 07:47:56 2016
From: robh at kernel.org (Rob Herring)
Date: Mon, 11 Jul 2016 09:47:56 -0500
Subject: [PATCH v6 2/5] usb: dwc3: add dis_u2_freeclk_exists_quirk
In-Reply-To: <1467860066-15142-3-git-send-email-william.wu@rock-chips.com>
References: <1467860066-15142-1-git-send-email-william.wu@rock-chips.com>
 <1467860066-15142-3-git-send-email-william.wu@rock-chips.com>
Message-ID: <20160711144756.GA14260@rob-hp-laptop>

On Thu, Jul 07, 2016 at 10:54:23AM +0800, William Wu wrote:
> Add a quirk to clear the GUSB2PHYCFG.U2_FREECLK_EXISTS bit,
> which specifies whether the USB2.0 PHY provides a free-running
> PHY clock, which is active when the clock control input is active.
> 
> Signed-off-by: William Wu <william.wu at rock-chips.com>
> ---
> Changes in v6:
> - use '-' instead of '_' in dts (Rob Herring)
> 
> Changes in v5:
> - None
> 
> Changes in v4:
> - rebase on top of balbi testing/next, remove pdata (balbi)
> 
> Changes in v3:
> - None
> 
> Changes in v2:
> - None
> 
>  Documentation/devicetree/bindings/usb/dwc3.txt | 3 +++
>  drivers/usb/dwc3/core.c                        | 5 +++++
>  drivers/usb/dwc3/core.h                        | 5 +++++
>  3 files changed, 13 insertions(+)

Acked-by: Rob Herring <robh at kernel.org>


From robh at kernel.org  Mon Jul 11 07:54:47 2016
From: robh at kernel.org (Rob Herring)
Date: Mon, 11 Jul 2016 09:54:47 -0500
Subject: [PATCH v6 3/5] usb: dwc3: add phyif_utmi_quirk
In-Reply-To: <2213342.0Nx5tnEW8p@phil>
References: <1467860066-15142-1-git-send-email-william.wu@rock-chips.com>
 <1467860066-15142-4-git-send-email-william.wu@rock-chips.com>
 <2213342.0Nx5tnEW8p@phil>
Message-ID: <20160711145447.GA16636@rob-hp-laptop>

On Fri, Jul 08, 2016 at 02:33:09PM +0200, Heiko Stuebner wrote:
> Hi William,
> 
> Am Donnerstag, 7. Juli 2016, 10:54:24 schrieb William Wu:
> > Add a quirk to configure the core to support the
> > UTMI+ PHY with an 8- or 16-bit interface. UTMI+ PHY
> > interface is hardware property, and it's platform
> > dependent. Normall, the PHYIf can be configured
> > during coreconsultant. But for some specific usb
> > cores(e.g. rk3399 soc dwc3), the default PHYIf
> > configuration value is fault, so we need to
> > reconfigure it by software.
> > 
> > And refer to the dwc3 databook, the GUSB2PHYCFG.USBTRDTIM
> > must be set to the corresponding value according to
> > the UTMI+ PHY interface.
> > 
> > Signed-off-by: William Wu <william.wu at rock-chips.com>
> > ---
> [...]
> > diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt
> > b/Documentation/devicetree/bindings/usb/dwc3.txt index 020b0e9..8d7317d
> > 100644
> > --- a/Documentation/devicetree/bindings/usb/dwc3.txt
> > +++ b/Documentation/devicetree/bindings/usb/dwc3.txt
> > @@ -42,6 +42,10 @@ Optional properties:
> >   - snps,dis-u2-freeclk-exists-quirk: when set, clear the
> > u2_freeclk_exists in GUSB2PHYCFG, specify that USB2 PHY doesn't provide
> >  			a free-running PHY clock.
> > + - snps,phyif-utmi-quirk: when set core will set phyif UTMI+ interface.
> > + - snps,phyif-utmi: the value to configure the core to support a UTMI+
> > PHY +			with an 8- or 16-bit interface. Value 0 select 8-bit
> > +			interface, value 1 select 16-bit interface.
> 
> maybe
> 	snps,phyif-utmi-width = <8> or <16>;

Seems like this could be common. Any other bindings have something 
similar already? If not "utmi-width" is fine.

> 
> devicetree is about describing the hardware, not the things that get written 
> to registers :-) . The conversion from the described width to the register 
> value can easily be done in the driver.
> 
> 
> Also I don't think you need two properties for this. If the snps,phyif-utmi 
> property is specified it indicates that you want to manually set the width 
> and if it is absent you want to use the IC default. All functions reading 
> property-values indicate if the property is missing.

Agreed.

Rob


From robh at kernel.org  Mon Jul 11 07:58:54 2016
From: robh at kernel.org (Rob Herring)
Date: Mon, 11 Jul 2016 09:58:54 -0500
Subject: [PATCH v6 3/5] usb: dwc3: add phyif_utmi_quirk
In-Reply-To: <1467860066-15142-4-git-send-email-william.wu@rock-chips.com>
References: <1467860066-15142-1-git-send-email-william.wu@rock-chips.com>
 <1467860066-15142-4-git-send-email-william.wu@rock-chips.com>
Message-ID: <20160711145854.GB16636@rob-hp-laptop>

On Thu, Jul 07, 2016 at 10:54:24AM +0800, William Wu wrote:
> Add a quirk to configure the core to support the
> UTMI+ PHY with an 8- or 16-bit interface. UTMI+ PHY
> interface is hardware property, and it's platform
> dependent. Normall, the PHYIf can be configured

s/Normall/Normally/
s/PHYIf/PHYIF/

> during coreconsultant. But for some specific usb

s/usb/USB/

> cores(e.g. rk3399 soc dwc3), the default PHYIf
> configuration value is fault, so we need to
> reconfigure it by software.
> 
> And refer to the dwc3 databook, the GUSB2PHYCFG.USBTRDTIM

s/dwc3/DWC3/

> must be set to the corresponding value according to
> the UTMI+ PHY interface.

And wrap your lines at 70-74 characters.

Rob


From robh at kernel.org  Mon Jul 11 08:11:00 2016
From: robh at kernel.org (Rob Herring)
Date: Mon, 11 Jul 2016 10:11:00 -0500
Subject: [PATCH v6 4/5] usb: dwc3: add dis_del_phy_power_chg_quirk
In-Reply-To: <1467860066-15142-5-git-send-email-william.wu@rock-chips.com>
References: <1467860066-15142-1-git-send-email-william.wu@rock-chips.com>
 <1467860066-15142-5-git-send-email-william.wu@rock-chips.com>
Message-ID: <20160711151100.GA28081@rob-hp-laptop>

On Thu, Jul 07, 2016 at 10:54:25AM +0800, William Wu wrote:
> Add a quirk to clear the GUSB3PIPECTL.DELAYP1TRANS bit,
> which specifies whether disable delay PHY power change
> from P0 to P1/P2/P3 when link state changing from U0
> to U1/U2/U3 respectively.
> 
> Signed-off-by: William Wu <william.wu at rock-chips.com>
> ---
> Changes in v6:
> - use '-' instead of '_' in dts (Rob Herring)
> 
> Changes in v5:
> - None
> 
> Changes in v4:
> - rebase on top of balbi testing/next, remove pdata (balbi)
> 
> Changes in v3:
> - None
> 
> Changes in v2:
> - None
> 
>  Documentation/devicetree/bindings/usb/dwc3.txt | 2 ++
>  drivers/usb/dwc3/core.c                        | 5 +++++
>  drivers/usb/dwc3/core.h                        | 3 +++
>  3 files changed, 10 insertions(+)

Acked-by: Rob Herring <robh at kernel.org>


From robh at kernel.org  Mon Jul 11 08:13:32 2016
From: robh at kernel.org (Rob Herring)
Date: Mon, 11 Jul 2016 10:13:32 -0500
Subject: [PATCH v6 5/5] usb: dwc3: rockchip: add devicetree bindings
 documentation
In-Reply-To: <1467860324-15341-1-git-send-email-william.wu@rock-chips.com>
References: <1467860066-15142-1-git-send-email-william.wu@rock-chips.com>
 <1467860324-15341-1-git-send-email-william.wu@rock-chips.com>
Message-ID: <20160711151332.GA28672@rob-hp-laptop>

On Thu, Jul 07, 2016 at 10:58:44AM +0800, William Wu wrote:
> This patch adds the devicetree documentation required for Rockchip
> USB3.0 core wrapper consisting of USB3.0 IP from Synopsys.
> 
> It supports DRD mode, and could operate in device mode (SS, HS, FS)
> and host mode (SS, HS, FS, LS).
> 
> Signed-off-by: William Wu <william.wu at rock-chips.com>
> ---
> Changes in v6:
> - rename bus_clk, and add usbdrd3_1 node as an example (Heiko)
> 
> Changes in v5:
> - rename clock-names, and remove unnecessary clocks (Heiko)
> 
> Changes in v4:
> - modify commit log, and add phy documentation location (Sergei)
> 
> Changes in v3:
> - add dwc3 address (balbi)
> 
> Changes in v2:
> - add rockchip,dwc3.txt to Documentation/devicetree/bindings/ (balbi, Brian)
> 
>  .../devicetree/bindings/usb/rockchip,dwc3.txt      | 59 ++++++++++++++++++++++
>  1 file changed, 59 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/usb/rockchip,dwc3.txt

Acked-by: Rob Herring <robh at kernel.org>


From dianders at google.com  Mon Jul 11 09:53:12 2016
From: dianders at google.com (Doug Anderson)
Date: Mon, 11 Jul 2016 09:53:12 -0700
Subject: [PATCH v3 12/14] regulator: pwm: Retrieve correct voltage
In-Reply-To: <20160711070251.GA5823@ulmo.ba.sec>
References: <1465895602-31008-1-git-send-email-boris.brezillon@free-electrons.com>
 <1465895602-31008-13-git-send-email-boris.brezillon@free-electrons.com>
 <20160708154302.GC1151@ulmo.ba.sec> <20160709094718.GB6247@sirena.org.uk>
 <20160711070251.GA5823@ulmo.ba.sec>
Message-ID: <CAD=FV=WoUi4kNHW1h6XCezJyYUXbWLXj3yPYg+W-qhMAjpPn4A@mail.gmail.com>

Hi,

On Mon, Jul 11, 2016 at 12:02 AM, Thierry Reding
<thierry.reding at gmail.com> wrote:
> On Sat, Jul 09, 2016 at 11:47:18AM +0200, Mark Brown wrote:
>> On Fri, Jul 08, 2016 at 05:43:02PM +0200, Thierry Reding wrote:
>>
>> > Mark, do you want me to provide a stable branch with the PWM regulator
>> > patches and resolve that conflict in your tree? Or would you rather take
>> > the whole set based on a stable branch from the PWM tree? Or maybe yet
>> > another possibility would be to base the PWM tree on a stable branch
>> > from the regulator tree containing the above commit.
>>
>> Probably easiest to use this signed tag and resolve it in your tree:
>>
>> The following changes since commit 1a695a905c18548062509178b98bc91e67510864:
>>
>>   Linux 4.7-rc1 (2016-05-29 09:29:24 -0700)
>>
>> are available in the git repository at:
>>
>>   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator.git tags/pwm-modernization
>>
>> for you to fetch changes up to c2588393e6315ab68207323d37d2a73713d6bc81:
>>
>>   regulator: pwm: Fix regulator ramp delay for continuous mode (2016-07-07 11:45:06 +0200)
>>
>> ----------------------------------------------------------------
>> regulator: Provide a branch for moderninzation of the PWM code
>>
>> There's a new, improved PWM API which allows a lot of improvements in
>> the PWM regulator driver.  Since the bulk of the changes are in the PWM
>> API this is being managed in the PWM tree, merge pending regulator API
>> changes to allow this to be resolved more easily.
>>
>> ----------------------------------------------------------------
>> Alexandre Courbot (1):
>>       regulator: pwm: Support for enable GPIO
>>
>> Boris Brezillon (1):
>>       regulator: pwm: Drop unneeded pwm_enable() call
>>
>> Douglas Anderson (1):
>>       regulator: pwm: Fix regulator ramp delay for continuous mode
>>
>>  .../bindings/regulator/pwm-regulator.txt           |  7 +++-
>>  drivers/regulator/pwm-regulator.c                  | 40 ++++++++++++++++++----
>>  2 files changed, 39 insertions(+), 8 deletions(-)
>
> Merged into for-4.8/regulator of the PWM tree and rebased Boris'
> pwm-regulator patches on top.
>
> Boris, everything looks right to me, but can you take a quick look to
> see if it all matches up with what you expect?

As I mentioned in the other thread about the linuxnext conflict,
pwm_regulator_set_voltage() is wrong.

You have:

  ramp_delay = DIV_ROUND_UP(abs(min_uV - old_uV), ramp_delay);

You should have:

  ramp_delay = DIV_ROUND_UP(abs(req_min_uV - old_uV), ramp_delay);

-Doug


From daniel at ffwll.ch  Tue Jul 12 05:16:25 2016
From: daniel at ffwll.ch (Daniel Vetter)
Date: Tue, 12 Jul 2016 14:16:25 +0200
Subject: [PATCH v5 00/44] dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1467275019-30789-1-git-send-email-k.kozlowski@samsung.com>
References: <1467275019-30789-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <20160712121625.GP23520@phenom.ffwll.local>

On Thu, Jun 30, 2016 at 10:23:39AM +0200, Krzysztof Kozlowski wrote:
> Hi,
> 
> 
> This is fifth approach for replacing struct dma_attrs with unsigned
> long.
> 
> The main patch (1/44) doing the change is split into many subpatches
> for easier review (2-42).  They should be squashed together when
> applying.

For all the drm driver patches:

Acked-by: Daniel Vetter <daniel.vetter at ffwll.ch>

Should I pull these in through drm-misc, or do you prefer to merge them
through a special topic branch (with everything else) instead on your own?
-Daniel

> 
> 
> Rebased on v4.7-rc5.
> 
> For easier testing the patchset is available here:
> repo:   https://github.com/krzk/linux
> branch: for-next/dma-attrs-const-v5
> 
> 
> Changes since v4
> ================
> 1. Collect some acks. Still need more.
> 2. Minor fixes pointed by Robin Murphy.
> 3. Applied changes from Bart Van Assche's comment.
> 4. More tests and builds (using https://www.kernel.org/pub/tools/crosstool/).
> 
> 
> Changes since v3
> ================
> 1. Collect some acks.
> 2. Drop wrong patch 1/45 ("powerpc: dma-mapping: Don't hard-code
>    the value of DMA_ATTR_WEAK_ORDERING").
> 3. Minor fix pointed out by Michael Ellerman.
> 
> 
> Changes since v2
> ================
> 1. Follow Christoph Hellwig's comments (don't use BIT add
>    documentation, remove dma_get_attr).
> 
> 
> Rationale
> =========
> The dma-mapping core and the implementations do not change the
> DMA attributes passed by pointer.  Thus the pointer can point to const
> data.  However the attributes do not have to be a bitfield. Instead
> unsigned long will do fine:
> 
> 1. This is just simpler.  Both in terms of reading the code and setting
>    attributes.  Instead of initializing local attributes on the stack
>    and passing pointer to it to dma_set_attr(), just set the bits.
> 
> 2. It brings safeness and checking for const correctness because the
>    attributes are passed by value.
> 
> 
> Best regards,
> Krzysztof
> 
> 
> Krzysztof Kozlowski (44):
>   dma-mapping: Use unsigned long for dma_attrs
>   alpha: dma-mapping: Use unsigned long for dma_attrs
>   arc: dma-mapping: Use unsigned long for dma_attrs
>   ARM: dma-mapping: Use unsigned long for dma_attrs
>   arm64: dma-mapping: Use unsigned long for dma_attrs
>   avr32: dma-mapping: Use unsigned long for dma_attrs
>   blackfin: dma-mapping: Use unsigned long for dma_attrs
>   c6x: dma-mapping: Use unsigned long for dma_attrs
>   cris: dma-mapping: Use unsigned long for dma_attrs
>   frv: dma-mapping: Use unsigned long for dma_attrs
>   drm/exynos: dma-mapping: Use unsigned long for dma_attrs
>   drm/mediatek: dma-mapping: Use unsigned long for dma_attrs
>   drm/msm: dma-mapping: Use unsigned long for dma_attrs
>   drm/nouveau: dma-mapping: Use unsigned long for dma_attrs
>   drm/rockship: dma-mapping: Use unsigned long for dma_attrs
>   infiniband: dma-mapping: Use unsigned long for dma_attrs
>   iommu: dma-mapping: Use unsigned long for dma_attrs
>   [media] dma-mapping: Use unsigned long for dma_attrs
>   xen: dma-mapping: Use unsigned long for dma_attrs
>   swiotlb: dma-mapping: Use unsigned long for dma_attrs
>   powerpc: dma-mapping: Use unsigned long for dma_attrs
>   video: dma-mapping: Use unsigned long for dma_attrs
>   x86: dma-mapping: Use unsigned long for dma_attrs
>   iommu: intel: dma-mapping: Use unsigned long for dma_attrs
>   h8300: dma-mapping: Use unsigned long for dma_attrs
>   hexagon: dma-mapping: Use unsigned long for dma_attrs
>   ia64: dma-mapping: Use unsigned long for dma_attrs
>   m68k: dma-mapping: Use unsigned long for dma_attrs
>   metag: dma-mapping: Use unsigned long for dma_attrs
>   microblaze: dma-mapping: Use unsigned long for dma_attrs
>   mips: dma-mapping: Use unsigned long for dma_attrs
>   mn10300: dma-mapping: Use unsigned long for dma_attrs
>   nios2: dma-mapping: Use unsigned long for dma_attrs
>   openrisc: dma-mapping: Use unsigned long for dma_attrs
>   parisc: dma-mapping: Use unsigned long for dma_attrs
>   misc: mic: dma-mapping: Use unsigned long for dma_attrs
>   s390: dma-mapping: Use unsigned long for dma_attrs
>   sh: dma-mapping: Use unsigned long for dma_attrs
>   sparc: dma-mapping: Use unsigned long for dma_attrs
>   tile: dma-mapping: Use unsigned long for dma_attrs
>   unicore32: dma-mapping: Use unsigned long for dma_attrs
>   xtensa: dma-mapping: Use unsigned long for dma_attrs
>   dma-mapping: Remove dma_get_attr
>   dma-mapping: Document the DMA attributes next to the declaration
> 
>  Documentation/DMA-API.txt                          |  33 +++---
>  Documentation/DMA-attributes.txt                   |   2 +-
>  arch/alpha/include/asm/dma-mapping.h               |   2 -
>  arch/alpha/kernel/pci-noop.c                       |   2 +-
>  arch/alpha/kernel/pci_iommu.c                      |  12 +-
>  arch/arc/mm/dma.c                                  |  12 +-
>  arch/arm/common/dmabounce.c                        |   4 +-
>  arch/arm/include/asm/dma-mapping.h                 |  13 +--
>  arch/arm/include/asm/xen/page-coherent.h           |  16 +--
>  arch/arm/mm/dma-mapping.c                          | 117 +++++++++----------
>  arch/arm/xen/mm.c                                  |   8 +-
>  arch/arm64/mm/dma-mapping.c                        |  66 +++++------
>  arch/avr32/mm/dma-coherent.c                       |  12 +-
>  arch/blackfin/kernel/dma-mapping.c                 |   8 +-
>  arch/c6x/include/asm/dma-mapping.h                 |   4 +-
>  arch/c6x/kernel/dma.c                              |   9 +-
>  arch/c6x/mm/dma-coherent.c                         |   4 +-
>  arch/cris/arch-v32/drivers/pci/dma.c               |   9 +-
>  arch/frv/mb93090-mb00/pci-dma-nommu.c              |   8 +-
>  arch/frv/mb93090-mb00/pci-dma.c                    |   9 +-
>  arch/h8300/kernel/dma.c                            |   8 +-
>  arch/hexagon/include/asm/dma-mapping.h             |   1 -
>  arch/hexagon/kernel/dma.c                          |   8 +-
>  arch/ia64/hp/common/sba_iommu.c                    |  22 ++--
>  arch/ia64/include/asm/machvec.h                    |   1 -
>  arch/ia64/kernel/pci-swiotlb.c                     |   4 +-
>  arch/ia64/sn/pci/pci_dma.c                         |  22 ++--
>  arch/m68k/kernel/dma.c                             |  12 +-
>  arch/metag/kernel/dma.c                            |  16 +--
>  arch/microblaze/include/asm/dma-mapping.h          |   1 -
>  arch/microblaze/kernel/dma.c                       |  12 +-
>  arch/mips/cavium-octeon/dma-octeon.c               |   8 +-
>  arch/mips/loongson64/common/dma-swiotlb.c          |  10 +-
>  arch/mips/mm/dma-default.c                         |  20 ++--
>  arch/mips/netlogic/common/nlm-dma.c                |   4 +-
>  arch/mn10300/mm/dma-alloc.c                        |   8 +-
>  arch/nios2/mm/dma-mapping.c                        |  12 +-
>  arch/openrisc/kernel/dma.c                         |  21 ++--
>  arch/parisc/kernel/pci-dma.c                       |  18 +--
>  arch/powerpc/include/asm/dma-mapping.h             |   7 +-
>  arch/powerpc/include/asm/iommu.h                   |  10 +-
>  arch/powerpc/kernel/dma-iommu.c                    |  12 +-
>  arch/powerpc/kernel/dma.c                          |  18 +--
>  arch/powerpc/kernel/ibmebus.c                      |  12 +-
>  arch/powerpc/kernel/iommu.c                        |  12 +-
>  arch/powerpc/kernel/vio.c                          |  12 +-
>  arch/powerpc/platforms/cell/iommu.c                |  28 ++---
>  arch/powerpc/platforms/pasemi/iommu.c              |   2 +-
>  arch/powerpc/platforms/powernv/npu-dma.c           |   8 +-
>  arch/powerpc/platforms/powernv/pci-ioda.c          |   4 +-
>  arch/powerpc/platforms/powernv/pci.c               |   2 +-
>  arch/powerpc/platforms/powernv/pci.h               |   2 +-
>  arch/powerpc/platforms/ps3/system-bus.c            |  18 +--
>  arch/powerpc/platforms/pseries/iommu.c             |   6 +-
>  arch/powerpc/sysdev/dart_iommu.c                   |   2 +-
>  arch/s390/include/asm/dma-mapping.h                |   1 -
>  arch/s390/pci/pci_dma.c                            |  23 ++--
>  arch/sh/include/asm/dma-mapping.h                  |   4 +-
>  arch/sh/kernel/dma-nommu.c                         |   4 +-
>  arch/sh/mm/consistent.c                            |   4 +-
>  arch/sparc/kernel/iommu.c                          |  12 +-
>  arch/sparc/kernel/ioport.c                         |  24 ++--
>  arch/sparc/kernel/pci_sun4v.c                      |  12 +-
>  arch/tile/kernel/pci-dma.c                         |  28 ++---
>  arch/unicore32/mm/dma-swiotlb.c                    |   4 +-
>  arch/x86/include/asm/dma-mapping.h                 |   5 +-
>  arch/x86/include/asm/swiotlb.h                     |   4 +-
>  arch/x86/include/asm/xen/page-coherent.h           |   9 +-
>  arch/x86/kernel/amd_gart_64.c                      |  20 ++--
>  arch/x86/kernel/pci-calgary_64.c                   |  14 +--
>  arch/x86/kernel/pci-dma.c                          |   4 +-
>  arch/x86/kernel/pci-nommu.c                        |   4 +-
>  arch/x86/kernel/pci-swiotlb.c                      |   4 +-
>  arch/x86/pci/sta2x11-fixup.c                       |   2 +-
>  arch/x86/pci/vmd.c                                 |  16 +--
>  arch/xtensa/kernel/pci-dma.c                       |  12 +-
>  drivers/gpu/drm/exynos/exynos_drm_fbdev.c          |   2 +-
>  drivers/gpu/drm/exynos/exynos_drm_g2d.c            |  12 +-
>  drivers/gpu/drm/exynos/exynos_drm_gem.c            |  20 ++--
>  drivers/gpu/drm/exynos/exynos_drm_gem.h            |   2 +-
>  drivers/gpu/drm/mediatek/mtk_drm_gem.c             |  13 +--
>  drivers/gpu/drm/mediatek/mtk_drm_gem.h             |   2 +-
>  drivers/gpu/drm/msm/msm_drv.c                      |  13 +--
>  .../gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c    |  13 +--
>  drivers/gpu/drm/rockchip/rockchip_drm_gem.c        |  17 ++-
>  drivers/gpu/drm/rockchip/rockchip_drm_gem.h        |   2 +-
>  drivers/infiniband/core/umem.c                     |   7 +-
>  drivers/iommu/amd_iommu.c                          |  12 +-
>  drivers/iommu/dma-iommu.c                          |   8 +-
>  drivers/iommu/intel-iommu.c                        |  12 +-
>  drivers/media/platform/sti/bdisp/bdisp-hw.c        |  26 ++---
>  drivers/media/v4l2-core/videobuf2-dma-contig.c     |  30 ++---
>  drivers/media/v4l2-core/videobuf2-dma-sg.c         |  19 +--
>  drivers/misc/mic/host/mic_boot.c                   |  20 ++--
>  drivers/parisc/ccio-dma.c                          |  16 +--
>  drivers/parisc/sba_iommu.c                         |  16 +--
>  drivers/video/fbdev/omap2/omapfb/omapfb-main.c     |  12 +-
>  drivers/video/fbdev/omap2/omapfb/omapfb.h          |   3 +-
>  drivers/xen/swiotlb-xen.c                          |  14 +--
>  include/linux/dma-attrs.h                          |  71 ------------
>  include/linux/dma-iommu.h                          |   6 +-
>  include/linux/dma-mapping.h                        | 128 ++++++++++++++-------
>  include/linux/swiotlb.h                            |  10 +-
>  include/media/videobuf2-dma-contig.h               |   7 +-
>  include/rdma/ib_verbs.h                            |   8 +-
>  include/xen/swiotlb-xen.h                          |  12 +-
>  lib/dma-noop.c                                     |   9 +-
>  lib/swiotlb.c                                      |  13 ++-
>  108 files changed, 689 insertions(+), 789 deletions(-)
>  delete mode 100644 include/linux/dma-attrs.h
> 
> -- 
> 1.9.1
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


From k.kozlowski at samsung.com  Tue Jul 12 05:19:07 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Tue, 12 Jul 2016 14:19:07 +0200
Subject: [PATCH v5 00/44] dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <20160712121625.GP23520@phenom.ffwll.local>
References: <1467275019-30789-1-git-send-email-k.kozlowski@samsung.com>
 <20160712121625.GP23520@phenom.ffwll.local>
Message-ID: <5784E03B.6060000@samsung.com>

On 07/12/2016 02:16 PM, Daniel Vetter wrote:
> On Thu, Jun 30, 2016 at 10:23:39AM +0200, Krzysztof Kozlowski wrote:
>> Hi,
>>
>>
>> This is fifth approach for replacing struct dma_attrs with unsigned
>> long.
>>
>> The main patch (1/44) doing the change is split into many subpatches
>> for easier review (2-42).  They should be squashed together when
>> applying.
> 
> For all the drm driver patches:
> 
> Acked-by: Daniel Vetter <daniel.vetter at ffwll.ch>
> 
> Should I pull these in through drm-misc, or do you prefer to merge them
> through a special topic branch (with everything else) instead on your own?
> -Daniel

Thanks. I saw today that Andrew Morton applied the patchset so I think
he will handle it.

Best regards,
Krzysztof



From daniel at ffwll.ch  Tue Jul 12 05:38:26 2016
From: daniel at ffwll.ch (Daniel Vetter)
Date: Tue, 12 Jul 2016 14:38:26 +0200
Subject: [PATCH v3 2/4] drm/rockchip: add an common abstracted PSR driver
In-Reply-To: <CAOw6vbKMWEEDEqw0_ba_FGO-2mOBg3uanQzk5nbnCtowWdLu9w@mail.gmail.com>
References: <1467364685-21390-1-git-send-email-ykk@rock-chips.com>
 <1467364754-21674-1-git-send-email-ykk@rock-chips.com>
 <CAOw6vbKMWEEDEqw0_ba_FGO-2mOBg3uanQzk5nbnCtowWdLu9w@mail.gmail.com>
Message-ID: <20160712123826.GU23520@phenom.ffwll.local>

On Fri, Jul 01, 2016 at 02:00:00PM -0400, Sean Paul wrote:
> On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk at rock-chips.com> wrote:
> > The PSR driver have exported four symbols for specific device driver:
> > - rockchip_drm_psr_register()
> > - rockchip_drm_psr_unregister()
> > - rockchip_drm_psr_enable()
> > - rockchip_drm_psr_disable()
> > - rockchip_drm_psr_flush()
> >
> > Encoder driver should call the register/unregister interfaces to hook
> > itself into common PSR driver, encoder have implement the 'psr_set'
> > callback which use the set PSR state in hardware side.
> >
> > Crtc driver would call the enable/disable interfaces when vblank is
> > enable/disable, after that the common PSR driver would call the encoder
> > registered callback to set the PSR state.
> >
> 
> This feels overly complicated. It seems like you could cut out a bunch
> of code by just coding the psr functions into vop and
> analogix_dp-rockchip. I suppose the only reason to keep it abstracted
> would be if you plan on supporting psr in a different encoder or crtc
> in rockchip, or if you're planning on moving this into drm core.

Agreed on the layers of indirection. Also, you end up with 3 delayed
timers in total:
- defio timer from fbdev emulation
- timer in this abstraction
- delayed work in the psr backend driver

I'd cut out at least the middle one.

But since this seems to correctly use the ->dirty callback it gets my Ack
either way ;-)

Cheers, Daniel

> 
> Perhaps others will disagree with this sentiment and this is the right
> thing to do.
> 
> > Fb driver would call the flush interface in 'fb->dirty' callback, this
> > helper function would force all PSR enabled encoders to exit from PSR
> > for 3 seconds.
> >
> > Signed-off-by: Yakir Yang <ykk at rock-chips.com>
> > ---
> > Changes in v3:
> > - split the psr flow into an common abstracted PSR driver
> > - implement the 'fb->dirty' callback function (Daniel)
> > - avoid to use notify to acqiure for vact event (Daniel)
> > - remove psr_active() callback which introduce in v2
> >
> > Changes in v2: None
> >
> >  drivers/gpu/drm/rockchip/Makefile           |   2 +-
> >  drivers/gpu/drm/rockchip/rockchip_drm_fb.c  |  12 ++
> >  drivers/gpu/drm/rockchip/rockchip_drm_psr.c | 200 ++++++++++++++++++++++++++++
> >  drivers/gpu/drm/rockchip/rockchip_drm_psr.h |  12 ++
> >  drivers/gpu/drm/rockchip/rockchip_drm_vop.c |  24 ++++
> >  5 files changed, 249 insertions(+), 1 deletion(-)
> >  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> >  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.h
> >
> > diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
> > index 05d0713..9746365 100644
> > --- a/drivers/gpu/drm/rockchip/Makefile
> > +++ b/drivers/gpu/drm/rockchip/Makefile
> > @@ -3,7 +3,7 @@
> >  # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
> >
> >  rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
> > -               rockchip_drm_gem.o rockchip_drm_vop.o
> > +               rockchip_drm_gem.o rockchip_drm_psr.o rockchip_drm_vop.o
> >  rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
> >
> >  obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > index 20f12bc..0fec18f 100644
> > --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> > @@ -21,6 +21,7 @@
> >
> >  #include "rockchip_drm_drv.h"
> >  #include "rockchip_drm_gem.h"
> > +#include "rockchip_drm_psr.h"
> >
> >  #define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
> >
> > @@ -66,9 +67,20 @@ static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
> >                                      rockchip_fb->obj[0], handle);
> >  }
> >
> > +static int rockchip_drm_fb_dirty(struct drm_framebuffer *fb,
> > +                                struct drm_file *file,
> > +                                unsigned int flags, unsigned int color,
> > +                                struct drm_clip_rect *clips,
> > +                                unsigned int num_clips)
> > +{
> > +       rockchip_drm_psr_flush();
> > +       return 0;
> > +}
> > +
> >  static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
> >         .destroy        = rockchip_drm_fb_destroy,
> >         .create_handle  = rockchip_drm_fb_create_handle,
> > +       .dirty          = rockchip_drm_fb_dirty,
> >  };
> >
> >  static struct rockchip_drm_fb *
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> > new file mode 100644
> > index 0000000..c044443
> > --- /dev/null
> > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> > @@ -0,0 +1,200 @@
> > +#include <drm/drm_crtc_helper.h>
> > +
> > +#include "rockchip_drm_psr.h"
> > +
> > +#define PSR_FLUSH_TIMEOUT      msecs_to_jiffies(3000) /* 3 seconds */
> > +
> > +static LIST_HEAD(psr_list);
> > +static DEFINE_MUTEX(psr_list_mutex);
> 
> I'm not crazy about these globals. Perhaps you can initialize them
> with the rockchip driver and tuck them in a driver-level struct
> (rockchip_drm_private or something).
> 
> 
> > +
> > +enum psr_state {
> > +       PSR_FLUSH,
> > +       PSR_ENABLE,
> > +       PSR_DISABLE,
> > +};
> > +
> > +struct psr_drv {
> > +       struct list_head list;
> > +       enum psr_state state;
> > +       struct mutex state_mutex;
> > +
> > +       struct timer_list flush_timer;
> > +
> > +       struct drm_encoder *encoder;
> > +       int (*set)(struct drm_encoder *encoder, bool enable);
> > +};
> > +
> > +static struct psr_drv *find_psr_by_crtc(struct drm_crtc *crtc)
> > +{
> > +       struct psr_drv *psr;
> > +
> > +       mutex_lock(&psr_list_mutex);
> > +       list_for_each_entry(psr, &psr_list, list) {
> > +               if (psr->encoder->crtc == crtc) {
> > +                       mutex_unlock(&psr_list_mutex);
> > +                       return psr;
> > +               }
> > +       }
> > +       mutex_unlock(&psr_list_mutex);
> > +
> > +       return ERR_PTR(-ENODEV);
> > +}
> > +
> > +static void psr_enable(struct psr_drv *psr)
> > +{
> > +       if (psr->state == PSR_ENABLE)
> > +               return;
> 
> Should you be worried about races by accessing this outside of the state mutex?
> 
> > +
> > +       mutex_lock(&psr->state_mutex);
> > +       psr->state = PSR_ENABLE;
> > +       psr->set(psr->encoder, true);
> > +       mutex_unlock(&psr->state_mutex);
> > +}
> > +
> > +static void psr_disable(struct psr_drv *psr)
> > +{
> > +       if (psr->state == PSR_DISABLE)
> > +               return;
> > +
> > +       mutex_lock(&psr->state_mutex);
> > +       psr->state = PSR_DISABLE;
> > +       psr->set(psr->encoder, false);
> > +       mutex_unlock(&psr->state_mutex);
> > +}
> > +
> > +static void psr_flush_handler(unsigned long data)
> > +{
> > +       struct psr_drv *psr = (struct psr_drv *)data;
> > +
> > +       if (!psr || psr->state != PSR_FLUSH)
> > +               return;
> > +
> > +       psr_enable(psr);
> > +}
> > +
> > +/**
> > + * rockchip_drm_psr_enable - enable the encoder PSR which bind to given CRTC
> > + * @crtc: CRTC to obtain the PSR encoder
> > + *
> > + * Returns:
> > + * Zero on success, negative errno on failure.
> > + */
> > +int rockchip_drm_psr_enable(struct drm_crtc *crtc)
> > +{
> > +       struct psr_drv *psr = find_psr_by_crtc(crtc);
> > +
> > +       if (IS_ERR(psr))
> > +               return PTR_ERR(psr);
> > +
> > +       psr_enable(psr);
> > +       return 0;
> > +}
> > +EXPORT_SYMBOL(rockchip_drm_psr_enable);
> > +
> > +/**
> > + * rockchip_drm_psr_disable - disable the encoder PSR which bind to given CRTC
> > + * @crtc: CRTC to obtain the PSR encoder
> > + *
> > + * Returns:
> > + * Zero on success, negative errno on failure.
> > + */
> > +int rockchip_drm_psr_disable(struct drm_crtc *crtc)
> > +{
> > +       struct psr_drv *psr = find_psr_by_crtc(crtc);
> > +
> > +       if (IS_ERR(psr))
> > +               return PTR_ERR(psr);
> > +
> > +       psr_disable(psr);
> > +       return 0;
> > +}
> > +EXPORT_SYMBOL(rockchip_drm_psr_disable);
> > +
> > +/**
> > + * rockchip_drm_psr_flush - force to flush all registered PSR encoders
> > + *
> > + * Disable the PSR function for all registered encoders, and then enable the
> > + * PSR function back after 5 second. If encoder PSR state have been changed
> 
> s/5 second/PSR_FLUSH_TIMEOUT/
> 
> > + * during flush time, then keep the state no change after flush timeout.
> > + *
> > + * Returns:
> > + * Zero on success, negative errno on failure.
> > + */
> > +void rockchip_drm_psr_flush(void)
> > +{
> > +       struct psr_drv *psr;
> > +
> > +       mutex_lock(&psr_list_mutex);
> > +       list_for_each_entry(psr, &psr_list, list) {
> > +               if (psr->state == PSR_DISABLE)
> > +                       continue;
> > +
> > +               mod_timer(&psr->flush_timer,
> > +                         round_jiffies_up(jiffies + PSR_FLUSH_TIMEOUT));
> > +
> > +               psr_disable(psr);
> > +               psr->state = PSR_FLUSH;
> 
> This is set outside of state_mutex, is that intentional?
> 
> > +       }
> > +       mutex_unlock(&psr_list_mutex);
> > +}
> > +EXPORT_SYMBOL(rockchip_drm_psr_flush);
> > +
> > +/**
> > + * rockchip_drm_psr_register - register encoder to psr driver
> > + * @encoder: encoder that obtain the PSR function
> > + * @psr_set: call back to set PSR state
> > + *
> > + * Returns:
> > + * Zero on success, negative errno on failure.
> > + */
> > +int rockchip_drm_psr_register(struct drm_encoder *encoder,
> > +                             int (*psr_set)(struct drm_encoder *, bool enable))
> > +{
> > +       struct psr_drv *psr;
> > +
> > +       if (!encoder || !psr_set)
> > +               return -EINVAL;
> > +
> > +       psr = kzalloc(sizeof(struct psr_drv), GFP_KERNEL);
> > +       if (!psr)
> > +               return -ENOMEM;
> > +
> > +       setup_timer(&psr->flush_timer, psr_flush_handler, (unsigned long)psr);
> > +
> > +       mutex_init(&psr->state_mutex);
> > +
> > +       psr->state = PSR_DISABLE;
> > +       psr->encoder = encoder;
> > +       psr->set = psr_set;
> > +
> > +       mutex_lock(&psr_list_mutex);
> > +       list_add_tail(&psr->list, &psr_list);
> > +       mutex_unlock(&psr_list_mutex);
> > +
> > +       return 0;
> > +}
> > +EXPORT_SYMBOL(rockchip_drm_psr_register);
> > +
> > +/**
> > + * rockchip_drm_psr_unregister - unregister encoder to psr driver
> > + * @encoder: encoder that obtain the PSR function
> > + * @psr_set: call back to set PSR state
> > + *
> > + * Returns:
> > + * Zero on success, negative errno on failure.
> > + */
> > +void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
> > +{
> > +       struct psr_drv *psr;
> > +
> > +       mutex_lock(&psr_list_mutex);
> > +       list_for_each_entry(psr, &psr_list, list) {
> > +               if (psr->encoder == encoder) {
> > +                       del_timer(&psr->flush_timer);
> > +                       list_del(&psr->list);
> > +                       kfree(psr);
> > +               }
> > +       }
> > +       mutex_unlock(&psr_list_mutex);
> > +}
> > +EXPORT_SYMBOL(rockchip_drm_psr_unregister);
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.h b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
> > new file mode 100644
> > index 0000000..622f605
> > --- /dev/null
> > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
> > @@ -0,0 +1,12 @@
> > +#ifndef __ROCKCHIP_DRM_PSR___
> > +#define __ROCKCHIP_DRM_PSR___
> > +
> > +void rockchip_drm_psr_flush(void);
> > +int rockchip_drm_psr_enable(struct drm_crtc *crtc);
> > +int rockchip_drm_psr_disable(struct drm_crtc *crtc);
> > +
> > +int rockchip_drm_psr_register(struct drm_encoder *encoder,
> > +                           int (*psr_set)(struct drm_encoder *, bool enable));
> > +void rockchip_drm_psr_unregister(struct drm_encoder *encoder);
> > +
> > +#endif /* __ROCKCHIP_DRM_PSR__ */
> > diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> > index cd3cac5..3c6dfc5 100644
> > --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> > +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> > @@ -34,6 +34,7 @@
> >  #include "rockchip_drm_drv.h"
> >  #include "rockchip_drm_gem.h"
> >  #include "rockchip_drm_fb.h"
> > +#include "rockchip_drm_psr.h"
> >  #include "rockchip_drm_vop.h"
> >
> >  #define __REG_SET_RELAXED(x, off, mask, shift, v, write_mask) \
> > @@ -121,6 +122,9 @@ struct vop {
> >         /* protected by dev->event_lock */
> >         struct drm_pending_vblank_event *event;
> >
> > +       bool psr_enabled;
> > +       struct delayed_work psr_work;
> > +
> >         struct completion line_flag_completion;
> >
> >         const struct vop_data *data;
> > @@ -911,6 +915,16 @@ static const struct drm_plane_funcs vop_plane_funcs = {
> >         .atomic_destroy_state = vop_atomic_plane_destroy_state,
> >  };
> >
> > +static void vop_psr_work(struct work_struct *work)
> > +{
> > +       struct vop *vop = container_of(work, typeof(*vop), psr_work.work);
> > +
> > +       if (vop->psr_enabled)
> > +               rockchip_drm_psr_enable(&vop->crtc);
> > +       else
> > +               rockchip_drm_psr_disable(&vop->crtc);
> > +}
> > +
> >  static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
> >  {
> >         struct vop *vop = to_vop(crtc);
> > @@ -925,6 +939,9 @@ static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
> >
> >         spin_unlock_irqrestore(&vop->irq_lock, flags);
> >
> > +       vop->psr_enabled = false;
> > +       schedule_delayed_work(&vop->psr_work, msecs_to_jiffies(10));
> > +
> >         return 0;
> >  }
> >
> > @@ -941,6 +958,9 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc)
> >         VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0);
> >
> >         spin_unlock_irqrestore(&vop->irq_lock, flags);
> > +
> > +       vop->psr_enabled = true;
> > +       schedule_delayed_work(&vop->psr_work, msecs_to_jiffies(10));
> >  }
> >
> >  static void vop_crtc_wait_for_update(struct drm_crtc *crtc)
> > @@ -1582,6 +1602,10 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
> >                 return ret;
> >
> >         pm_runtime_enable(&pdev->dev);
> > +
> > +       vop->psr_enabled = false;
> > +       INIT_DELAYED_WORK(&vop->psr_work, vop_psr_work);
> > +
> >         return 0;
> >  }
> >
> > --
> > 1.9.1
> >
> >

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


From daniel at ffwll.ch  Tue Jul 12 06:59:04 2016
From: daniel at ffwll.ch (Daniel Vetter)
Date: Tue, 12 Jul 2016 15:59:04 +0200
Subject: next-20160707 build: 1 failures 6 warnings (next-20160707)
In-Reply-To: <20160707153237.GF11467@suse.de>
References: <E1bL5XI-0004Z9-EP@optimist> <20160707122219.GO6247@sirena.org.uk>
 <CAAFQd5DacKar+T1Fj62NvAZOT_QWBhahftZJXGq40Z19n-Z4Hg@mail.gmail.com>
 <20160707153237.GF11467@suse.de>
Message-ID: <20160712135904.GJ23520@phenom.ffwll.local>

On Thu, Jul 07, 2016 at 05:32:37PM +0200, Joerg Roedel wrote:
> On Thu, Jul 07, 2016 at 10:18:02PM +0900, Tomasz Figa wrote:
> > This is probably because we missed this hidden dependency and decided
> > to push the IOMMU part separately through the IOMMU tree. I guess we
> > can remove the offending patch from the IOMMU tree and let the DRM
> > tree include it based on a topic branch from Joerg.
> > 
> > Joerg, David, Mark Yao, what do you think?
> 
> Will the DRM patches go upstream soon, or is there still discussion
> around them? For now I am going to remove/revert that patch in my tree.

drm is pretty much done for 4.8, but as usual there's some driver
maintainers who are rather late. I'd say unlikely it lands in 4.8,
postpone to 4.9 seems more reasonable given the trouble it just caused.
-Daniel

> 
> 
> 	Joerg
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


From heiko at sntech.de  Tue Jul 12 07:27:40 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Tue, 12 Jul 2016 16:27:40 +0200
Subject: [PATCH v1 5/6] drm/rockchip: dw_hdmi: introduce the VPLL clock
 setting
In-Reply-To: <1468235149-29625-1-git-send-email-ykk@rock-chips.com>
References: <1468235079-29152-1-git-send-email-ykk@rock-chips.com>
 <1468235149-29625-1-git-send-email-ykk@rock-chips.com>
Message-ID: <3636683.EuFECY2EQB@diego>

Hi Yakir,

Am Montag, 11. Juli 2016, 19:05:49 schrieb Yakir Yang:
> For RK3399 HDMI, there is an external clock need for HDMI PHY,
> and it should keep the same clock rate with VOP DCLK.
> 
> VPLL have supported the clock for HDMI PHY, but there is no
> clock divider bewteen VPLL and HDMI PHY. So we need to set the
> VPLL rate manually in HDMI driver.

I don't think reserving the vpll for the hdmi at all times is the right way to 
go. 

While I do agree that reserving the VPLL (and NPLL on the rk3288) for graphics 
use looks like the right way, I think the core Rockchip drm driver should be 
responsible for managing it and also for deciding which output encoder gets to 
use it.

While true that on the Chromebook device-types the edp is static and hdmi 
needs the broad range of dynamic frequencies, this is not necessarily the case 
for all future device types and/or socs.

In the other thread we discussed adding that as rockchip,dclk-pll = <&...>; to 
the base display-subsystem node, Doug didn't manage to find time to respond yet 
though - and is on vacation right now.

I still believe that would be the best solution :-) .


That property could list 1 or even 2 plls, depending on the soc or board 
layout - maybe someone frees up the cpll in some special layout or something.
If none are listed, then the drm driver would need to cope with its available 
clocks.

Implementation-wise you could even still keep your shortcut until we encounter 
a device with different , let the drm use the pll for hdmi only until someone 
comes up with a better concept, but still the binding should be correct and 
versatile from the start.


Heiko

> ---
>  .../bindings/display/rockchip/dw_hdmi-rockchip.txt |  3 ++-
>  drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c        | 25
> +++++++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-)
> 
> diff --git
> a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
> b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
> index 4e573d2..4e23ca4 100644
> ---
> a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
> +++
> b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
> @@ -17,7 +17,8 @@ Required properties:
> 
>  Optional properties
>  - ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing
> -- clocks, clock-names: phandle to the HDMI CEC clock, name should be "cec"
> +- clocks, clock-names: phandle to the HDMI CEC clock, name should be "cec",
> +		       phandle to the VPLL clock, name should be "vpll".
> 
>  Example:
>  hdmi: hdmi at ff980000 {
> diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
> b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index 329099b..701bb73 100644
> --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
> +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
> @@ -7,10 +7,12 @@
>   * (at your option) any later version.
>   */
> 
> +#include <linux/clk.h>
> +#include <linux/mfd/syscon.h>
>  #include <linux/module.h>
>  #include <linux/platform_device.h>
> -#include <linux/mfd/syscon.h>
>  #include <linux/regmap.h>
> +
>  #include <drm/drm_of.h>
>  #include <drm/drmP.h>
>  #include <drm/drm_crtc_helper.h>
> @@ -33,6 +35,7 @@ struct rockchip_hdmi {
>  	struct regmap *regmap;
>  	struct drm_encoder encoder;
>  	enum dw_hdmi_devtype dev_type;
> +	struct clk *vpll_clk;
>  };
> 
>  #define to_rockchip_hdmi(x)	container_of(x, struct rockchip_hdmi, x)
> @@ -145,6 +148,7 @@ static const struct dw_hdmi_phy_config
> rockchip_phy_config[] = { static int rockchip_hdmi_parse_dt(struct
> rockchip_hdmi *hdmi)
>  {
>  	struct device_node *np = hdmi->dev->of_node;
> +	int ret;
> 
>  	hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
>  	if (IS_ERR(hdmi->regmap)) {
> @@ -152,6 +156,22 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi
> *hdmi) return PTR_ERR(hdmi->regmap);
>  	}
> 
> +	hdmi->vpll_clk = devm_clk_get(hdmi->dev, "vpll");
> +	if (PTR_ERR(hdmi->vpll_clk) == -ENOENT) {
> +		hdmi->vpll_clk = NULL;
> +	} else if (PTR_ERR(hdmi->vpll_clk) == -EPROBE_DEFER) {
> +		return -EPROBE_DEFER;
> +	} else if (IS_ERR(hdmi->vpll_clk)) {
> +		dev_err(hdmi->dev, "failed to get grf clock\n");
> +		return PTR_ERR(hdmi->vpll_clk);
> +	}
> +
> +	ret = clk_prepare_enable(hdmi->vpll_clk);
> +	if (ret) {
> +		dev_err(hdmi->dev, "Failed to enable HDMI vpll: %d\n", ret);
> +		return ret;
> +	}
> +
>  	return 0;
>  }
> 
> @@ -194,6 +214,9 @@ static void dw_hdmi_rockchip_encoder_mode_set(struct
> drm_encoder *encoder, struct drm_display_mode *mode,
>  					      struct drm_display_mode *adj_mode)
>  {
> +	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
> +
> +	clk_set_rate(hdmi->vpll_clk, adj_mode->clock * 1000);
>  }
> 
>  static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)



From zyw at rock-chips.com  Tue Jul 12 07:58:38 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Tue, 12 Jul 2016 22:58:38 +0800
Subject: [PATCH] Add dptx firmware for rockchip rk3399
Message-ID: <1468335519-28245-1-git-send-email-zyw@rock-chips.com>

This dptx firmware is required by rk3399 Type-C DisplayPort controller.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>

---

 WHENCE            |  18 ++++++++++++++++++
 rockchip/dptx.bin | Bin 0 -> 98320 bytes
 2 files changed, 18 insertions(+)
 create mode 100644 rockchip/dptx.bin

diff --git a/WHENCE b/WHENCE
index d8b1eaa..f463e26 100644
--- a/WHENCE
+++ b/WHENCE
@@ -3294,3 +3294,21 @@ General Public License.
 These firmware files are distributed in the hope that they will be useful, but
 are provided WITHOUT ANY WARRANTY, INCLUDING BUT NOT LIMITED TO IMPLIED WARRANTY
 OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+--------------------------------------------------------------------------
+
+Driver: rk3399-dptx - ROCKCHIP rk3399 dptx firmware
+
+File: rockchip/dptx.bin
+
+Licence: Redistributable.
+
+Rockchip Inc. grants permission to use and redistribute aforementioned firmware
+files for the use with devices containing Rockchip chipsets, but not as part of
+the Linux kernel or in any other form which would require these files themselves
+to be covered by the terms of the GNU General Public License or the GNU Lesser
+General Public License.
+
+These firmware files are distributed in the hope that they will be useful, but
+are provided WITHOUT ANY WARRANTY, INCLUDING BUT NOT LIMITED TO IMPLIED WARRANTY
+OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/rockchip/dptx.bin b/rockchip/dptx.bin
new file mode 100644
index 0000000000000000000000000000000000000000..042ec48d748a4a1e9b8174fa0d3fcc64bf2c562f
GIT binary patch
literal 98320
zcmeFZdstJ));K(S=fWi$C5Z7tXhSdpf-PdL*H&&t0d0X+s#ebtG*)S~XzlSXNwgR)
zr-f)OP}?TaD5T~T(y9TZg-8)FREX9BN((urRc=B=MJn09H3_yom+yOi&-;AO_t)#=
znmyMwYu2n;v)0T6PYHoM5&lOYBteMHLWnCA3L&HOHH1QO3I0bRbZdej1^GRq6Ccqj
zk7$aqpG?^3$#!*?KMBLvCm;cOVPHUj5 at uJQ`^r0gM?`w~b8k+tLJ8zN_^J=e6K^}(
zLqYgIaV}JP at YO)50C_11&)5lQ=>$&!B9(|`e1i9XzyHgD|K-5{4;=U|*7Jv0fi*Vt
zek^(pp}>fFiAz(wYSy6^SeA?dNx0w_hG5=v;j=?y{%z?yiTlpY^9MJ9)h+bC8=8Yq
zcnd=^n;R+&75swN^urw;fa2F(uR|CE=>PLyLo8}>^K6S1+=&f+6dNvyO~{K~r;JT6
zb&&I)_5VGm0UEmr?zpA5xP?A)3qS0Z at Uh#v({AbK9pwCb?f*<SIr{I274*f1j>d+E
z$0j7iu3HzIo*pZa#OCG2DwVON|8KN3xp{WD3Hsbhq5o00 at OZa`H{8~}<(9tJO_Jl5
z_lcYG3%AnB|7ThR<_Y^`C=sz~`afy=zsZR}G4}sK(`Y8W7^C;9Q3gfKPn`9b7Tjip
zhAHPUVpEii|C^5gzo`@cKcxRj?{9mc(62vp>|5ai#B^jENP2NSKr28qz%_sZ3Wb)c
zQK*tdFYbgit|%G%o`k$S07u at Jcp~V<CLeln^dx$*U>v>pj0ZwWh$|=3i`Vez#k-Q9
z^QwmW-jG*|@d5M#xU%WRo&m6N0$vKoDSGiD06xS8 at LdX!0Pq at sH$Vu$Jb-8b1pN*`
zzncI80oFmD0Pv3nqtK%e_k?)3h+bShjb0o9-yM_b#d3f{07}S9hp(rdh2|kP?C)&k
z4KNvi4{#L91 at N5)@B_r70S16ZD?l;e*6<K2h3^G`w>?p4EA+Jzpbg*`sPmc!&<A{2
z17Cq3&<J0p5a@?KCju=rP2WX`M4Ny&p^%>Dk9o;Pw|(iwx1XjLm%?}u(6$uvZaoEK
zgS?dhg8=CO3<15^2k`bn{b;BY2oM6W$rIXv(iwm{1qu`z4KxHv*k~a at H2?(=2!H at s
z04 at L_05br*0Tux?L)%g=%;-Z*B>-)wj{=?Z0QX7w4s+?nagiVwnP3m)v5_mlyGPl`
z1Y_$w!bZ>l88`?XfxM&c2<`F)zE40%paA|tc_2Wz2Lla2ngeYo!q*p|8lV$k7$6kt
zgaOO}cnM$t;2ywzfP{+(nO*_;4To{Yl8FA6ro$u0BMFSVI0d1bFm5H_nEVkNmBRNZ
z(D63V{IngRMeq#;+T<XgbOuoRGY|{B%!YIb$gc$CH4who;ERSinFsSZ%}$~Lknmz?
zy9m;`@I4M=JsN>Ry;D)>%@h=R4Zs9Y`d1V>8T$7GxRnoO0M`I^<)P4%Fc%1Tc=UY~
zx)LA+U>X1)pm#qC4cv-C1pq@^APsN}U|=J}p}#{wQ$6%4fw%$710V8$)`TfQACw6o
zKNs*v1O8e70YCzzi{XomJ)EWQpil$gehuoC!ki#Td#9t&d62gYVA(U$bAFy4Xc?4O
zL)mxG?iPH1gK;}_<pA&tBo^V7eg)t|h<gD{Lx_=q(AU at smLN3o5kgm>&LIM1V;y|m
z2y}=GFoi(nP-piLtQ(+f5!9JBj)9aAUo#%~oP$D-9{`%*`!(=m5#-gv*E<t<1aK07
zk^^tEP^bjj<Yq&C022TM==KM=32_8g`5E|L0O$o^LB0s!@nIBt2-=Yl7Xo|@@gV>T
z`k4%%91r9lD?ru2(`w8EG4Kl at 7vK=UJ^;Z66uJS{+aXx*LtGU47=Z7ELYD$002~EC
z4B$P$Vvt7~Z~{+QAq31t0L%mY3y1IV;||@6UW53uvHW?^W+lYe0faL_U%=N7v5*U3
z0Cxlbt6s_iJOFDzCx?QLiH7eP&=bWF4*{M2yc9{Z>@N&-x+1CE`Spcq!%hPCefzBU
z?V%3)BI1ypHw`s2QOZI4wuAPa=fdRd0$$EWNlA4;8pF*sUOqR?h1`VPCd=wwl_w)|
zyE$4nSGUP-fo}6;^`6Ni_MOk0CMLP1yl3C`o?ZH$J^ycZ)!*!!*dKMerli}qrQ4<H
zkXmud^`l#^&qttH>2?_d^$z>JV>giMV8m-j at t+iuo$km$7ITuP(yBB&#W_FKRkhhJ
zebWxqQ+#6-@*Tj$Lwo54yF!URp3G6D*vnGw(sj5B#0~Dbd&}0 at dBLcY1GOD>s~9>`
zL_rGL=}2qZlYF((WUVD<x;yv*Wqs at pRudD~rp9ZMf3H9ONqt<R at p9%n>shzCj`cCt
z&O<ZFS?(8Zcl7rpU;Gs5NIPf|Lo4XdS*F?Y1V`d?c8am`y=noPFQ&ZV6Dyui&i4vS
z4TDae2NyEL9(gf5aJ!D7sU)Gegn&SfgDhM8>?KEjQL7Ln3br4<;{sp9_S2k)PsWD(
zwf;tr at j$cs$(Vo#ny>#m51tmsIL89~aI5jK%Y<R|`v7m at +msLfkcD35Z_84{^v0#j
zC~=B&Owh|?=mq#LMUlcV9a%+)l3i7!qm*m$9%z-7MoocFWNO$lDvQkWDooRx2_31w
zEElbH4dL)jIx_t7!74($#ubw+PIA=|6eold2e|#tKk<lj9Ao at j<>a3fEpuIBm{7&k
zF`9 at DmdJ&Cf#5fUFcV^rEP87hm1<n7V_Su8Yc4ON)Mi56C>IIc&Uov{1+^9iGP}!d
z?n`~N1O1_vxFvc9@|oRS8LE#veCbGM6<hWFzmcEhB)>2%%L_yzag0Whuam~HIrp_b
zaMI~lHTtLdrhGM{#2;gc{<ADtmpzdMYC>Y^D8+P0L#!A$HeVb|#y*jS2t<*&Cn<Jn
z6cFmO at y(<6pUiWjW>j at zl=6u@;YqpecN0Xn2=*S-o)o%EQa&H$dAaNx<>%^^lJpBl
z0H>T2GEO4d(JT*@$UGs!Tk5s?wX_Y=W1~i;JpKD4!XH?HUTI<3v%{{^VT at L2;Uk<+
z*?wS!{lFR?A2cE0a~DONgd#H$f)1c~9w#((NAv8^&@fMkBwzil4k3(@Un^4{qWI~D
zaIb2WgqlgcNWDV65fUX-<}P(j_dgOI&P<MWj}&Eu)&9o&%8kc#o$kG5`n1*4p9=}A
zeVqJHmoSke4d~#6T|5ammXeWCxsVkplKRXJ6HDeEU8@&o)J??luYGSRshtxZz9TX!
zXS++bLM)N&<nD=%6iZFfP$`o_HgN`<gS0PUipj9(Yzw4a{zzw;A<g(BEjNlYaG$O~
zWZe%ir+7#)jWpInPxu}YHyXyA5_!!YK3f^)pAq=gzFXl!&!De<FOM1xyGEumri5=)
zrcTdTd5)SaSs_?;E(~%UEhsYRUB|ll11H6Z2pyHq8cjOkDOk}G_fqzXFmXm$wB!j+
zDu{9ix(eI*E8#<3&T15W>QXj+l$xrJH;&<=rc)Csrl0TVEWxNe398BG6$o86Jp-|V
ztbonq;Jf)6e*t_-YLlY&%zcs*4>{$Kv)5%aDSkw6n=l#}23dh#lTw9LoYlrm*DBRB
zG72hU_aqbw3M-s=Cc`HSTJ4&PYiS0U>FrAM%#8Y~*!>A6K|__ZRxo at p?%fU=yNC{#
zCPo#hBdizcCKjw;Ld<uqVne!;#*5#mG>LzGvMxZsyhTQIKVG^6kf=8Z;?>qPtP3HU
zqZU1hURG;1lb^b))mR=B8w3!_cYJ)?p%r(4R)n;1nx8Rhe0B5v>e~^PsBz{fkLvpO
zj8PPBFioh4;^Zms=NTgOQSRy}x4eS)6j82FWSi!1i*gwd+#irf$fDT7DAvI6d$uSh
z6j7vc+B9Pn!-4WWOB6Ck5!Lk(#wbwyu-}5LTvm(Lk+Uiv&wCfDtJm{^;J-R(U+){Q
z7XQT=eF{dn)){?zY!1uLJ<k97vC&K$Llz5-k7Yp_I<i_`E!&)7{lOJ<<Q~i%z+c5!
zqF^isR-X5Af$Z;4a-17AVLmy}yLj61D1EzKc|xgfw;N|<s2d-f7I9{uFS3DxW{9t$
zWG%aXnn8GhK`|-TZ4Tud*MEmIOf7klpgfj)bkmgYBTjs!oQgLcUJO}r%Y9b7e&qUE
z2CQ$2MKRBO-8qLV_c}a>(vd!A#aGZxtwFe;lH&WUXywGuj_YWH)=OR>C~7&Cyk-<{
zxv-z<7*$O18gxAlEfUpvBORexN-R-OSOPTVXs!EW?Q&wSm%C%zH9)#oOHIxqi at gog
zj_)zwu$ML}jW_L<kPM^gvFQVj_Q&J47=#Sd>V?)Yph`!GULt0)96Th5wWN+IszIh1
zi(q9yQ6zC$WsM-zHIv0SoF>9ys3x4iL|K#2^=y}1HT2A~mZF*N$&E}3rjcS_RvEZU
zBRG4*E2}xtrIlRqNhs~@4!>00FxYA08dE0fh|6W-FydG at M@QKFCf=4-5hMMr<1W|u
zbhM2VwL*2Cb`(T>Atqn}CP0J3X(AkkTIa*opd+dnniAaY>d`7DR4c~uqQ{hXED<})
zFag>GS6U4?GwwG?P#n=&oY*r0c&K3Ud7RKb0(8_u%{iQCAHJ*tjQrn}%4mM?=?i40
ziB`r}>xeDPDyFEwQU3w%O8ha7ci(4!N5fDDx2Jf)AoVHUqIoz!Z74Ejg3-?_HHEvM
zy7U|wBSw*a6I1=XZGIC{>pDAWt}VqgHHFvtHrMRuS)UjvE%u8OrfCiC?WTH4XtrNk
z=(2BEmYPuqKBtGeomS6StnqT)_n7kdaUt8}b7=vgqtvMx_0l*KOszg7!>3aJcE)Wf
zj4Q6+@{`^COa{f_beOkhh)!XGR`)>9(3NR1H7<rkw-ei;7HxTEvAhhL+;JUFyj|A*
zbE`+&Q}xm+cZ?LTAx#i&Tl6bt5J#-SiN;^f1yBWkX@!b5S`qXj$PwjM+*Rmqn(IXD
zmo|nN(8{~?HKBJV5Btfx(&*vwj{-Flh*ML^%pYhdte0Lcd(w}{AETby1=)H(o<@pO
z3p!zT31iJP=Pbqj46^<)NE-txT`m)4<2oZ9V=WFi+INh#uzrS<Gj86NKH-If6P%r?
zb&qX+yvW+ewkw^s6kf9TZQgfnIzsQq)$kqoT6L@{Le9t{M28&dA9XBo9!lQxBQ>Rv
z#a=^$?l``ka-m#(<eL~Pnenp|{>EO#jE_F7=);_FIX-tDlk(a*o1n<_cdK_p$$m0f
z4F;}LuO*;+`^g7-k{t}oPH-|}`=tQ`vw$J@{(4n;Idjv5mD3I#%gpi~_P_nyYw!GZ
z)wI^x_0hLR`|dnvh$?uk_QvIgX!)y~HXJn+6u!1{{qT+r?~6BYxczSHp`pLnAML0=
zXgGSU_h#Fnf+O;iUpJJOAKi3f<<}VD$>FbV*J6ZUw_dEjh7r~pt`^+F2(QWixoHR^
zytVRP*u`*+aVWAj{M>nx-AY&Q;ihk|#sg!0>T>Szn)DMu@$hYCE2G}~*4n;1Ov42E
zG>pZ2lmE)+4xP))nie>FIQq5Jq*c$|j%s~v=o$N?==xU;8!%a~$=7e%@pZkSe8b9j
z!!8Ooy#IdIj>uZUgTr4mLpQstkGpVo7BGr|rzcmvp9P#t{?Tp#X0EO*XTH7=7#bhF
zmQ2=<@iGN;xQ>jS)kIvD!rTyr+$=wF7R;H$UR>%Gs);!LH5ns-BY^1BN6F+5U3eo5
z<)P{`uB}3f>g?uH93NA~- at BK`_b+9}x-RvKuT&nb{&>+HJX&hWg$a~L=LK%D5GrN2
zIf|DovYokmrj~N+{F>V;3>i1jUyYs$uV{khrw+-`Uw(|TRcL&;QkkEp6{z3N_|!G|
z#wgZ>;1qB)TO6d}y=Rx#@i#XKW!LTN-c+Vuw=1S+fK;01b9Np+W|L3Y<bQmr0?Ibw
zGPSd80Z0DvF`LQd%<Y}?LJ!ZKb>>aEu)tsPrc%~whgx|7N>CWvr)Pu-Gj`4%KX<Mt
zY<+<9 at Ud=(D)#an*k{B0UJ8;9jBM*4`IMn!mT(J+oYzw?h00tdVVArkFD)|3H7OwJ
zLag=;F2j at LJI^mB>t00lzZNeU2(Iq5aPRm at gi7TX%IYi1ddlFAwNxcp7mxOhfF?5d
zcUS6f*cHJUK81?6GIY$r>m#i$!{cxJ4|G&_b|}pqcH>N}lT5}f8P+w9)kO_9jSP>k
zpWb<w3wnuSlL$~dHIEL|ZR<3-OM^$j^!jPtg#~Vf{8s;g`s!xWSPkpXG%WH8S5?&r
z at b`6$u-nDpqADNhyv!}0aW2 at SK$Q21@=t0Cl+;3}k`gtJDXFr5jL3s^Owdsxd2pjc
zM~NE{=qUCTI_4>K#%MUNV+GB!w$r<2&2+!nu5&3Ms#rh_OB4w`ni_R3;+K(WH8r)w
zrH-aSTQnmbTLb~adIiloHlky~9#4U%W&IwN<&8wfgH8IoLO<(r+s~oWE6L1%(5CoH
z6=dcaT1UlvLBsgjUGI<7yWAev>fdSlW7y(mJnXnfH3vovF7=d-SzTF;cb?0gqGvD<
zv=2drfpad}fK!bRbSzOcx?bdk5n7+edD-`C-ud^o;u*N4`<}C{H4dlT?{$fx-tBS2
z{+&J51N}<pw&GBwna?8~OH%`56+^%PdqKz4R3p{u5pgw?Siiv~5HLg#Fs)Tc_3DVY
z3KbIuuJsCieJYWw){zFnIum!ar`NGeSdb2Jq<eI116SmTS9BW)ir>I$U>kgNjNnSq
zB3xtj9UTKUB*#`;bX&)yf;9_pe*3Sg`6JXq>wKJW|5e8ZgnYZQI-1{isfw)gL)G6@
zcZH4USmooyKDbWft+HoEpf`?qDkORB?92~ngA3=A%!x3HLD5J{AP8uy#}gs0_o!m|
zm>N8q*rJym{dEGv*s#N1A9-9eb)<|x0t<tqV>{<Wd>&6#(NASwBh=LVi6iQGNr4-s
zE_Cr7XyJ}fRD*|(T|d#l>*9=XZnTXny?`eY*2h+^hKs40I>LSn&iUYcpm;jPspkc4
z>tM}fwnePm1_qQh&^3sdKWlT!uIU96HhFF4t}9&Md5T+-$UDYNk5N*L&D=u2Afni>
z$z at Z7VOF5X5WGMbNEUG9E`mZ?2U#SSY?N7#(H-n!u7PNBF$7ybqU#gmXU9hexlkVQ
zJKMZ39jUHz2_!agm!{Mw7K`Fd)^V;dUQ+WFT`xM84O46oT0a0UuZn_!OcUX#bDxu5
ze*}8PWB}>sJFHfJ_IQWFZenWA(=UJ>2wo!?)FjC1H1vY|&2AlOoq~>O?U2b7<)dTm
zW5t at eP|Vw7pUHTFTSscfBe~ZCZK<4l+TfvYe7f{(1Y}MHBi=RC8o_&+Y4Ga&iF^JV
zQN6J93vOp+b!R2F;;}XtbTpXrvR2ymGi`fzv2D}hAB%pe8h3nQ0j@`Jv<*-U>PP?f
zVmMZVjpM at 8tzK2*e*7jF5dDKod4w<qW>@=EB1X72_qJ=iwlgmuJspnvRn7f^JFarC
z4!6S#f2Q33I41*}+~B`-f at v=d?*s6Ad<Qtuxz|chD&_9jv6#n+KK;o(a0I?CU=)(#
zabUUMOe+ at Fdr=&iVU4?MvC!a8Evv3oiXOT+40PB(R(fp`a#kE?trIDilA!5L*ElQQ
zRNRAoYm;!@OUk88zAc1}$nbDUzRPVkN66Wj{J>r+;?9_k_kakr$$t}@dMP+2A=-0Y
zbl$ohGD?*GUUho345DL^4YGABsuQAR;o<9Ifo43X;DeA;X2q9LuCUvJWMvW7zk!V6
zLcj at IoR5Y?%iV8ke<$B+ciCsBeC5K>!F7*ZLw`EaM$Tl8)tA2dXzo02f!Eyf1)Z;Q
z6O=>N1H^K7(~LFHTN4Yc^xs?z!sDORgVq8 at h<|nY9jo{e#KnH}e6Ls`=BHEMRx;$w
z1i9>bfx{`$WYNb)h$hGRq$q?ojjST;$gwj-k$~oP+cDwbNi*TJa|R73vIz<6*biW-
zImW^i4bs at t`jgB)^RF^%9fmZPSxrAZCbL%YA2KVZ{#9nB*kRj(z-QQk<|;hEa3MrL
z(fdLBxV`{xoCkI%L>)Mz!sT+r$_bmikBf`_u@|cO7cX=Lc%jEP7Ei+^r~aj6qr*Q<
zJ1g!6ERR<^ERJ6w+N(YOpmQTv$1?c0g148}k!AuH(A+_rXD$}KPJ at w<%(D+OZZik^
zWp_yMd^P~7c;^5Wg?TZ?*He?HU}sOX;}5=Sc>l^*jr+gyRpb31d?iKK=)J2GUX6JD
zYet9+>>^O%O-%7k7tYZ$FBP4j+&C)?!aP|-saz>{%`5e8uH^a at B2KtOk}r!x9BuLs
z4s!}UuL486iqS;&u|H)NMx;~@FK78x4<}X+%SaSI=Xl`YH^Uu_?U6{Kl<b~)x#Qud
z!-Xu$*ROoo#?FXW- at Iau0rSLarjK=_f at uYAE}LD7Bdjg-^`cOIr0;;t_*|+nv^?=n
zN`sVop>5m7rxyv0{NjlHu7k>9>KV%`8JbIU2}`E_{Ir3rD}-L|o-QXd>(H{_PFIpK
z7g6gre(t3OWabe>`Nk;F#%Cw+?2~Rk!~4f1LkeZmRJc&FF&ogv#m|6*x{GwQ^ysjy
z at j;0z at 2?Y+XG|_TJWRzEq^RRFc&og1Zj?!1-zq-u!hRXS>9NHaZZyu at _)19GfnjPj
z)G1DcX?YQ~Ce|DrZpc5T4bo9n9-w5e(k%yuOFWYIOo-7SvyfY0OWw~)zQf$j8hmHi
zBJ|w;N(S$lNz%U!XXQ6BTS<eoYsavTBQB)N-W at LB6tf3ljdM<~@+g)X92L5 at 3~N57
zi&<q`hJCaKP6_M$g1XG*D8z$PSZEvEG%Szc=#YE3Jg{WEBnzf5=S!G1$pG__yUT56
z0f$%Oa`Tm9&c>;pW4*1yy(#F-$*7erA4dfha(v`j<0PL}qbcEVZ%(TQ9tj>%hd+(l
z%{tyR)@B*B5q$_m7}!2qOu;XMONWQY7fW$@SM>0}w|SwJO3~lx!KmTg<1JT)>z~~{
zj$>+gmQwo;sKSRSAMsgQI&Zkd^=5^^TV3C(tmImY=$hw-3$~R|radh|!;0k at b@%A5
z(BV7zayIoervS)7S5yrwG&vlIdU(Ny@~S?Q7phfSX94B)EaV#w_5cg%Rp%F3i}N+1
z$S+`6Z5*;b3p#OLiso5lic$%OMb9F?sl$PwucT9e<x^(0lEu>avL at y>-(jcsQs`&7
zbXzP1<Ab+wxQbaX-8b4s=_rw&?xKcE#%121sVy)W-qbq-qw62BeJaac#9z=gtl<{c
za9e%}mE1n0^DwxGv~(AO`e4c#T-4v+8mQ!oO6Z#39zu- at o5l24o=80{ccnah<dUBc
zwvFTY_JbXkDE^4fw>>OzOWrd8mMmGPrycw7r?lVDL$Fg=&M|9*s2-a?taH76s at o4W
zelSL{A5wKcv_6egw;qb0h71t|u&|A%kmfU&OeIY%k7=Q)wd`iP%0owKKA>A#AA(?Z
z-zf&cD!cKpmAu?z-9{Hn%UT~=bLp<;hd{mi?J2XI3~D7U-`}F1endRtKJpX4L$|We
z&D_JOqNXCB at Urh8BCV=~WAIjL at tpWnieWmd@mNRxSsY@>cV>$Z(`A(pOE}3|ODmhA
zIDKbcZU6(h>|5yD!09^ou(T2j4hZCCKwr<|^x-l7y66T>Zq2!e)Km2oZ1K)Ke!lKu
zYs7B1-BT<L!>zuyh9OzQ<K3>i$G45k6w?N8C!>5SHTiVeXSmO_8wS=B{QAp7ngeuA
z0Vc4!&AG6OSzX5-j?tMyQ$Bwds!9;5FX}8~`_li|Z=A#nWbHICu=H(W9G*MAn3M}o
zP4&2b3Q^!1ZgF2HSm!%%ipRdb0AWAz%o6Y{=lMH*%h0*Xu&dZd-R__9@!at{=U)lC
z_$S}eVF18Bkt?GnP_9{CS;G0yi*p}6t}hnmwvX8|*8Q+;TQy)0P~+}+-4V7yHE4I-
ztDelgS?pECkS}Xu;;T!@_gg)77pH>F2;XRl_P8i&1c2Xd7kgI#?;tDpSTQ?ywR}M_
z51KULgK)O!H(JNmL#<_+5jyuAl<$FqoYjsR=~veuUpuv_icoNng+xA;orR%_NVuW7
z^hvLc at NWj2j<!YkC$ArQLVLu5hbo_kvM)UkPZb<DD`qS at p0(1e*!%S&_hP0&*u)aA
zbji&&ycSvOoxImhF>^OiWPNl4n5e=p%};R1Ny+-^UwBO6mrCD(?XD=eVCO_geC9jg
zvuig#9r|U`R7csVfQL!0<SOPuc@`--QvLe;+n5VUuEb8OYa!(S-7WJGosh6$`owF~
zMeft*JU#uT7ldCPhdG=3O{y!I<jRn2<i<neb#P6>%<QL6Ejg9s%G~bCfKs^7K^wVm
zKxz6$fulmqFZ8L`-*=QUlU!NQ7P8S!E>wOCQ?QYEQnT&`KJS_?q#})@vmQL4$Z-FI
zFg^DlIL at RggO1g~*k*fB#kQD<l)(pz?{kgc7c`uYUs8BF?2 at OAot|`wfB4%4B}9JD
zgMkn8isVXHR5uA_u0k=dAofcrhKbmVQQdr0$3Zc(QC%Pc4-NcORue<Ci!O0Fvq#4i
z-vFmt1OWqlaDxlw{#J1544?wGL2yNwqBN+NoHlA?r~8<UG3i*<>zF=2n`6?gKVo+r
z0)_|zrsfBngn+S$uwJ9#)`VH_B??B+e9xmCJs1QHU=aKUJzk^ny1>N^m&7|9_#avT
zf4?Oro&76)yb4HUYEp4Q`~4iFBee#nMuuR_J|D_r(qlTX1J7t;iIN at d??FM`Fl{u3
z;NfXj;moPFx)#V;gNI>V>u5g^(*!vQX5&1|_Y%jM|2D{1G(O<1vgrYJr9LJ-xv0PH
zJE#?HHilS?I~=XILM<cIdVJRmwK`z%$qRa8F3>)Af#jG9>_bMNl$-};IS=BK)f~z8
zI?vvApKKjV7B3Nh2YiA1Bz%Hj?*&d+3H!0Tj+LXg8t7wtAX9wN!4IR+JI8D>Zh{V3
z);H-A_Vl8sV4re<*#TCt0Y^1m%%<Gln4t`U`7wBvyWq)i>H?WCC~@<H<AIbAoJFv@
zYtkOTmG4aV<JrCg3RZe at p}pSAD1P<a1r}UHU8=Z&Cm=udfhzTZ-c3RYO>Y_9k&kvd
zpQ#qF at Ey>*Bctfm<M9?HZEnbDzxiXuufKkBOYctjXp?<l_kQ3rViT{R77RSD9)WY7
zNtW*g7IBjJDy<f;<joja<Y5%8(2J7%mOa=sBhvU-M>baK#mQhb)zWYeWD&h)79|Du
zcXm`KZ-GlAy$FgHd035f@*WqN=rx-tDNX*C{P5>#j<TAC52!7o8ai3aQixWoMJv?e
zH?)PSxepZL75c_uQ6-)1&eV%u-?(tW7%pn9=o`AXq6zo_xT)o$D*9%-QS{oI$0t`Y
zgrXHqY}iG;TuenXeld%ZE#eiw`ZB2PaBKux0qe}?C|2B8I))6h#3c6tuK+uLaE(JI
zd|kb;vQ!^o4Wb`cU?)I4oqjxH$yqqOzb#ySFYcnzB(YtQls0=SsEkrs1jp1VS2lNW
zu at zI<>WT_gc^!f_*N4Y%V#N-9Ij;Y6MJH%Sq(Gx~MkFGL7<Xt?5N;CVaE(<7VS)yS
z(IOnqvwjX?h!}?g5e~gIpV2vL at n`sy;Q1~B{4Y{vy#KGS9g<SQvIuxAz||4fm)ZD!
z%Nh;$n~tkZ%@pjFfGl^R5Ka at JI(Ofq-r8xAQ6Ak|b^9cBV-=|W5RMTxfwdO6uoxd1
z;b)5ws98uIA=D8H7NLmnm$67$#58fRUB`gCi4aQj8=LZ>NM$g{EXJ)uRjhUO5o=xc
zlk28M3xvuzwJ{E*#u3z_V6D~8ZeqS+?juwm-`A_RmY$bU<14fw9B1!V5g at H1_>}ko
z8x96p`WjEZ13w6}WA18 at y5>+)%~5?#PFLyu?dq-R at 5)jR-S7T2?aj#Ra4WH{iFrDB
zoPt^*qn78%LuGM?VhCiWUNFb)@G<#lUoYysQ)9e7$!6-ajNAXH#q^Wf)WPRiKSaeX
zRF3#V*e%5&c6E&>m8-u#Nq_mi^&T7Fc{x)>BAoIZu7OR`Hr|g4l+dBJh^NKA_9mvK
zMgYuE*91@=GHU;fEw*vUrvsPApkKAF3My;>Hq00VMD(Bk?55i2X=tHxMg4&9P}za|
z`5E^^2X^Gq+ at jBYv_|HBGe_bR?Nixo3N^;<eZ8ogKNVl%Zq5k7ciwOn+qL69OxayX
zw2jjf<IMzRAv}!4IMmOTn7EP-qPkL0^Y;DD4ncFJpz#XSE2k6$M^CsK2{$$2j-Zm3
zE2$)ug#y+3`%t^IP at w%1>J_?M2p0vxR1+*2!A4L?&Xp7rl74}7^?j(F*DuKa5bE6l
zObiQwWCUR*NCcHSxRQRN*(7L5ybrZIO at j8Hp<e#W_ZvKlE~zPrnL41SG7wa&<d$j$
zMrwcA()$t<Zc_1WwN?O?OE`*#$3-7o+$3tZ12VUavF`f?3b*~z7w-cdKtVPQG++wG
z!}`MVkZZ0vGstjA@;_z9nj<6q`k#2GL&nKCKiD`$cn;!nK?)R{vx>O4eJXvlKNfZB
zh!76-cWS)xr%6WBS(rSreYA>H*9btIzh_e%%1ar1#Ib%=e)kpXt$9aevc}8VSu)rC
z+WDrHV4mlQ-*z0wQMsBHHWqwX>wuwF1OnU4e&eLy&qFfxR>?sbm76Wpo3Sv7uCQ;M
z1X;p~BJuZZb?rnx<w$A7ps|?L8oha<{&Jqigb_aPM at YXPN>Xo4cvY4?M|~Amr$$WL
zs|ZVt=iV0eRi3(W7aRc^-KT=c#DhuptB>pXnRkuu``KW~5s_CXMb=iqE_)9yfT=0c
zfg%0XN!jni0Q8re at Y0AFZyg+UiuzRtb>BHeeGydCUsj82A>~tcYlvE;xd^mjrQk{R
zS!xpCxmu5dR|z4MwGy@~;8hUj6`U!sQd);1+J<Co6qJ|?N0(JBqw<W{%ub3yDe55w
zdw=e3HTyuQwmP}p!>@UWn$%mE6tgcWCL<|EQgdMlD}=9PVKE09FN1h&kJ)cJqx$>M
z46XJHK1Z_x>Dl{|_cFl_(Sm6}!BdShz^UyUf>hrOl^q<SUNEY+R$r8vH_T|PEcY`i
zCHfn{&5uB>s?HA?{HR5a^h8jqP}bnx^3{-KT2Q`zi1!y3$Ig4&otgnZlS*2Gd`_38
z51G|lo3G0FRD0!&O3hd7n&KfC4W-GTa~r1@`6b2d*D*nZXEIT7Vwq|P5Q at uSl#mPE
z;jNdCHP*P!zG_~-YpXZ^+lN&o+Yc$4`c%h;sKBH+6ud_kM^w3S3VB})7nT{78ycjP
zdzFjy??V(HOme*oDv-ixp*)CHDCJGHXO2Tg_P*Y?H$L?=HSSm6sp at Gn#H+yo$Q{`=
zW7zwVYTz(8Yn!az>+rV$)Ks`1e!;B?E~((b#SWTnrgIxiTA!T-WtkS5n#%s3{zO}6
zqT%ppylVSUNxsOyrreucq+5sb-x(@<XQ+HuQmh21$Xx*Sj5OsAXXmrzsg0K!A|RJf
z$+z8Bl?4n9M>yFu^02*9(qB#a>TgUM=~sV$SASUpSL5cF`>mg|R}rhYax*k5;ex{v
z7DPa_!=;ZfsNh#Cvvgz=BOTs1RF<cR=Fm-Km5Ywin$kyzpQBredHK9g at 5%d$xzJ=4
zH|8?7rDw$sQ7m}P8L at Qe4+%TNPKrXa{Du^aZED7LUg0<ah}Z%Tp4&JDBfaeLy;Swq
zbwXLnqM;eV$@7_dWp)!AZjj6q)s1<Y91te)`@c)nX|`(iP}y at sQs$6RB}v+}IcZax
zbswz?9V!cj#UR>8D^!v~{9sb;ALOIO-}OP_f3U0Aj_3 at dd7{xY735$8%#HPoGZ#NX
zGW*Yf(C8oBY^sKc-aq70CDvmyN50KJSVH4`+xid)E&f3`TtD-;*K`FZDQX*t+g=)P
zfmpC~Ovn-KaR{0)YI{=D4r)qL)VqqPZIqiLDjixTtq+Q+al9D=3kmBa at nJS>I&XXp
z+9{K*GTd{n6 at Bf*MlGLmY`z-N`%m;({=<9A8kK6<7l*V#VUZG9QiNE^PKrFBPW7W=
zLoVx6eHD=z)YR$kE}MRE_Vg2>%Ur4P&}kM{_$7F^al~S;bSdu{Puw<-n*!Hf`2KQc
zM!2b_VZZOd^mn~B9wZf!RUvmao*>ncRf<$!STDi)$SU?Dr>(8}@t$H#@8<lA-nDv<
z>0Qwtc8<EN@*ZfY-9R~<ulr@(i)g$TxE&~nOnpA`VmP=w;}Ir`fWYy1RhBv*zh1Sf
zYVJXnS%}$ebcA|GsBnZ}Y(u`y5n3GKf2}#`5q_ox6;6)>ht6rI^PXeN$pSA)!fSEh
z3NA4)d<qM_&dgaxC77x&J;$$NtXAh}#aqEZ1^-}(UL6fTtPHyt9$8yWv9>VJU7H;K
zH at J$aVu<e1&g=)*?kbmX!(*y*k2b+_#?3Wpc)Sq^9&$}#=f~a)a at PlC$buvldi~~%
z_!*`0cV(L$5BH!>BuR#m3`sOMe6}ExB!ndVs(>pI65&FDz+Zs(nJy$u@%&{1SvLbk
zf{thT?Yoz<_uh6R+`5ZD8P!kEFi$@~xtAoCq?BxHi|}ds at dDvfY20|AAw|D2qdc*E
z8*C!*@b>ScE$`fGBCu^VGvhWD-w|(u2d;PUTdd=~u(N`BWX&FRRDicI1 at 7>^OB-*3
zohZvurLW2bDrw}S`tLl?Yo at 6EJdHJ>TFqR|IG+_}3Yp`u?WBGElfJc_xnGbmt&WQ_
z7+=b(_wtgs39l;CR=`}uI-X!T?jRWac+o02NGn@*uhhr||0Fnrzs#Bd3m-PDnXqBu
z`SK(3qwkfba*L+Rgc+ty{5VZF4L9+677=&C$U<PZ>cMW!En9RiSX*&U^eX#{T736F
zG3FEVxuPz5QHJO8 at K&Oj+g$oua}&4u{r8&thAa`wWf9Aa5wA3BrT9@^#LMc4c%?i-
znRj0i5i5&`Qx?Ccj#zqr at xDdbrH$1(qD*v8d=vI;ea%uZC*YjYt;S?C*CfCT=n^H2
zs<|AVKQ8bo%*)%Mj%O%4?<?UQU_7$K6UyQUbv(Io)paTd>Xq{$&=K?b6lYnkbv4`Q
z=WX$u2+wt7ev{0?429q1LXRxpM>Frd(6HQhK&{-B`LjbuP!puAdxG#CdC}bLXh0FR
z;fN#5$4r8CC6MgMx7+gM+#)&T%dyaB5sAOqfN>74Q{J11)D;JkCLW;3P`LJ*hCKmj
zH|rByke|wCD;09JNoJ9+g|+_<(FWIq^WNPq!nEqw2U5B4ZI}Q-`2=<2Zmn7ahX;X>
zRIh(F|CX&tp>UHxO0hl^LRFV-)hyZiK(U#o7CP$8P3*84S-6IA{X7T(V?>)RrQHS!
zvzc~Q*}KBb%CWJsEvzgF%>^5I0PNEa0Ti0o&)XaQ=20_5W&S}fxADjn5ZU~oo=e3o
z$L}+ at ub)Y=;7h$|AdKsuDJ^A|Ugmc8;?@?RK4ai9j at s5^k|6^^vwu)PAI1cc(;g3&
zEl{4pRn+SP^UuO~aMXVm81oPb^pp9A!CDmvcI1C)%d>&~54kmaZTx5OSsk3NclJ2#
zcgx!u)J!nup%@;iHQ~plsuMPafAE;q4r!^<X7LZn#)G~yp7H?a^*3u1-Nftww|3Jk
zK1bAqi}e1pOG}6J$>EN<#qT(&MT(Pg(%H61jR0P77s<1y!NPxveLNehAyBUV?ED;?
zD#uoI6dr>r<pS_O#@fQVDihgsjK)eZU2k~lFB_}^HrU2*z>zktg4ZFu^kSCExG6q1
zip<`adY$XDGYK9dY(!+Zk1!#xEH95)6unV)mm)X5{!e{vFW4Ghr8AtiH*~*7>R~HY
zfZ)kmy at d4!xCEXGsuOzf!94(eSm0tCwFVR+?idbV#%R;4FHlsAeNdntXx1uW;KtP)
z3cp0Q#NoM#jKi)}1NN(uR(s&+9tbus at WGO=SdE`L%ci;?uEceXQ{68ud=RUf-?VvD
zwaiu)XQMu_ET5*|;;C_CJQ-)=65H!xJ^^QECB{HxS-{!A#AuspImG`GFvoux?A(bF
zknjzd!=Pl4SZvFWhV+7fIc#h%&h?szclX4Fwpj}x`)<Hle#Cs+62%=V33BJzr1Kz`
z9WbYjviS}iR+#ZI(8r3)__pn|qE at Ip^n?!8EX*%iV;<f#4`_qHSs|$$IA?%I&kQQp
z%69k`5r_|uOlP^;V0e-14?-v$3$Y#%uE#E(aDAR5Jmd)9cZ8Xa at USD4JHjjof5$M!
zd#Q3z5IG=;#%!k;f!J7@?>$yVi4tBlpPI;<F?)uXGaIfO6%6D!NtQ*SHx<2(eT~Y^
zN46 at 4^n=0DFqryme-OyuK#Hjx_*F^SPlMFdim%l12OM>w+M&T0n33wO;gMjkXHQd`
z9iGxK3#2F4kvOY)2q$h}=HQYJ%>#IEGFH-Oz0az08B5$3-Dl+&4N{Xq&||?!amsHM
zLsdEmmZ10^95O-_7KDwJpFgNpZv_j9a at 7`&RmQT;kX8>WU>Oy*@Hs_tbzsK6`FTa2
zN~L$FmJ6<{`pfakLQinDRgHsXje`(ZH4K&(dO}383Eo-DTFJ1LNK{CaeFt)zSE^Np
zr8(DH4eGi$Ncn^`q<lNL>|&Be5ORp$nx%@BCM3y1!({VoR+U9m45qCJxxW1ea{Hvo
zakDq)Q_PeX2PHc_cX!GBGnTo?>K}hRkBnJ_$`kX;1|@RmVRB3y%AAYpo=0pqGV>M@
zbCZavBiQGNm at t$%3)O|9Obrq9EMn&ob$=t+Gf`$L!G4(lQ-M7LWiBEz15r!>!45z%
zzC`9EB9kCuMo^s%pG9W^h8DywLouN!<_M}|p%@X$<fA$t#P&g%I}v*uVyB{*6=<nc
zM;d`^r8`>7EGHAXf9dY1u52-iaw^VM*E97unEJ~rt=H at qrt)uDP9f>RK|UwnZLqjY
z;Wh;h5=y#ME`v7F2g*XFA}GU$&f0Aj<=Dg at fQC1VbJjAG)@M-eH?sxG9mcC9oZcBJ
zsiAAY#Kngmr8_k5!y9P0f0lPyL?75nXlNUfre2>h(BbLRpC|Xs>avJG06aQoOh4S{
zY370$YRU<ocBJOkKt0~Wh7%v*&+cmwM2bI%6n{v~RF}|Ht_+6Mxg&&=dYzeCx2n8W
zA<B{Tw-~yl34_&_1!-Gh*0cK+fSRqiO3L7Wy~O;oMA0CLjFp;|{mmCVZA&HPT~(y$
z6<QTDNNq8%&#<nCM;@x^LGgN`k6jZnNO@?#26YZ<u at tDI7J?s1?YBnJnMO#8z9e`v
z23qC}+8mTPhd<EYVx+$7?l5~ue#|9fYKV{~z?*pv>WgdX-l^6uR+YQj|DdRwDLZl7
za+S1Af7<dCYhc7Ep^Xn;B<nsRfn1<c;dWTDJ|m_WbETNz72Q!^CDn}*wR<M8FH=oN
zo?N*y9|$`|@Y;WU(nP*-K3Nw`Zrn|>!^w?VWZhbFV<E{FlN(FOn72uF!`<ZeTPlx1
z at d~1bg3&2Uhn&O%**dcOm$-g$9FdugiB%E2?ssIOoK=I8Ebruk)LnzaiTrV8j|S8*
z+JqfSO-o5cg*GzokR?uHi+kT3 at IiC)eRG`D81Ml$%Hm}DfDiO>2P5M$6#+SU1x!WU
zVK}KTAWT#1Hj_FQ<s&K}sHs%E9!B#Hz5TiyFtmu^O}(KGm`v>zdy~BO6c}Cs!Askz
z2$-xs0n1pX`guTGVsw`nO%Ia!f7QDm0!lcQ$qV<W+6P2SiJCTexd>yjXat>B$ABz9
zF)iP(vK|E=JO8zTvK0d$)FJPbI%HY8dLV1J4Z6Qcl*J9G8U_r%8BcgtSIX4wj0&yV
zU6Q;yv5JNDO~Q`6k6OAL)YPHvl+;YYp_2rk$H}?bJB^fVdm{W=ncHq^)CT-2nER`5
zVFcXNpC2#=fA}Nc6J{80N-X0foc`pe-sW|`1w;QtEXPBC1^TWF#YV&KYl{Nm0}^(*
zMmrGlLRPKDm(C2LsTkmZlICS6!t$c_iryzuVYN-o^vBY7nBc|LI~%*fr?3|CR?&vU
z;x6Z^EgTTdCHA|6oB)#N2>$)_(xI9o1Ip%qpK?(!kpz@@I4J>C{M#Xq3TtJ+nJK^L
zg>Z^hA$?96hb7)tTm8im5X2btdkr1gn%`Dy-4F8kO%+KQ^0yAa(uAo1_Q7nx8ZG)P
zZL5wgi<O8A?eI2pC(L#NmIR26v4h=bcZ?{c5<ca%zl&NGsXe7`XQ~@lTF==z70Eu>
z7Imlueo2U_p;m#FP+f<!F%;>@5M5iX=soD+9MDpziXV_&XGS_`34f_YR=-Mn5`OKL
z9j$}cen5+LD=g)Bpd}ip*-AuWF0&i$N!KxFcV7!R4xg5n2J{+1LoG~2eNCyhrTu!l
z=?}U@=WyrSHw2nRQ((^de}&17{0o_mwd9n#bMOk<EwSt_wYT`b<`;rDGT(uB5jL_F
z)1L@;APBe2T~b~vtACI at IRwNTWSbNHgz2*6$Jl!SVOa|w5SG1dwMJD4HEJEPvnLR+
zgs3$u%hadZ at j0Jfrah(ARGf1h%H at govxXD9cmvelZmWK4P2|f`+shIM5 at Wy=vr=N-
z5D4eQ#y6)>qi~{Ny>E}n9-D~0Q0u=>#25DDZXaM-yylH33pr*#ypGhYC3qE|scd&e
z<FPUwysH^U)b!t_Je-=wp-{)vjdOL1-k|xDLE2)rjP<z+`gDpj%wG`|S99|&ND?p~
zB&qZ)mZY?|8Y|(gs*YVnivM9ZHs0m+`hr9y5j=NHl8-nEleF9nBE0g?Lh<6AT1|_c
zm$ryA!h6S`a%U%wbt!@_SJhH`VaC;>$zl`1%T}mr at 9IU9u}Zis)1HBe`IOo??^TPt
z%<O(p($B at G|II^Zs;IvzfHKJr{dJD|I*8~h{pHXKaJ*Zp$ozlY)fN}YVV at Cq*a25;
zVrwD at 2lct)glFC-G7u5a4d$0+(%`$<S&^GE>O%eyxu<}lJGDkYUAhB|A at j+V?bpE~
zf}OkO<XyZP;2i8I9I!QBZm%@L@&8rQ+$5>4W*V1Ejp~WwzhI|oI8iN{B7+VKLpb^k
zfv`$o69nx5=Nh)9vKa2~tZ9}0E at W-B8~Nj~EofBM^OuW$uxFA8kn{gy0fAmi0?(Ls
z9kY(Kt6 at u>O4?4?eF_w-$;020b<ZLGs!TqpT7HjyeFl4ElyYC)5czgS=EKpJU3a^N
z`Umg#TgFWZxs6|HKa&Ad-gbaJFbae^ma;1SZpb^2YBfFZ<L#O)cR`P1FH1DvDIKuB
zt$?tsv%jJ|@~w<a3nXH?MnT at 52aPi;|JA#{YYS~5IJJ2fnaO<bysw$bl at nYxzXL<S
zoA>uN*m18&EU(CoYgwYrEcN<>z at Ly3Cu>6Hr!GCLSenV6f?%AD&_iAQD9F=W;+X^$
zr*`Px+EM*g_=&Sb&yj8P!G5)acB&*~nL`b+(`c%kJvye<u at jV4+jXvg@Ejg_OUPa4
z(D`>^?%Uwlr>=BVHT$mkCOk}4h26E at z(ao)*2<XKA%;D`z3Wu};3?dk4<|mDaV<f2
z+YT5(wpF95fV-Mj)W at t5{m!rHusKj)@R#xbE-tqNj|xWd<`FFNd!l4P;pk6*nJlaO
zc#O<tKql5F4Ykhoe-_$h-iNN)S$Oz4VqfRR6Z8J4B_FBZ`Ve>qkFp{_nj)7<(kg8?
zn6 at jcz>@6X7%y*W*k*28t-rihJjb5g?gs7O5Kkf4QJ`k2VC!T|#WEls_H-u|U~Z@$
z_KQuZWR~hdzx6WO at 2<MvFTRXw2Ky-w!z`!T0FAMyvZBIajA1*)`Yp;_=M<eqZv*)b
zy$tW_y`C)mnAM}ITm9BLRAT`&|7qxGDp5?rXiH0feOda~(P?W+-%_ at l)IYeW8{6UJ
zZJ7ivHJ3>Z+nU&#AeJE|{#1Hv#OgZZP4 at O>?dD}S)Nj>5r<8lh%hEOd3Z+D`A<z~$
z#j-vlj~3WAO<^w_Rn_&2zC|^)|Fv`LSBO1pRP{~2^#ZE7 at L#KFz_Je<Rh{eCRA7#r
z?H5&`62IMC)z|&-gG$Kxs$WzNzr3sWGjM-rQDdd{66TH$9t;4hYEJiybtsbuf(!we
z0Sqv|GAE3x6#e3}s3z~fbnH;cE3G9c<WbCDM^#1r8a=%ERu%S(^jJm<`hl*RkD%f0
z2-|gL+}Vl>nE8=@$Gm9lp#J$ju+BpEYw|&c6l$a|p=AL%^-@@Wpr4G(50}b4^Wc!<
z%^bGvG2CeZQC{$8 at RClh3}I6 at sZS_hi%KG0+b at XB+jgLzYA7t*&FzflZu at 6nYzn_T
zJKB^co0gF!WfO3UmN8#S?V?6X^0QQODZl7r!}{`wHopd<Br!j||A_}yHvwg`N%lj6
zZ6WF!3ATY?mk at P%M5csbzeTWLBiIXw%x8%@K9TtV#rzYoub{eGlv#o5&Y;ZCQA{?f
z+k)yA;)g$V6oM-(-+>pfTH3-a;Fi_&slM$4Le%cyD&gNy7w&<Foag)a!FtG?;U!Ll
z0}L>xdl4_~ajIhjTPFs?H0 at 2;&{FGGR<Xrj*bDNLM=})Gu+cAblftt{$qrep^xQnM
z?jZsEjIZ$mcK5M0d8i;!u>l-bK=>=-t$wJg>ZA6mXH3?l5WGDP^2_ at aK~GGHiT&CV
zy}Lwz`9X5<&fo21pZDMeO`>r_hV=*nypQ`j+$|b`js)`zwzBSDos$ngT#PY6&!R5G
zb05@^PP$uDEI+y%uaU;%V9 at UB?tpPrS8u2`DgPLYNM#4t|5@`jQl=3|WHR%o<euW@
z<_*nF%92^z9`>o*@#@;M%eF*nULp|<F)i0f%V*^9tj^{QopQhC4&}&be2494w&j&}
z*(*29Z{jspkf>KmT4a6dYuNoU&YS{AkfmU&Y?;E(0VapVNr5L|!DZqR)x(JT-~bCo
zquCEL1=4j5 at hWb~u95JI{jek036#0YLPIx5W>>?RJiCDan*v(PP|f*1AcvaQ;SQ_G
zfnnf&i)40`!+Mv6ex(EvA9>i>ufz{_z^PcN*@>JqDcog6M-*{z>ICF&z6t^8#{BY&
zq9)*9hyF*VlYu%Gunb)7igTK8?5fj!Fyq=&diR5BcNtvSgTt|kR7y4^7mGnpej={J
z^8IIwR8RRu>m|hN{;{T@&jO;<yrZ+DrQHO^IPCho-1Yob4&nHNhGF8-MQbhMHC}@;
zI!OhQSQL03jw!Yf(8~=_qMtHi((o>@`6_h*K4pLDD>ZZ5EA^cH4HtQw)b1hvJQ;BL
z6^VnH-$MUaRJ;0&4zmPok%BUZr*t|C+)Khi at vL|HWRBU-o^AHeX>Mlmn&9t%h%X>s
zoT+SMpTa*!^;#bs$|%<7XpHun#6HTS3bP!1d8bwQ>*{`M3IdA|kDpqhBY{z2v4l9s
z-tu!V-=)<Tleq$uP;HdWpNVfCa$tum{f3!d$xXng4nv{?=u>=zyZI{rd-&YB7&FT`
zMo7Ru_vNFD;;(?7i;gO0r3t=eQI0_+?5O^(rR`#4rI91mb0qNEqp`B)pS^I)x8^yF
z_<cL4J$cKtm0U+tn-cpek1m39+1 at J)_>ljMWkZH+vyYX_sHx~JGxYMcdd?K}hK%Y;
zCESnb$W?nq8TJ}ouj6E6e#oEaw>6^eBB)(nce%=|&su7K!QCO5;#F{jTr=*!uT=3X
zh`MXTd`g_y=b(tnhEK`G{G+`E_)oLIU<0$Ji1*v)5udU$uY!8Eo`Ci6m0r#43hHZN
z_5tGuY%=I+q{BEfN5_J9CB`i|qBNxH?ajCMrfLKrv{YcxG4R?nr-3MVOqYNuy44H1
z<41TP21^YVL-#M5&+OD<2Q4t$pMt*A<@fih9V!pb3W|!bNhV<?7teCgX;gjzUsxLr
z7x9v)0JML{212Fv80t#v9Zc&5dk5rc%;2GW+u%6T(b74pcOG37eT;ZhPpiJ`6 at 6yk
z;;#C(SM%u;rdeM?HQ)AHG=dKI1?O}mOx(X}IO{@0ed*-evR;F#_`cYK;H_4dZSGZF
z>eUBNcADo7XKZ7Eml6ChQ!+mka*NAgGQo0RT^{xpaDAm}Q<;ve_cLn*`cv%{=bO#4
zZB at M$ReSaumq@~vNHquTNpIG`kL?6#FsWCjlvtjf0 at o|>e#i386gC^_uVG74!I2gd
zj at u_iy!70Bl6?k!>RxvU8|P!%gj&;ZQ6xva83lR=L%|8skrn5iM|a|bFs<SYJ8wo%
zy~_A+N at IE3Ma?0^D>|y$*Q?0^J~`FhY(%ZEV%O}%llWgYfEoXxd=>-0CBW~zC))vN
zRM4d$9AQhPf9wF(eRkD`URWhqP&6~Kpsa&Fo~UH_wbfr>h5XA*KKtmRW)I at +$xd|)
z4KV(Bd$h{!s`y at OhJ8Oq7>KB-Dwmoif1niaw1brV8R2ceI~T?8f&9In|8g&87pR7c
zmWo1~n<V*Z7o(dr`2ySQCQDw+hQAf!SUQot+ZuYThwO$)z;T5}Q0!L{oTrefQhSR&
zk_F``<({(5_}Jbt8Q7MN=Z^=?Iu{j3us?#Qsqj}C-ogyaZ|nJE13uTIEpePKx18_k
zI^SdVm_mKwyrZvK-rKNWya~;ZsQU at GenaynXko?gqcAG5B5t^tH^?kibEP9a)I$A+
z3 at vz(aA at vx!yG43dGuPhVwq?6inju*RjAjx#a=`9g5U7flliPT2V(zZOSe*p9~E=R
zKKN|!>G1*3=_lfKsBWX3&tNO;st<a~KIj1xhCknF#7N2BtUYya+4*mSIECTg*YWo`
z1Pc#7ShEI|ecm&_4Ax2J0=homQKG}{=H{zrCDyZT7pcqQ)lM10?9r at 3^HuDXWA7!b
z at 7j+y;`^2SrXDrSkG6%1gj4>YjC~6ntcKYHd0vH*+eceA^-y6(k16oWZ2Y6 at kc=|a
zTfWb6FCkM)<{Dt9*W`nRRra`t@=&er8C=^VkPTRVCY5r0H`0dPFY`(cgkdY)DSWJ;
zSc-3)l2+m?skU7Z7{^W7ymY{U9De3<96M>o9;eo!j<<aftDWDtc|@P)X^fkuZk(oB
z2glitdpEdWqZpx~WiIO(i}bz&i_~NJi8Vd1hxrAZO-pQo$fkffWXuedc&_JlJ()QZ
z1)L?}v>#tcB{oCxQvqiuC7#D+Y$23ApIC<D at F!sd<_P$}_C!5Y3JW;vn^*!7mw-9$
zLM3d(vvfUkz2 at QjtcZf1C5row3$HJsMqt34$wK8P(C`SPx&qElkSco)E3OIE at K`qS
zG~~S%FsF_RgM<naoPe{od}Ytaim&SaNvn?coK|S at yd)lTpgPMf`>5x<;^VrTbfOe;
zgaLDMLNHNkNGybrfcWBjx0=s;s*Tmjy$>?YbW2`Ov$;BX<^!P;^mW&JJ)M^7<d^TW
zP4wWwo&j5R@`pok&OhsDPiR<m^2K|9x>uv3w+(hLt6P^U3v)S;bSW=wMWi?*;K)Ro
z%YMo&w^gxZpJK^=#gYSxB^in(zyo|YnR2iPmMh!_No74n8-=_Bk|<9}lIQE*)63 at e
zsHeY}r0R_P$J60ao(V!vc#nY(J~qB6Og?a_n<Y_rDw2Uj$Nl at Po^G(C{U?ibV6eGF
zT<T7V>4BvAglDr>w(aenv@``<La}(|tg at Q!n$10&JG^H+Z4Gwbvt0OX4NvZ6Y50wI
zz{A!s!n- at 6CbdV_@QCL+8Ge4l`)R7J;UVvHU+RLa;Xbce;3T)`V?`UGa0`s9$U7R5
z3x8)U32!DXx^5%$J*S+5rEuJuU<c=^<&~bUS9)y!p!d})*oU0!B`?ogd)hc7u7r at 7
zJgW- at inKT*`_Mq4Cm4cYvHYIX2|3>}L;sZ13^|vWo{k)BgQdk;*oX9Aj_<TEdHsCI
z*|#vS+Pl-lEiH$GaDhH84*udnBlz90A{W_>PM(-#jkQT{1I0Moc*DbJD%IcMz^Rug
z${zE8#<?~~UkNo{2{&I^YPqt~c4eJ-p*`j<T`_}NqiX5an;q4kW2&a~7*&%U$vL9g
z%(`D_YEc)jr)6S~O(T$<jLXgu&@Jti-Q9bBuFxuck11Z>Z}vTw{cUrrpZVo|qEIIF
z2KBbWf6VKl_7@#Z+MI!BQVPa@;|-&FtH9LKu616Z<F}vI5Zq*+#ecHxgeUx at l-l2Y
zD`_ikV#WSK<Beseu@;Nq${naYstIB)dV|XSZ!Pvf3u$k+`BZCTPq)?xq8I|iR_L>Z
zMq!3%I#VyqppM)8C(BNFN@(b6WGLw!+?9AIG|ZQF=gFKeJ}iNXvK!r<|EIlg0c+|?
z+uhl at a9bh)j6y&|1d8AVg4SE at kWEBEoJv}$cG?-)kO;I`Wb9O}mJk&|aSCZ`0dXct
zi=x#TNUe&OnGh-7u*KARgH(uu#I_EmR_#S{zO at 60{&VL4=lsue&Uwz+d7kXG*X{e(
zde?QY^{sDJ%8wkeFVMgw0Qx0u)HdaU4ax=DJJMSZ3c<lSCEge?)wrOzuXhzAma^Ht
zU{hb;UFU+0007$BRs|e}^1l)|NdGz#dDm*WbsSxEN{Eky*&1yrcFTX^-UGLxBGWB*
zrEEBMt2#>Q|8{J`-9?M#F9!P7pAZ_Q&svmk<90(oJJ7#XaBe{tTh8>aT2If``KnQz
z$Y3rehr2bVZ-iOr4%KQx^b^nU`&%YhYBFihfA82Eqa!tld#q6%H2rG3(dN}K`<C&{
zRLfaEdvxJ~z4liN7aTpg!+9KmS5p;uDXiGtI^ZtDB5WX4b0)m2<W>l}_1&$b5q>;4
zcZBzmGl;aG9H8!MOb8dCnu+%cVHvlyIpRJnhSlD6nGBkh7ag)%Nk;4~N&NYloZNW&
z5}AI44+~k$C*eAaAV{-O9y#WPd}b^-RimDmjF-f^zjGOy9TIz={x%*`to#D%*Pc)9
z3~=&SmR+Of=2LT5QgaKaxxc68t^zKw6|DQ~NZ8oaW)=cTH?icJBVSratZTa<j|_bt
zzN>p(q9ZVKM&j<9t~>iNaTsubI8a93s^whEUq@>B1x7*8Q<VWa;B)0kKI04XLFVG9
zAFQ&%nnQ9m<na at iZhT?+M^i=KBYcjC#3(X4*>dKg=$Mx}#lPGds15}!ViAsh0SCSb
zB82 at A_>hA<nIi?Z&WNC{hmH_WZB|sL18S<osS3LDlLHpB`fmHdGqkT<AE*uvUN;-1
zy88u4AaGAbxQl>=-fsb9)z`31^^-wxtM(mNo;Q4FY;!Gx1t{`XFURJ6XO)BbP;g#@
zb;2b--R|Dtdd3UZl_A*ME`9k_bCu9Iz7YDM`n`VT)e=~YZGi((SSgLcReS+YDMChE
z!G)UK438 at pEP5N5r(wrSRpaWq=%_gFa0-GX+VXst-Z`ZZY9}nFPxNvW7CW#^E3THf
zt`C}M4C2DnDlN at XQRARsL!TG4=uLgWQ-yo0vJc5}3N>m;&LIjM6?!VXaOSGPc!Orf
z_l{Rc8ioY2CTV4YBVVi6MirK1zc|KQECo at cr?!79)w^(D;29=!$a@`8B>FxDV@*p7
zi at F`lmfh79`+-$U<EE{IexhX2qPs6{2-Iwn3^{|coY>f at ksFxCh2zqN?k0xm-ucRb
zNa=GLyIuoc4l9}j#uZBeh)4fH{)OY1F_M}QshlR+Wd<q8Io at vuM}ryzL!hG}(191L
zHsB<?XL}{{jowp10@%b`=kxLOYA>T=omi^Y9t-V at J!@{@JIt+pZJ-UU_0*A1jqP5>
zGwV$4-lnrJM_lF_A;#XM at 0odB+$Rncq)V(Z={1H1NE)Q3(F%_$i!^tfG^J9SQr6Jm
zX!@(;{9hbh`y6-nI*RHX+T0*#Zm>O9Y|Ra^<c6AZ#~ROMb$fA(K6V)D94NKxW5=C6
zj*4B5^VfSDK6IG!!gP7#op}-Vyh!Q7eU`kZjCoRRUbw;Ns1<;8Qqyk7`C3QUhmJc&
zN0HuP*y*U)>CokhwYeeA+)#V&SZnS$OD>FRVbT=XI(bdS<w_GvimDwbx8YBYrW(ij
z9geOl$DI!x&P-U#4Yp>AEtw&v%ur+ISZ(GQ!*)jm!3ZP){vb#7aYQtTq=e}lqFek7
zokL#kK*2iRMlG*c$=i(2bu5-Zp+*L+HNjNjHzO^lABvvwR3}fV{TfcuqEsUl4#%gx
zRU!g3sI{)K3hLCMq{{9(&Ij5dXun-hXQ#qz17RLL7S_x^1`TBU&N#`-TJkDIwqyDV
zNkYP1^^{SDRgQ*Lj&r<-zk><{2s#n%j}Q_vIv%=9UWrLir!VJu<nBn{`lG7bm2}f3
zqr!J`O at dl9v17KCSJKwWv#UjI=Xq^M-ZdrFLKgu^+v#l*>_WfpcvZ_QIsaRS=6hb>
zk#{v(o;I)0l(Z{}L*Q8k6>bvjJ~q2^j|5^sJSgv(iI93RdIs(9_9Tki;80a~!*h;v
zeATC1CS#B`l?MR|H!<UGUMFU`dtM%H*QXPcKnXWsJJ`+!P5JU(SiSrA^liqC!0jWk
zP at 1@2nusTQxXeVcmGYI791A|gBeGVz<xSbV4Z3++<-8(lo(6dum(SNOPcFE`q31;n
z at -FeOQ1pU$E at e#4HYVp7lk<(qD~&l~X^y`vCsdmgs50V_lxgc3(|b`$_S2A+5&;PE
zfmtd*qrjLiSfs$#WWlb0Os$bnl7u?ISN9=N{yuirsr_MT*<>K<KgCv)+J9GkfVjZt
z-D(}2Q*gV8-~8Z%JJBbw>M7*XwBm3UwLik5WtT<MM_?_O_Q(J`J~O=8_YT<hxMi4&
z0()WI0zPM}qbO;i(Mqac at G^SCp_1v<@F~O00+{g=bc^!CZuZJ=_O86ys|zodHNPi1
z&GRW0%@~QR?^!?f%x9X2UZZphCReRJa~d`sP15LZU#@v|?Kd_L%a<dzww^id9HDWh
zYQ#2i>&=}`3%L)4s(M|@tYSQL!>6Da&Qv0iD0kvcfs&pg;6R@?1qF$M`YUok7RCpt
z<L%ZnTKng*x4x}zH6e{Um>)TtR4 at 2@TyyVvOE3BrI=ne1WfzDjKTn{&=&0q({_}t9
zwewV3UWPXDBYR?POuQXkuqS?`Ll<?4wP#x|)qLyBDmK#5COSqZoniz*k}KM#g}ql>
zm9Jr`_|jd!Nke;gy-Etk6CmMXq)Z?q534OrdwM};T{J5UbQ}~BbXIZ1E<Cv~Vhzr!
zTJCWy!)TPsRQt&WeedoVvZcjk*omXmc0lLf0}j)OU9-uSBEp)rO_^AtOe|L>*3em7
z9tyN*6ZG59cp2X9HKC2B#E(o=y^)@v%PP at yZ?IDhkY3Pj*Rz#OnoFQ<hxU_#Kf)=y
z%_dYF@{=8Ha-xkoRH{W=h$<|E0z$VSaa+IF>_l59`m`N2!w`PiR}0G2#R-}c4(zso
z!@3`dQUl?g07 at _V9>5Mvtj6Zo2S+J=-H^Z$+ at j4lLxOjIOtvf}4(%lc8e%8^FqTR&
z(lLj((sXqF0bc#~cXf#$6<>1oh{@su#I6`J=`SuPDz>A|R#XBB(xI&;REkklRGSok
z)+zK_g~6)$lU=dX3h7qVv|Z=5^@TeXJM4<ewq~A1QC0g1?n`|TQcb~Ickt3ZnS-Jk
z7kHV4Uuq|DjJzEY`5-*}I+UN_9oMe6<h!<#Eq`<w(Iz*9#>86p$+uqb&R$U2y6dtx
z%Elr|iMtPbqc!!1LH%o89Vmd^qoZTAbhKcX%X;>w^qtp$pdFPu(IzVt1uD^@&018<
zAj<)QAm`KOlGNXpU>HG%(n!Ed at H%KfgA=k)rQv91?P*tf?w81`c%cj30{jF at j9<$x
z3>?5_BF#|1c_WhyM9%`UcoDJdH)P8)R{<(E0VnBX9Rg)C=JdmHP)jsF3yIxAKr*qU
zZ?8m)HbScO(78grC=nuWJT=SOvTvNpmv0##CBA*;j42fq+-?!?{b1IurmCA(=@k2i
zew{Y$)z&)?Z#wA-4+X`qxq5bxEpu^_k`i4F4{o0Gi1|kAQ>cYjdK6me4kyz%VwBVY
z9r+0y5uu~uBT+|vB3R=x>UTqak`jqS(WGFi3l1~igjyZva_am0em1B7<E$NNO^A_&
zV3yrQukwsv_Mh`WzIU$Oq(1c1o`~={y5%SoT0)gp+*(~-{U`D5pYZ0qF17GbO^-!7
zrQy|^3Gh7$%k5f|&j$SQ%23)rBI09LOMnYq#elA301a?K0a>%&C>FZxXCJ2fHa`g=
z6wt>)VzUta`8VI$v$i=COYCSP^uu?KV=SPeXi6nofD%ME;EM*gLqUW~d-i_%lE3Sq
zMP-QuKli8a_?s at V#F`mJXGYMOVTQlngxUpCk at n2-P>ROP9D%cc!G5cL*QJ=>LAxxz
z-487rT7WTY8#HPtBHSEtDQTf_6U(>2XWvHPx$kw0FVBk0?CMp^Lc$cjMaL}{?>$oW
z!L074=*J4~J$Jz+_4jM_wVJKw^wpozi6thq30OegWH2bvC6*gevAe!QPLTJ$AgwP?
zhZ$5?Ym*5lrhXqPMZjaU-yWcSv%d%Q0Lh3D&qH-&@$vpSzR+ZK=pCJ+vU at EwM-!K{
zaJi~`1PV5Bc@{3e_6Gt*o45iC*Q5I`fo7Swo))f`>MmjAa%!I at 8rJkwWZx`V0KG-k
zjNVtl$id<(OUF97-W8cQ`}(H#xrDGY4sJg}M2=J6>(kNJ76=^>I9>52LJ|)DhS+8B
z*$dV^KE?0%8_(WNzf}vVQmyB-RP+}l?lL9r)+N>{6F*WW*5-`_-NV1N)6rHs#!8u!
z=DM6`@1*|&<b?t3KO~j*JL#DJ7$pV0Vx^;vl(|LY{stZ#w+Bu6!k&MBo-wBf<{6oW
zK^iFDs$XU&J>~m*UcGJ2QrWYzKyiSg%tU(DQP4- at Pj|&O9qCy at m4n)6#UGWVr&95b
zEK3X&@Ej%n<q^G<d}q&se>*la2SLB<a`m7@*^05w6`@v=0F<r2E;~iZOM9?brlRaH
zWm0Ub^VjRA85Mu5^hfdKm2#cpof3boqTHPdhK_uF4~p-l&VSqUo7=LyvG%-B3+TQy
z2U->a1>Bh`%IYY{Nu_^z`81v4k0t&Z(4wt)XNkY^aXPQ|WL)ooQwpQdd??(~OEMq>
z)5MdEMJm_`=7f_q-`7|R6!`^N;|dh3m5PlpG+yd4s>kZGLcylQ;3yUOO2)6j+S7Et
z$Ew%7?CTW3n6k~D6>7~IXUUS9vNDU|Ys8I%CyK84tS4?k50XhU#l{?oF=w1Mu}tT+
zQJ1)F5bF{OLLKdqQ#}P<8)@EZ2)3-m98;pr8MlNgP;3K0K+l{~QO2w}$62}`D{Go0
zP|SS)mB+e~R~02)?ysAdt(%vlo0m^550otrH7ys;kWKH2K%=yz=Q8{aJ#vmG2;*y)
zi<R>VkU+p8X3dZ-7soE$0I#oj!7Dv475OXYtz`JgcXguxDqM(2A4LLIYy#^Xs02L?
zy5d9RHQg(fJ at Y}s_gV!&Xb&&!?7WL#(lER^U%}(_XYjCw7s3u5en}%5=JmjfQAogN
zHhy7(76=K=n-Z{s7%M!Pm{mBYOo;8UuTh|9cHS__9PIG~FRPHqw_$p~0iGrxe?X33
z7%Om7Z}~g6hHGF$!tHLz#0dcg`gj~s#)Nb-{G3 at Dlmc!{I`%N>2-n2M;)p}RG%g&=
z$;7e?iO%boojc=@nDmD~W8ZKMERMN at b<M}@z^n7|bb_M^*!>1fhFv3OH#{O(fqwaI
z%x;K&+j-2+%5aA)q<@duJqGinf!x){V_YHG$EZkuiQUpK8Y^{L9*RbeS5xD$4_q%3
zd%zt8G|ils;CLn>(RLIwG!1gV0kZHqfizqrk>ET at 884@hFf)DX5E9^JhYZnRG8vli
zZ)XU&tHrpubm5xva?DFNm<;`3Zop9rtl<f~3c&nVyfjaM_vIt-+`PhWbOVXu_<<F?
zB*Q?^K~1T`SPmry-rblmyi0LDb>W3iq&WZa+SPX#^Bw|A{cD_0SvV}R;C#vkF_k=C
zyUdI>1b6A at e1&#G&?}f7BoRNEf&q7C<YL^z3y11C1c3UVKdiMuPGHR++}%0R0^DOx
zgB2(+yMCX+u8E!jWLRxYTJH+0!H!OVWiWMu)$pxcL?8hQL@{VODh2;F7vYiIVtD^X
z+Jf#n at 0VTzt3BdT&v1Sorec8_2Kxr^!eklJ0CQWgY8fj-qp=0-GA4&`O@)@}02q%o
z*uxo7IMlYlYKI==iBi7ssFZ=5x(p+2kI)QtQRnG at l8`}u8qg;?keAb#-SCKDwL*G_
zwPT1C8;{uy6=1pxx5(kK&d<l}AY|L)H3A%+Dh>KAm`v^*?%X}<Ilu$h)jz>5?n!nb
z&*6X(nxUQ+tT<h<tME%25lII}>PFw!Map9a5(1}%-Coe{<DSBfz$eyyXflnE- at f6d
zfS)Rj)$)^QXR6h5Pn4I^Co_w<DROZ)TK_0W?g80VzU;(nGvzV}%3UCXpz&$<b?YEV
zmw&fC*8neZCvM1cr^8FH+`0mv#zO(J6%3ITZ-Y&?0(UWSU)+F+RW4j*qDdeB0yxmQ
zpJHcDw=DNCV{J_G0__v<Izx{9rJ03szc6Xd{Zwj509Pp!jLL0{N<%BaK3`6SZ-Qki
zc%{;axH~v71zA<#3J5NdbWo&DbYB-fkp~U{+7L1V9`VeRZc8)dS1|=8Q#EJEm6 at C3
zzP>SQ)=VLeJpQq!3}F%$V=l at xVo63MJvLG&zOS3iq&+U at h7=MZ#f2bEBcjrh`0h-_
zoqfUs(`A^=7ea?4Z@!U|k{kw(urNH7E3nEB++fm<`7s6l7`I<PL3D^){SzLKxP at VE
zdoj1Tf5NTmUvZN?L3D`Qdrx>Iuf(L?P{vVEl<(Y_h5Ioo`mwylt-mq6#OALYs?HEL
zZuAW&AUQY=L3PHCYaeq27eE_7b8#JWMm*NQ05{|>8Eg;Op9u>M6W$*+OIgH|(}4QH
zt`Nc*VT3T?W@${rjDV&(;K@!0+)Y5*eaM#y2C;CIN1U$U4!{)gXr_X_p}!f{g9GU&
z`WyAQzsqH~R>~qCtALP?gkedv1G8aciIw3Ew-Z4`CYM#1w>9F?bPcQ)=vs$$)sXxr
z=T<2=m!^m($DD*=Db@)I$*|FL%|-Ms#F@$Tbxb0h1BpO?0r!DE at Zap=L+~I5ls{o8
zhxS=vBYM9~-vw4^4oP43TlJmrZiV- at ei=`bzCmEsZ+5%1obB(ra~<B@{S?h~Qqa4s
zuxGBN4Y-8pOx#adGvA&V31-}`>on6tAwhclA#i}OV_}~P2XyyV7q1WXR&XfDsxQW|
z(}>~*EK}8W?)pH7SO~%X(++X#^}!CYBl8i5<BvEbeTJ#VS-=H%m3AY~KG+8q|BYdI
z6c}PJnUUvCSJyspX%rlS>nicyC43#lOho5Dxt_7xUeR#94{Dru0_bE4d8xd-AzJ|3
z{fivJ+}l at g0}bco^^c9cliwa1REs{kzNkuAz5VKhE$U;RL<amWa{M}Vd_V{8L<`gy
zo&vgvd15p{@x){E6lsxOk9%ooK%sr23;f8ymFjS^SVE|$w&6(XLgo~dg=QktMIgr^
zYWDECAo|Wz9gZuGn}vyC0ilw5T%wOW)q%LK4d-s8*MnMwVUFu3;@Eb&sO8EdX9yL7
zZBDvKz-$1zNdVoDx!|k>{+|zob}Q`~%vtfN0q2kj5KhN912NFD16DT?<51Y+F2LVF
zggh6PejUMKX*uGl$N1JcBZC)Flsp-f925&-jaL{s>sfVhOr`UF6%R!lX`woJ>|tle
z4hxy^W1Kp~Uq%aOI^Bzy5!YQ0;##<%=-vCW6ZUi9=x(wT{Og<{>1YF5kH)6WxxO;i
zj!v1RD@)}?v#ythh1Bg9lP%*3(OFN5UTLHYV7tK?g3yl5v&f`K;>WlO*>(LEI8R&|
zruQV0q=aBB(UQ$6&fv7X%qf0>Q at ns9h~l&ab6S2T1RaFHN)(?W1bc}jkY0bEXxT~>
zuOQ%dpQI&(Ae9g#5+H|EJf08)6UF{SWHi?GQRh?#lSvm{CNd#XOakCDouJdmqIaEy
zgmt)Zs-y)JU690-5VO6GCS7S}#O at fI1Kv$kjgp>J;dvdca2iP`9R=$%I(l-Bb0Anq
zE|kEYW_K{y1VK4EI$BFdRs3R4`T>^?Ln)`((A`yszW{aQqAT9ztvpf3Vz4!OgMy~P
zKo8=6Y;Nl4>c#WKQ!gX2kxT<U*uF$?0&&#fa})<h5hGk7g~iOC6k1a1ZTPCI;j1p#
z`pkn(W-O_X_EfEfMRtZ{NDPkB-0kTSRXYt=>?Y~t2+{?YIt-<K76h<LU*FSQ at wYDE
zR+-~t^Vtgr&qd!YH0gWmk4P{9kmPZbb07PWQ8fSo#-znQ5jhQ{mD~id_}JQ-H(01+
zfQ{hrP`*z$Q`9kc44r|5NIF2K(cUtE4EhNu?`g&v<%za{lCFg2au6;!on}H8Ea+nG
zQCDS}(d#R%*9CkOzC>d5x}@{ESie`;R)tT-!vtS<i30}=s^1eUw8_VS4&14?d+I^p
zXmqFX&vui6Yoeoum9e|1Dyr=0HLH<Cw;jz^I?6&v!?qriPjnTGqQb6<!Y-|6(4J=4
zcZXeNr_}$))p<xzG?X`rs)E8ayxw(6e!96X1()f%nGm(n+e#fvYIYYZIu0&Z{$mbT
z4&-pZt8(RkFNfI!IRxO(30f`=C2t^`^guTMGMG)l53Wi)rC{o1(cCV>T%66uW)LnM
z%;q_-mgXTkGoc78Ck6qwI_j8cEf4Ng87!tEcP1hXuF9f+orwy$-Cahu!7^%uGJ^WB
zQ_wKmE<R(0YQz+RiJlDoC}>vz#bSFnr-gPht+C?fkr at MURlDjqr-X+qoamArePu-#
zI$_z{Cb6T7^)6xCppF)MJBK-ecnrk6ob4Pq8Am;L>})I6;-^ljb2HW%xM;o>x8<mc
z5IcakQvkZJ261tRsSw$X<gZNz4-5Gm+y}V=Ll|wLjzfJi?-d%Ml<y41xYFE!>=npv
zAvYC$)9KS%H{ZNt1CXooHj>JcC3-y`ul9`5K~*1zm7BW^J~*zCT==7Bx1X!P0IzAP
zFZwC!Zxr=+iZWAF6Gd4dAq8|4Zf*CvtYn&<R65CpC<+qpq>hy{nYUs^Sd&|>;z5}y
zR6CVwqH3T<Lo~y!F7yi2KROETSr`mJQK{(H4%}x^4CoN9_%<k_rC>*ZI(E*t!qz2t
z0Q>2K4ZXd~2|BBzwA3*t(77>02ME|1F0h~F03e|sIo~Dda5c3-S~pFc48$6~=z^Z%
z7)GKIka(h<XAD%J^)67n|6gwB#|P^WZs(BphC^MC*C1#Ifv)mip6KoU!1s!G at u&Ue
z4|t;2?=iNCa|EMZTKy at _7Tmp+LigrgTYb`$7k2*J>*i^%cgkPwl&aoQ^3TuD@>#rS
zQDCG$(&~HN5-_E34~I^F%Dq2c1s1$E9!7Vfco`jsxH5Xmes1m0E*V`eqbp>z&b>bS
zQYYT?w at 5Rmxb5q@49d<1dDjv=o-|HFA$52RIt{yw*dDCo=0WG}1A3nVL1_p$Re5LR
zcWJo>d488qi6#k`MKV8xl4gqB^e9Xp6L;ef_iQI}4zr8K^UJcz=9Wd`ZFSM}ytLnS
zQJiosf0K+Oxwqu$)=OC94~S`m5oOU>2p5HD`Kyrk=J;_IieuuhGEsz%|HcPX$5<$?
zh5v?yBC$9wmial(HBy|U`z|8|@fj6vc;0B3-}Qi|;PBx{o^>SQ97(p-^UAe>ZB0DL
zQJa-l#p&zhl?ve_B;uYc?oOwPtAd*<55`P+KpRi2dKBvbue><0?K58adx32ihsEl4
zx|k?q56*u10d1Jnr5nk&j^z9+Mz#NN88gFrX~Xy~oiE?&%dz{CZBNoq==UoXc6Rc*
zzjf0OZ~FFH%>v}D|Eb)J1-PbzBIR+5_4-wZs=o;d-FFszZk3zy(O2v<d9vGwi80Lm
zS&`cvHj%fgl8KNx$j6kpuZ2Tcxcfo#cH(|(CMe0Vax+k&I&TCDPW6G~I+=mB_+n&d
zW+2MeCT6(nS=^~>kCkg&^EG4|R?ZpbYhsyFa6VSD>P3 at 3h@CCNBf`^SX;}$~f4wOC
z78e<=15jtT)10&e*Cf*NMN+-;v%50s(^AXb*5_Z;-5m_%u1g>iLX+h5_XYE|bBBEJ
zeo!#-$k^Ap?}S#cdh=8<R0B$|nu3ZVSo6U&f2a>i!5Sr06yR1saX^E20mTIcr6>UE
z8V|&7K-K(JqoCwo$_Nw5X?+V<I}6Dvn}~T>1*<Pj70U)m+68NFPV<)yk%YQ8oTNLQ
zDDX0E*xP9uL0CudKwDRv8ERUf>IYZc(oPGmU*dxkLoVa`H4x+gudNxXRUjzbWBkV+
zeCBB&Q4J(G))D+65^$vr)Ew}6f6!+zCe$e=W_SaA4Mgrz_mfZ^>Tz{=8mhzC1yJlK
ztG_|EeZMdjWyojp1h-Gky2sq at pFb3R?ZJ5U0=f%oBMJ1KG;tqOJ&aI2-caX)YJi9v
za9s?DVs&@AM7ew_QDxzNWa3sUGXu>>GflXvz!B^3E(N2L)jYIMz#-INzGg|x0A^=k
z_qD!NFHOB5O}!{h{Ysj8$<9>)%bT<8KmbH at x%<RY0oE1TZF80FMZvXKU1hUScv3ew
z!OhuG+RVU+P(~06>Ofrh<Ml62at#@W)yKGyYe+iWKIR&NPU}%W6?hF547d9|9)AWU
z_qkT{{1RHL*C_b}qBV>CdPZ at OQkn%c5SjE4e<wlz`{!$N?zJM~+VI<vL*ApZr?{^>
z#$l|6@&|O>#cR%yzFp%x`}(pv|8bt?>ifQa*P1!{6s_g{$$~hR#E+KH6nBS`Poiy$
z7EN8G$b?<pI$FuCG<kE4-emoOFxUjqe-wrmw(a!;0c12A9ss{Fv96MaV<I{)F7htl
z&eJ9CSxrNM6nK!@#4KeZ4Fv^-V@%A<f0I{`xHn&&NhhlJO|_hO at 3?n;d%5_i*`_xx
zykuPXm2u%k<H8GCt_su?ZJuq{are{&*|~o%`y47ojEYZ$<mhlwAuZU&Dju<ugm%T?
z&}9BvYbe*D9E17mduycPM(ad>Q)&)edSo3zz$b<^HFe+{BgXWauoR92M<fiEB^sUf
z!L`-`Dk3~qA)!>^t(2Dkwnw&D?a$Zo-yWUg|B^+P3YQQP{t3ix0}MdMSBGTL(4;WK
z*NAJBLL<$#coQZvb^2>!(NPbdt)dyhy0iVF2_s}QH0XR8wX%B#u&gf+{MHM7(f5tt
zeV;Jx=f8-?`0GA at Ao3bH6byFnL;KVd$XX|evzW^ReHznayzJx)b}IL{Z_ilJCPbV{
ziSGZ7tjyK=E<OX=xWF#H4R?*C-2c<9q;N<?uDLv19TWqRc8v{QNaElSm^Tzy!32Kq
z)=e>HtC|7~Ta!<v903w`sn{tEtiNrx(_+zYakgYsj+GW_y@?|(CmpDzQbYT?dm(Ax
zLJSl6f{x4=#rRpNR6DuYPUV;1HsAWzkunqyzCy`dr&L_;GCP6d7hXUJXbA4RbY#xG
zHMChDXfl-FQGP8hwT}#PUh4<JYpjWa%4vK%eG4DxCXIp*&lhTbv{0##4hE)`hf{!?
zlLS=r;Y!V0M;lX;IH0(a=#~;%vcRV#{qQ$BTqXrGlR at H=T&WJ8IfO6JDJBRvwa`AC
zhsB^iIR);Z3GK6!1?sT at 5DzZAnWd%0&J=J|;byM`B0)lvcc5DA17!(i4HN_?UA0&(
zAETQO@(IZ)|AxswsH4L3fui8ALwt-;Vr7(n{oYJHT$(;Vce;HPO0;^ewCH~}Tl{=%
zu;vY8RNKUNt$bo9$K$&(H813G4$obp3SM<&v4u~F7h6D$y86xS&NS}V6zG;K5GH8e
zfE{A3fmkvPn??d^Wr4~+Asg>*z-J^HL96`H<W9Sve%Y$dymwo-Yu=N9mrey^449P4
z-zUk#9_xOsd0`T#I%_ at 8$jAX0RsLe>a@`$#J%C5`A(IcGaj(zfxSD26#)4m&c9(I@
zR at 7x<1S~<bfHX1y at u>MPNEY3N97)2%r1MkD-|+ij)7X0l#1Kyz8L;Ie5WrQYET%kP
z5Gx?15W4uD%jlVkX6Jvr!8kt^1wp{7^a1)|XKXTZO=N)kG=ry>T7yT)fpKpm;#OE}
zXpA)3tW9oG`;P*3e>>pH1t5_8rBRQ~JtsgSj0 at -8n9rl+FHL&<rO^nkZW>`Gn at Jj4
z_A at kKEtv{8hIRVD)x at Bi7cP0Hg%Aq&v3qQYt0mX!q{6aZ8MU^oO7#m&b3yv67N&pX
zTS!zBY?2CP)LX4Px_@%f;1dk9I|z=#k0nP2t$_M{euoV`Yq61$6J9guY-oAp%vF)e
z5f(V4guK-`!Ay$LHZb6D=sn!*aTef_11TMRx09>aoKTN_QAVvU!HXX`*-ml|MGMpa
z`cQ|0+86~|jd1DvVEAN+YXlEwZRHi;#<j(KqWhK$EmjB584QDFH=d}y?o#5ss)U>Y
z2}+47h-j+%diSv+IBPLLklEaV)YXH7k<7mLQXIm|eoQKtp1AXh?ZK~bnqJ3`--&P8
zsca*e3fFda`*6U?NJ|7g?t%f^(i5+EB-#JjIRJT@?ce at xfwKcKVRLL5zp^2Z0=2Lp
zX>~!;8eQbL8M69Dt9$9`L!&+mbd|k48(e^Y;|ZgFy}k;+R(;82V?K949GDAkrJ4Bg
zJbwH>N(x4l7EPK*`@-9mOCflL>-0%&(p!Ud2=U at 1HgMDhc$ZK<RUeE&AVSWpa#4wJ
zbv*bmCevO(@9Zeskfuntib!0?fX2un8VrR9Mq$lk6d>Gm6HhYU4Cer(W*?XgGBmq?
zAmoQ^le|F}f4HRfp-Y-2(el=%{oH|klo8`hiuH?LvWS+9K#_4jcA(MHFZ(4Q<JGpx
zGoWW(J#!S?D#;u+&~bw#OdZNOEkJ~N!l;tqCp^}iZs8N^pum!7w}%m8xEJocs?H2q
z4p*zi;|_0Gbky9q#l&0hW_D^zPY5rkddOb6c2|&eQKPwW?W1^Wd&D89j(EH_y5uD{
zebDt^c?2%n8?-}g9dDf&)Tpe2Tzznq1cmMc7!;l8pf8hl!5HMFxviJ3D6A^53dU#{
z4ct}REbz*lA*!k|!7Fp}U$-F22=7e#tB%luF>>0bQS0f*LNsk at 3+#u8<7EnsyNojk
z(0TlT?DcPZN7z^UM&Hi6Iyv7c=V|30eYcCSl$BgymvcK$$vZ*1RnD=<Ni6UxCk~U(
ztgf$r;n0i}x87s2;buoI4-VNJ2NlZ^Jfi8Hj#KOQBNHP$ZjBCvvsH=Qw$2~gK-+O5
zVNc|=U6xxD$p!I;s%;1#lP!G>TAzM3obPe>U;icD+i*!eqkFsJ{{LL}cKLnT39OR)
zpXlE1Ho*wT)KSniMT!-RVtKLB*hOh_^?^HsZJ_oXLFu!?w%JgC5zfSrFu<yq<vbbf
z56LdE&#SzR*1%g8C2$R-9j$BFU^`!8>ngV0*=Q@$+6;xZ3XM&d8=}n(b>@z>=Z>@D
zqak8TZWw$xrZDG2x}d}%EnVwvciyr!thY6-vlYE<t5|0{{|6hR_*QuJsxD6hXCf?l
zT2o$;GjF3kZ-diDqGub*I?%);dnKR0D}653lDCNw3I|$6lRMUZhrA$hSN5#?0t{!4
zxOWM*PF@#t3d>tJu3NXXZU($mMVDlQfo3((Hf>aB?b`&}qACP<a2rm0tpjnX&-`GU
z7$h_nC>AUh$Ey=T${zd-FWMrC;G!~pIUb>PbO0#Fh8HazXh^dG1${ksH_ at X&7!h8K
zG%Ro6`9sFb9iSede6cVr*_OK)3BZq5y~{i+;F*K_))}1X(f}|kL1O_JRoJbBv)TAf
z2wqb$sqmT!J at DMhyuZUd%bDj!=DCS^;!QbPh4%nyRSEMfHQa1R;yOG)HAcfDC)%CY
zxEt+Cy-R+r9g#=`?h|DALE#Wq{IDFxbeH-xh2UQ8G#>ny;uZa+uj0pB_%Rzl7T^a?
z)Y2Ul{<gaJ;H?UeZTra*r#5q3_di@{^5sBqaD>7m2cM{#FbM{+%5Z7s9l5>TWVkEM
z8lxRQ?#}Lx`omiAZuByK*sCwWugd4%mcAZV8Whpvs_k+~6_rv&6?{c@;BUNk7hLrP
zVWf$kaKos3QUUu;dEE6p(KIxn=*3U?kTotoBB6=^-A<w{(x{?|T)dz0l|dhGCb$X9
z7F8s4R&?4V;MmrAm$+vDM~S3D;*$6!B(zC>kW8~2=f-}nl`OXREiT&6hh4&xpZfZI
z^3wiGvph~7;h9eK at 7-^P>^^VV&tC-L;_XDd5H{}Tj6C^wU!U5a=3I*RIQa>0)*K^j
zXadjC2}F!g5-lMP7~q`V?}$V30reZq>6Lz!McA*(up4xZK1=vWal$7Q&HIEbeuikB
zibf~AY1!}5JqFZOjE*XT<WBU`SDA at 74f*vy5MBw(5-Py0{;uV_k at X6@<msY({ar$8
z=)0<ge8ei?s?19Ve0~10w)xFxJT4vM*}v#Na}v&^!mSSVWp<)OSpSU02Kx3Tu|H|g
z@$N&=FPWhCkMXQ0AZXt+W&##_zr(21Ct4oz60U0N<L#38qI29!pZNORU3<b!*x3xB
z+d9nk=PdO$JKUQislSUAfM3<_N0*KU`RrSJ^v&7+myV7kekGK)_QSk+jxW;z&G99s
zU|hh0a}L+RMR*fsXPB%Ks|=7M$PRFad_j)uzlV%M=7y)?AA at ONGWTCe1L6{sg~rx?
zH(Qu1W4qh3d>XH$Xx at xb$e&3Kk^i(A*aCAjHtBD0r3H3<I*eEdy=g-153TVbBwSFC
zFyoEZM5)D7qY%cWbtKGxBPCwOC1&A49{P`lg&lQ19b1rBQEG?vY+uu-?c!SwpVK2g
z7{f1`*U^bpU@>z6)k9Z>qN4;3&Tc3TDjQ$N-7_I)X4$AtKGE>24n#gB=0%JmCU`f^
z=|Cecg?JRr#&IXO at 9n~OEx-xS5y)5d^WeDJFty{bPuXnHN998pWF2+hWx<`^L_=%`
zLMO$1=s6)eoL<ereU(V&vCi|U8Ox7z;wE%#)m6c@$`Q<+>SuU{aUF*_b>6#YkJM|H
z9ZB&~XcT?|4h&(Sz-&LS+nYG<9qBDxSMt_Nu+Fi2l!w07T<ZmwYDM~tgd5MewX;F#
zIb66f6)ul1qboU8<W^~`u0hy=&g}8Q>ugLPEAkhZEDz+~9TwOqo%k$Nf*C{_2W}<1
z%&+r?3(GFelIvyWv+=Ni#3jyZH&@Fl{o%GruRRm$q^&Pi at se<Ys)8$dtv|v&exOVn
zR3-z<%lz0m<&qE0#xr+S#30Vy1YCUtkHNbv!)~`TX^`J;&-k`|CmAJ}&#Uo|j-MCr
zvCfH3;f4B{aQcfN!fv)R2c%;gxKZ-i at v#j7vM_$SmM_Z?QW^frjDWan?UEjDY)=4$
z at NZh?2eU^#mMZ*#Lv&qjzlB2kBs~F$oL>t*5PQb^OFkuHq3kn=rZ3yin87 at UE$|2<
zj{Ai6t#bku9D*R0t_(ZX4rC=g88bN(gwn78Hv at S~d+bz6tfKtsCZemkz0b}y-{4*z
zYX(UfF84~D=$hbkm^8yv`d1f12cP3xTFE}A{NNOhxD~d8LhyD_pEUOT!S=rE0d1}I
zm&^KS;(_m#v28BDz8e9M%)VvBW%Q8K8`3v|=-P+rx6#C9-&1jBn;!&9A4f(NSypf?
zVPxBlfVK>RQ`>&t&~D}iv=I_wL^-&?#oM6Y|6nRF#%8w24^HHWZB5nf=5zJEH4>L!
zr^~NvdwcrGdyt&a1mc?E0UT-y=#xs?q_M^A%=yLbI<kj~OnaxjsJ#6prIJsq+}eI`
z+Nk`3`fQJmfKrbMqcz*jh%Ar9w`AZNHPa(`v}PAL=@}>7265{+N5Y+<%sqEugnUhV
zK26NvWd};0hdVTvE%sY<BOt#A=h%JmJj5w_qa8LT!&bFNdZIcKnhxB(UPr3E?+&zA
zCFwOW`GZjtp4l<+0osmNmc{B95089tHp*YCMSIu2QE;*?KmTrYIwbjn_wCF#`6T9^
z?Jz=;@I&mP9~RL~u?L!v*AexhgP^aD1Kr%Rs-jp^6QMR>H<%PGn%Xo;Pz^^<p$<Tr
zvdJY}Xlje0sqMQL;&HInb3MO>AhJdKL?wbZXWTQMj0G^xR_#6<x<phLS;pTJksZ0-
zvvoV%h|Ynp<4I7Nmx`eI<{vgU3fADYZHDgBfaLM5Q)iNp$eDp<{FxC0d0avwRQ3b3
zu5<h)qslg&-86m1U7Qh=@}X!h?=rvbQ}fxQhi1)6$vadXu&?86$E9~C^rFg%xhsCD
z0+u6)%cG91KRlxTq4})qe)Z(sS6>;?`pgx~=1R!vNjZFw?U0?hi|>3$=ECJq_g$}W
z at wN9$gQ_B0u(E-dUO3K&@9~zqgY!;-k0O#c5^hw2(*k0tI9{VRi+1%-V%jHBb|K~W
zG?x&^({o|v<;{3!Xy3tA4#vet!MKf9g&)ki1TF<!PE0)2+z+k=b2%}KTi~V{XmziU
z9`>`N?N<|4$t4>&=`#d1C(WCMvr4kLs{?ZQ+2hxYS{<JgAv at u>W}5Odzs>PI)3bcA
z-`4mX{+dx+7e95wYxE6~#|`HDE}OdmoEIz}eS_z5!!NXt4j^LU>BU6MVvieeS`<<N
z+yQ9OdoE&pg640d>f_DX+<lwPyM?p%t_~;}zifF?4!<%!e|6ECQQ3)_(xOt#x_o<4
z73RJ>{_6CsfT3_(-Ql(_&hvaK3#|@V5YI$kusF}-NIa7Sn3oRbd@$ROC!}m+ib{r|
zTRsin$a`?(cZ|(PF<KRMaAT(oCmk>=7BE<WJg(;V!u)s%!8|c^t)uN10lJ#e=H<ed
z)HC at rN6n0wiPO4#+MVfHeCz-iWpQU>RI+#sL60mFxD3=cY~XTegNeF7kw)~%38Iil
z`o#rq at E+O+w{WH5o)6raeo?9lh#ED65B+M?-1(}3es%GZH<*4E+Sx7lc-^Xu?F%S|
zLE at g2AA2VN`X=B6G+ULDK0NCHcb?=9yfOPsMMTl+t8 at AWhGyLTvOg9976BFk76BFk
z76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk
z76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk
z76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk
z76BFk76BFk76BFk76BH4|4R_?B#3ZGN*Mg3;4gvyc=$`<p8$U^_<IussNg5^N!PDJ
zz)bZcVxNJ3Ec_+#9|M0+_~(x#Vzc1SgMYIx5qkjsb?~o&KNsSyhWHXN6Tlt||8V#R
z!XLr^mk~tlxA5<T{{{G);a?B`kKkVke~8BXGy~@Us<8;tfV1>6nAz~h>H1ecobSOf
zo*n`LAAY=nANPqL`Jena|IOdOO6UJBKb(bs#cNsde2$9CBXOXdtVzGm&X7%e8s49u
zFQ0_pz?8|RGIquT4G`lHoOJ}^5Po0}gnw2g5&If|@4Ltyzy&}2HfX{t4(g}pLpeU>
z`W5!WzY9>N*c?pc0k98GGA_^Izi_4M4>`Zjd40t!NnGr-I0<eF!+($Y$4?#dpUQj0
zb9ngwFWv{ib!Rpq)kws))ey1R#DB$3BG!-h$Uf-)|Ef7y=D2PuAph8oO$-w at ah<~^
z?nn9|40itoOzi#uOdgnhU_wvK{#XQ91Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E
z1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E
z1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E
z1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E
z1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E
K1pW^r at IL@=F~jlz

literal 0
HcmV?d00001

-- 
2.6.3



From zyw at rock-chips.com  Tue Jul 12 08:09:43 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Tue, 12 Jul 2016 23:09:43 +0800
Subject: [v5 PATCH 0/5] Rockchip Type-C and DisplayPort driver
Message-ID: <1468336188-565-1-git-send-email-zyw@rock-chips.com>


Hi all

This series patch is for rockchip Type-C phy and DisplayPort controller
driver.

The USB Type-C PHY is designed to support the USB3 and DP applications.
The PHY basically has two main components: USB3 and DisplyPort. USB3
operates in SuperSpeed mode and the DP can operate at RBR, HBR and HBR2
data rates. The Type-C cable orientation detection and Power Delivery
(PD) is accomplished using a PD PHY or a exernal PD chip.

The DP controller is compliant with DisplayPort Specification,
Version 1.3, This IP is compatible with the rockchip type-c PHY IP.
There is a uCPU in DP controller, it need a firmware to work, please
put the firmware file[0] to /lib/firmware/cdn/dptx.bin. The uCPU in charge
of aux communication and link training, the host use mailbox to
communicate with the ucpu.

The DP contoller has register a notification with extcon API, to get the
alt mode from PD, the PD driver need call the devm_extcon_dev_allocate
to create a extcon device and use extcon_set_state to notify DP
controller. And call extcon_set_cable_property to set orientation.

About the DP audio, cdn-dp registered 2 DAIs: 0 is I2S, 1 is SPDIF.
We can reference them in simple-card.

This series is based on Mark Yao's branch[1] and Chanwoo Choi's
extcon-test branch[2]

I test this patches on the rk3399-evb board, with a fusb302 driver,
this branch has no rk3399.dtsi, so the patch about dts is not included
in this series.

[0]
https://patchwork.kernel.org/patch/9225567/
[1]
https://github.com/markyzq/kernel-drm-rockchip/tree/drm-rockchip-next-2016-05-23
[2]
https://git.kernel.org/cgit/linux/kernel/git/chanwoo/extcon.git/log/?h=extcon-test
- usb: dwc3: omap: Support the changed method to get the state of connector
- usb: chipdata: Support the changed method to get the state of connector
- extcon: Add the support for extcon property according to type of connector
- extcon: Add the extcon_type to group each connector into five category


Changes in v5:
- support get property
- support get property from extcon
- remove PIN ASSIGN A/B support
- alphabetical order
- do not use long, use u32 or u64
- return MODE_CLOCK_HIGH when requested > actual
- Optimized Coding Style
- add a formula to get better tu size and symbol value.

Changes in v4:
- add a #phy-cells node
- select EXTCON
- use phy framework to control the USB3 and DP function
- rename PIN_MAP_ to PIN_ASSIGN_
- add a reset node
- support 2 phys
- use phy framework to control DP phy
- support 2 phys

Changes in v3:
- use compatible: rockchip,rk3399-typec-phy
- use dashes instead of underscores.
- remove the phy framework(Kishon Vijay Abraham I)
- add parentheses around the macro
- use a single space between type and name
- add spaces after opening and before closing braces.
- use u16 for register value
- remove type-c phy header file
- CodingStyle optimization
- use some cable extcon to get type-c port information
- add a extcon to notify Display Port
- add SoC specific compatible string
- remove reg = <1>;
- use EXTCON_DISP_DP and EXTCON_DISP_DP_ALT cable to get dp port state.
- reset spdif before config it
- modify the firmware clk to 100Mhz
- retry load firmware if fw file is requested too early

Changes in v2:
- add some registers description
- select RESET_CONTROLLER
- alphabetic order
- modify some spelling mistakes
- make mode cleaner
- use bool for enable/disable
- check all of the return value
- return a better err number
- use more readx_poll_timeout()
- clk_disable_unprepare(tcphy->clk_ref);
- remove unuse functions, rockchip_typec_phy_power_on/off
- remove unnecessary typecast from void *
- use dts node to distinguish between phys.
- Alphabetic order
- remove excess error message
- use define clk_rate
- check all return value
- remove dev_set_name(dp->dev, "cdn-dp");
- use schedule_delayed_work
- remove never-called functions
- remove some unnecessary ()

Changes in v1:
- add extcon node description
- move the registers in phy driver
- remove the suffix of reset
- update the licence note
- init core clock to 50MHz
- use extcon API
- remove unused global
- add some comments for magic num
- change usleep_range(1000, 2000) tousleep_range(1000, 1050)
- remove __func__ from dev_err
- return err number when get clk failed
- remove ADDR_ADJ define
- use devm_clk_get(&pdev->dev, "tcpdcore")
- add extcon node description
- add #sound-dai-cells description
- use extcon API
- use hdmi-codec for the DP Asoc
- do not initialize the "ret"
- printk a err log when drm_of_encoder_active_endpoint_id
- modify the dclk pin_pol to a single line

Chris Zhong (5):
  extcon: Add Type-C and DP support
  Documentation: bindings: add dt doc for Rockchip USB Type-C PHY
  phy: Add USB Type-C PHY driver for rk3399
  Documentation: bindings: add dt documentation for cdn DP controller
  drm/rockchip: cdn-dp: add cdn DP support for rk3399

 .../bindings/display/rockchip/cdn-dp-rockchip.txt  |  67 ++
 .../devicetree/bindings/phy/phy-rockchip-typec.txt |  77 ++
 drivers/extcon/extcon.c                            |  28 +
 drivers/gpu/drm/rockchip/Kconfig                   |   9 +
 drivers/gpu/drm/rockchip/Makefile                  |   1 +
 drivers/gpu/drm/rockchip/cdn-dp-core.c             | 761 ++++++++++++++++
 drivers/gpu/drm/rockchip/cdn-dp-core.h             | 113 +++
 drivers/gpu/drm/rockchip/cdn-dp-reg.c              | 740 ++++++++++++++++
 drivers/gpu/drm/rockchip/cdn-dp-reg.h              | 409 +++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c        |   6 +-
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h        |   2 +
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c        |   2 +
 drivers/phy/Kconfig                                |   9 +
 drivers/phy/Makefile                               |   1 +
 drivers/phy/phy-rockchip-typec.c                   | 974 +++++++++++++++++++++
 include/linux/extcon.h                             |  13 +
 16 files changed, 3211 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/display/rockchip/cdn-dp-rockchip.txt
 create mode 100644 Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.c
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.h
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.c
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.h
 create mode 100644 drivers/phy/phy-rockchip-typec.c

-- 
2.6.3



From zyw at rock-chips.com  Tue Jul 12 08:09:44 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Tue, 12 Jul 2016 23:09:44 +0800
Subject: [v5 PATCH 1/5] extcon: Add Type-C and DP support
In-Reply-To: <1468336188-565-1-git-send-email-zyw@rock-chips.com>
References: <1468336188-565-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1468336188-565-2-git-send-email-zyw@rock-chips.com>

Add EXTCON_DISP_DP for the Display external connector. For Type-C
connector the DisplayPort can work as an Alternate Mode(VESA DisplayPort
Alt Mode on USB Type-C Standard). The Type-C support both normal and
flipped orientation, so add a property to extcon.

Signe-off-by: Chris Zhong <zyw at rock-chips.com>

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
---

Changes in v5:
- support get property

Changes in v4: None
Changes in v3: None
Changes in v2: None
Changes in v1: None

 drivers/extcon/extcon.c | 28 ++++++++++++++++++++++++++++
 include/linux/extcon.h  | 13 +++++++++++++
 2 files changed, 41 insertions(+)

diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c
index a1117db..2591b28 100644
--- a/drivers/extcon/extcon.c
+++ b/drivers/extcon/extcon.c
@@ -157,6 +157,11 @@ struct __extcon_info {
 		.id = EXTCON_DISP_VGA,
 		.name = "VGA",
 	},
+	[EXTCON_DISP_DP] = {
+		.type = EXTCON_TYPE_DISP,
+		.id = EXTCON_DISP_DP,
+		.name = "DP",
+	},
 
 	/* Miscellaneous external connector */
 	[EXTCON_DOCK] = {
@@ -270,6 +275,7 @@ static bool is_extcon_property_supported(unsigned int id,
 		switch (prop) {
 		case EXTCON_PROP_USB_ID:
 		case EXTCON_PROP_USB_VBUS:
+		case EXTCON_PROP_TYPEC_POLARITY:
 			return true;
 		default:
 			break;
@@ -286,6 +292,8 @@ static bool is_extcon_property_supported(unsigned int id,
 		}
 	case EXTCON_TYPE_DISP:
 		switch (prop) {
+		case EXTCON_PROP_TYPEC_POLARITY:
+			return true;
 		default:
 			break;
 		}
@@ -547,6 +555,26 @@ int extcon_get_cable_property(struct extcon_dev *edev, unsigned int id,
 				enum extcon_property prop,
 				union extcon_property_value *val)
 {
+	struct extcon_cable *cable;
+	int index;
+
+	if (!edev)
+		return -EINVAL;
+
+	/* Check the property whether is supported or not */
+	if (!is_extcon_property_supported(id, prop))
+		return -EINVAL;
+
+	/* Find the cable index of external connector by using id */
+	index = find_cable_index_by_id(edev, id);
+	if (index < 0)
+		return index;
+
+	/* Store the property value */
+	cable = &edev->cables[index];
+
+	val->intval = cable->propval[prop].intval;
+
 	return 0;
 }
 
diff --git a/include/linux/extcon.h b/include/linux/extcon.h
index f6f0a8d..50ef87f 100644
--- a/include/linux/extcon.h
+++ b/include/linux/extcon.h
@@ -77,6 +77,7 @@ enum extcon_type {
 #define EXTCON_DISP_MHL		41	/* Mobile High-Definition Link */
 #define EXTCON_DISP_DVI		42	/* Digital Visual Interface */
 #define EXTCON_DISP_VGA		43	/* Video Graphics Array */
+#define EXTCON_DISP_DP		44	/* DisplayPort */
 
 /* Miscellaneous external connector */
 #define EXTCON_DOCK		60
@@ -108,9 +109,13 @@ enum extcon_property {
 	 * - EXTCON_PROP_USB_USB
 	 * @type:	integer (int value)
 	 * @value:	0 (low) or 1 (high)
+	 * - EXTCON_PROP_TYPEC_POLARITY,
+	 * @type:	integer (int value)
+	 * @value:	0 (normal) or 1 (flip)
 	 */
 	EXTCON_PROP_USB_ID = 0,
 	EXTCON_PROP_USB_VBUS,
+	EXTCON_PROP_TYPEC_POLARITY,
 
 	/* Properties of EXTCON_TYPE_CHG. */
 	/* Properties of EXTCON_TYPE_JACK. */
@@ -225,6 +230,14 @@ extern int extcon_get_cable_state_(struct extcon_dev *edev, unsigned int id);
 extern int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id,
 				   bool cable_state);
 
+extern int extcon_get_cable_property(struct extcon_dev *edev, unsigned int id,
+				     enum extcon_property prop,
+				     union extcon_property_value *val);
+
+extern int extcon_set_cable_property(struct extcon_dev *edev, unsigned int id,
+				     enum extcon_property prop,
+				     union extcon_property_value val);
+
 /*
  * Following APIs are to monitor every action of a notifier.
  * Registrar gets notified for every external port of a connection device.
-- 
2.6.3



From zyw at rock-chips.com  Tue Jul 12 08:09:45 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Tue, 12 Jul 2016 23:09:45 +0800
Subject: [v5 PATCH 2/5] Documentation: bindings: add dt doc for Rockchip USB
 Type-C PHY
In-Reply-To: <1468336188-565-1-git-send-email-zyw@rock-chips.com>
References: <1468336188-565-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1468336188-565-3-git-send-email-zyw@rock-chips.com>

This patch adds a binding that describes the Rockchip USB Type-C PHY
for rk3399

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
Reviewed-by: Tomasz Figa <tfiga at chromium.org>
Reviewed-by: Kever Yang <kever.yang at rock-chips.com>
Acked-by: Rob Herring <robh at kernel.org>

---

Changes in v5: None
Changes in v4:
- add a #phy-cells node

Changes in v3:
- use compatible: rockchip,rk3399-typec-phy
- use dashes instead of underscores.

Changes in v2:
- add some registers description

Changes in v1:
- add extcon node description
- move the registers in phy driver
- remove the suffix of reset

 .../devicetree/bindings/phy/phy-rockchip-typec.txt | 77 ++++++++++++++++++++++
 1 file changed, 77 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt

diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt b/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt
new file mode 100644
index 0000000..b1c6ef3f
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt
@@ -0,0 +1,77 @@
+* ROCKCHIP type-c PHY
+---------------------
+
+Required properties:
+ - compatible : must be "rockchip,rk3399-typec-phy"
+ - reg: Address and length of the usb phy control register set
+ - rockchip,grf : phandle to the syscon managing the "general
+   register files"
+ - clocks : phandle + clock specifier for the phy clocks
+ - clock-names : string, clock name, must be "tcpdcore", "tcpdphy-ref";
+ - resets : a list of phandle + reset specifier pairs
+ - reset-names : string reset name, must be:
+		 "uphy", "uphy-pipe", "uphy-tcphy"
+ - extcon : extcon specifier for the Power Delivery
+ - #phy-cells: must be 0. See ./phy-bindings.txt for details.
+
+Note, there are 2 type-c phys for RK3399, and they are almost identical, except
+these registers(description below), every register node contains 3 sections:
+offset, enable bit, write mask bit.
+ - rockchip,typec-conn-dir : the register of type-c connector direction,
+   for type-c phy0, it must be <0xe580 0 16>;
+   for type-c phy1, it must be <0xe58c 0 16>;
+ - rockchip,usb3tousb2-en : the register of type-c force usb3 to usb2 enable
+   control.
+   for type-c phy0, it must be <0xe580 3 19>;
+   for type-c phy1, it must be <0xe58c 3 19>;
+ - rockchip,external-psm : the register of type-c phy external psm clock
+   selection.
+   for type-c phy0, it must be <0xe588 14 30>;
+   for type-c phy1, it must be <0xe594 14 30>;
+ - rockchip,pipe-status : the register of type-c phy pipe status.
+   for type-c phy0, it must be <0xe5c0 0 0>;
+   for type-c phy1, it must be <0xe5c0 16 16>;
+ - rockchip,uphy-dp-sel : the register of type-c phy selection for DP
+   for type-c phy0, it must be <0x6268 19 19>;
+   for type-c phy1, it must be <0x6268 3 19>;
+
+Example:
+	tcphy0: phy at ff7c0000 {
+		compatible = "rockchip,rk3399-typec-phy";
+		reg = <0x0 0xff7c0000 0x0 0x40000>;
+		rockchip,grf = <&grf>;
+		#phy-cells = <0>;
+		extcon = <&fusb0>;
+		clocks = <&cru SCLK_UPHY0_TCPDCORE>,
+			 <&cru SCLK_UPHY0_TCPDPHY_REF>;
+		clock-names = "tcpdcore", "tcpdphy-ref";
+		resets = <&cru SRST_UPHY0>,
+			 <&cru SRST_UPHY0_PIPE_L00>,
+			 <&cru SRST_P_UPHY0_TCPHY>;
+		reset-names = "uphy", "uphy-pipe", "uphy-tcphy";
+		rockchip,typec-conn-dir = <0xe580 0 16>;
+		rockchip,usb3tousb2-en = <0xe580 3 19>;
+		rockchip,external-psm = <0xe588 14 30>;
+		rockchip,pipe-status = <0xe5c0 0 0>;
+		rockchip,uphy-dp-sel = <0x6268 19 19>;
+	};
+
+	tcphy1: phy at ff800000 {
+		compatible = "rockchip,rk3399-typec-phy";
+		reg = <0x0 0xff800000 0x0 0x40000>;
+		rockchip,grf = <&grf>;
+		#phy-cells = <0>;
+		extcon = <&fusb1>;
+		clocks = <&cru SCLK_UPHY1_TCPDCORE>,
+			 <&cru SCLK_UPHY1_TCPDPHY_REF>;
+		clock-names = "tcpdcore", "tcpdphy-ref";
+		resets = <&cru SRST_UPHY1>,
+		         <&cru SRST_UPHY1_PIPE_L00>,
+			 <&cru SRST_P_UPHY1_TCPHY>;
+		reset-names = "uphy", "uphy-pipe", "uphy-tcphy";
+		rockchip,typec-conn-dir = <0xe58c 0 16>;
+		rockchip,usb3tousb2-en = <0xe58c 3 19>;
+		rockchip,external-psm = <0xe594 14 30>;
+		rockchip,pipe-status = <0xe5c0 16 16>;
+		rockchip,uphy-dp-sel = <0x6268 3 19>;
+	};
-- 
2.6.3



From zyw at rock-chips.com  Tue Jul 12 08:09:46 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Tue, 12 Jul 2016 23:09:46 +0800
Subject: [v5 PATCH 3/5] phy: Add USB Type-C PHY driver for rk3399
In-Reply-To: <1468336188-565-1-git-send-email-zyw@rock-chips.com>
References: <1468336188-565-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1468336188-565-4-git-send-email-zyw@rock-chips.com>

Add a PHY provider driver for the rk3399 SoC Type-c PHY. The USB
Type-C PHY is designed to support the USB3 and DP applications. The
PHY basically has two main components: USB3 and DisplyPort. USB3
operates in SuperSpeed mode and the DP can operate at RBR, HBR and
HBR2 data rates.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
Signed-off-by: Kever Yang <kever.yang at rock-chips.com>

---

Changes in v5:
- support get property from extcon
- remove PIN ASSIGN A/B support

Changes in v4:
- select EXTCON
- use phy framework to control the USB3 and DP function
- rename PIN_MAP_ to PIN_ASSIGN_

Changes in v3:
- remove the phy framework(Kishon Vijay Abraham I)
- add parentheses around the macro
- use a single space between type and name
- add spaces after opening and before closing braces.
- use u16 for register value
- remove type-c phy header file
- CodingStyle optimization
- use some cable extcon to get type-c port information
- add a extcon to notify Display Port

Changes in v2:
- select RESET_CONTROLLER
- alphabetic order
- modify some spelling mistakes
- make mode cleaner
- use bool for enable/disable
- check all of the return value
- return a better err number
- use more readx_poll_timeout()
- clk_disable_unprepare(tcphy->clk_ref);
- remove unuse functions, rockchip_typec_phy_power_on/off
- remove unnecessary typecast from void *
- use dts node to distinguish between phys.

Changes in v1:
- update the licence note
- init core clock to 50MHz
- use extcon API
- remove unused global
- add some comments for magic num
- change usleep_range(1000, 2000) tousleep_range(1000, 1050)
- remove __func__ from dev_err
- return err number when get clk failed
- remove ADDR_ADJ define
- use devm_clk_get(&pdev->dev, "tcpdcore")

 drivers/phy/Kconfig              |   9 +
 drivers/phy/Makefile             |   1 +
 drivers/phy/phy-rockchip-typec.c | 974 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 984 insertions(+)
 create mode 100644 drivers/phy/phy-rockchip-typec.c

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 26566db..83706a5 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -351,6 +351,15 @@ config PHY_ROCKCHIP_DP
 	help
 	  Enable this to support the Rockchip Display Port PHY.
 
+config PHY_ROCKCHIP_TYPEC
+	tristate "Rockchip TYPEC PHY Driver"
+	depends on ARCH_ROCKCHIP && OF
+	select EXTCON
+	select GENERIC_PHY
+	select RESET_CONTROLLER
+	help
+	  Enable this to support the Rockchip USB TYPEC PHY.
+
 config PHY_ST_SPEAR1310_MIPHY
 	tristate "ST SPEAR1310-MIPHY driver"
 	select GENERIC_PHY
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 24596a9..91fa413 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_PHY_QCOM_APQ8064_SATA)	+= phy-qcom-apq8064-sata.o
 obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o
 obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o
 obj-$(CONFIG_PHY_ROCKCHIP_DP)		+= phy-rockchip-dp.o
+obj-$(CONFIG_PHY_ROCKCHIP_TYPEC) += phy-rockchip-typec.o
 obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA)	+= phy-qcom-ipq806x-sata.o
 obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY)	+= phy-spear1310-miphy.o
 obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY)	+= phy-spear1340-miphy.o
diff --git a/drivers/phy/phy-rockchip-typec.c b/drivers/phy/phy-rockchip-typec.c
new file mode 100644
index 0000000..348f9b9
--- /dev/null
+++ b/drivers/phy/phy-rockchip-typec.c
@@ -0,0 +1,974 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Chris Zhong <zyw at rock-chips.com>
+ *         Kever Yang <kever.yang at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/extcon.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/mfd/syscon.h>
+#include <linux/phy/phy.h>
+
+#define CMN_SSM_BANDGAP			(0x21 << 2)
+#define CMN_SSM_BIAS			(0x22 << 2)
+#define CMN_PLLSM0_PLLEN		(0x29 << 2)
+#define CMN_PLLSM0_PLLPRE		(0x2a << 2)
+#define CMN_PLLSM0_PLLVREF		(0x2b << 2)
+#define CMN_PLLSM0_PLLLOCK		(0x2c << 2)
+#define CMN_PLLSM1_PLLEN		(0x31 << 2)
+#define CMN_PLLSM1_PLLPRE		(0x32 << 2)
+#define CMN_PLLSM1_PLLVREF		(0x33 << 2)
+#define CMN_PLLSM1_PLLLOCK		(0x34 << 2)
+#define CMN_PLLSM1_USER_DEF_CTRL	(0x37 << 2)
+#define CMN_ICAL_OVRD			(0xc1 << 2)
+#define CMN_PLL0_VCOCAL_OVRD		(0x83 << 2)
+#define CMN_PLL0_VCOCAL_INIT		(0x84 << 2)
+#define CMN_PLL0_VCOCAL_ITER		(0x85 << 2)
+#define CMN_PLL0_LOCK_REFCNT_START	(0x90 << 2)
+#define CMN_PLL0_LOCK_PLLCNT_START	(0x92 << 2)
+#define CMN_PLL0_LOCK_PLLCNT_THR	(0x93 << 2)
+#define CMN_PLL0_INTDIV			(0x94 << 2)
+#define CMN_PLL0_FRACDIV		(0x95 << 2)
+#define CMN_PLL0_HIGH_THR		(0x96 << 2)
+#define CMN_PLL0_DSM_DIAG		(0x97 << 2)
+#define CMN_PLL0_SS_CTRL1		(0x98 << 2)
+#define CMN_PLL0_SS_CTRL2		(0x99 << 2)
+#define CMN_PLL1_VCOCAL_START		(0xa1 << 2)
+#define CMN_PLL1_VCOCAL_OVRD		(0xa3 << 2)
+#define CMN_PLL1_VCOCAL_INIT		(0xa4 << 2)
+#define CMN_PLL1_VCOCAL_ITER		(0xa5 << 2)
+#define CMN_PLL1_LOCK_REFCNT_START	(0xb0 << 2)
+#define CMN_PLL1_LOCK_PLLCNT_START	(0xb2 << 2)
+#define CMN_PLL1_LOCK_PLLCNT_THR	(0xb3 << 2)
+#define CMN_PLL1_INTDIV			(0xb4 << 2)
+#define CMN_PLL1_FRACDIV		(0xb5 << 2)
+#define CMN_PLL1_HIGH_THR		(0xb6 << 2)
+#define CMN_PLL1_DSM_DIAG		(0xb7 << 2)
+#define CMN_PLL1_SS_CTRL1		(0xb8 << 2)
+#define CMN_PLL1_SS_CTRL2		(0xb9 << 2)
+#define CMN_RXCAL_OVRD			(0xd1 << 2)
+#define CMN_TXPUCAL_CTRL		(0xe0 << 2)
+#define CMN_TXPUCAL_OVRD		(0xe1 << 2)
+#define CMN_TXPDCAL_OVRD		(0xf1 << 2)
+#define CMN_DIAG_PLL0_FBH_OVRD		(0x1c0 << 2)
+#define CMN_DIAG_PLL0_FBL_OVRD		(0x1c1 << 2)
+#define CMN_DIAG_PLL0_OVRD		(0x1c2 << 2)
+#define CMN_DIAG_PLL0_V2I_TUNE		(0x1c5 << 2)
+#define CMN_DIAG_PLL0_CP_TUNE		(0x1c6 << 2)
+#define CMN_DIAG_PLL0_LF_PROG		(0x1c7 << 2)
+#define CMN_DIAG_PLL1_FBH_OVRD		(0x1d0 << 2)
+#define CMN_DIAG_PLL1_FBL_OVRD		(0x1d1 << 2)
+#define CMN_DIAG_PLL1_OVRD		(0x1d2 << 2)
+#define CMN_DIAG_PLL1_V2I_TUNE		(0x1d5 << 2)
+#define CMN_DIAG_PLL1_CP_TUNE		(0x1d6 << 2)
+#define CMN_DIAG_PLL1_LF_PROG		(0x1d7 << 2)
+#define CMN_DIAG_PLL1_PTATIS_TUNE1	(0x1d8 << 2)
+#define CMN_DIAG_PLL1_PTATIS_TUNE2	(0x1d9 << 2)
+#define CMN_DIAG_PLL1_INCLK_CTRL	(0x1da << 2)
+#define CMN_DIAG_HSCLK_SEL		(0x1e0 << 2)
+
+#define XCVR_PSM_RCTRL(n)		((0x4001 | ((n) << 9)) << 2)
+#define XCVR_PSM_CAL_TMR(n)		((0x4002 | ((n) << 9)) << 2)
+#define XCVR_PSM_A0IN_TMR(n)		((0x4003 | ((n) << 9)) << 2)
+#define TX_TXCC_CAL_SCLR_MULT(n)	((0x4047 | ((n) << 9)) << 2)
+#define TX_TXCC_CPOST_MULT_00(n)	((0x404c | ((n) << 9)) << 2)
+#define TX_TXCC_CPOST_MULT_01(n)	((0x404d | ((n) << 9)) << 2)
+#define TX_TXCC_CPOST_MULT_10(n)	((0x404e | ((n) << 9)) << 2)
+#define TX_TXCC_CPOST_MULT_11(n)	((0x404f | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_000(n)	((0x4050 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_001(n)	((0x4051 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_010(n)	((0x4052 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_011(n)	((0x4053 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_100(n)	((0x4054 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_101(n)	((0x4055 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_110(n)	((0x4056 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_111(n)	((0x4057 | ((n) << 9)) << 2)
+#define XCVR_DIAG_PLLDRC_CTRL(n)	((0x40e0 | ((n) << 9)) << 2)
+#define XCVR_DIAG_BIDI_CTRL(n)		((0x40e8 | ((n) << 9)) << 2)
+#define XCVR_DIAG_LANE_FCM_EN_MGN(n)	((0x40f2 | ((n) << 9)) << 2)
+#define TX_PSC_A0(n)			((0x4100 | ((n) << 9)) << 2)
+#define TX_PSC_A1(n)			((0x4101 | ((n) << 9)) << 2)
+#define TX_PSC_A2(n)			((0x4102 | ((n) << 9)) << 2)
+#define TX_PSC_A3(n)			((0x4103 | ((n) << 9)) << 2)
+#define TX_RCVDET_CTRL(n)		((0x4120 | ((n) << 9)) << 2)
+#define TX_RCVDET_EN_TMR(n)		((0x4122 | ((n) << 9)) << 2)
+#define TX_RCVDET_ST_TMR(n)		((0x4123 | ((n) << 9)) << 2)
+#define TX_DIAG_TX_DRV(n)		((0x41e1 | ((n) << 9)) << 2)
+#define TX_DIAG_BGREF_PREDRV_DELAY	(0x41e7 << 2)
+#define TX_ANA_CTRL_REG_1		(0x5020 << 2)
+#define TX_ANA_CTRL_REG_2		(0x5021 << 2)
+#define TXDA_COEFF_CALC_CTRL		(0x5022 << 2)
+#define TX_DIG_CTRL_REG_2		(0x5024 << 2)
+#define TXDA_CYA_AUXDA_CYA		(0x5025 << 2)
+#define TX_ANA_CTRL_REG_3		(0x5026 << 2)
+#define TX_ANA_CTRL_REG_4		(0x5027 << 2)
+#define TX_ANA_CTRL_REG_5		(0x5029 << 2)
+
+#define RX_PSC_A0(n)			((0x8000 | ((n) << 9)) << 2)
+#define RX_PSC_A1(n)			((0x8001 | ((n) << 9)) << 2)
+#define RX_PSC_A2(n)			((0x8002 | ((n) << 9)) << 2)
+#define RX_PSC_A3(n)			((0x8003 | ((n) << 9)) << 2)
+#define RX_PSC_CAL(n)			((0x8006 | ((n) << 9)) << 2)
+#define RX_PSC_RDY(n)			((0x8007 | ((n) << 9)) << 2)
+#define RX_IQPI_ILL_CAL_OVRD		(0x8023 << 2)
+#define RX_EPI_ILL_CAL_OVRD		(0x8033 << 2)
+#define RX_SDCAL0_OVRD			(0x8041 << 2)
+#define RX_SDCAL1_OVRD			(0x8049 << 2)
+#define RX_SLC_INIT			(0x806d << 2)
+#define RX_SLC_RUN			(0x806e << 2)
+#define RX_CDRLF_CNFG2			(0x8081 << 2)
+#define RX_SIGDET_HL_FILT_TMR(n)	((0x8090 | ((n) << 9)) << 2)
+#define RX_SLC_IOP0_OVRD		(0x8101 << 2)
+#define RX_SLC_IOP1_OVRD		(0x8105 << 2)
+#define RX_SLC_QOP0_OVRD		(0x8109 << 2)
+#define RX_SLC_QOP1_OVRD		(0x810d << 2)
+#define RX_SLC_EOP0_OVRD		(0x8111 << 2)
+#define RX_SLC_EOP1_OVRD		(0x8115 << 2)
+#define RX_SLC_ION0_OVRD		(0x8119 << 2)
+#define RX_SLC_ION1_OVRD		(0x811d << 2)
+#define RX_SLC_QON0_OVRD		(0x8121 << 2)
+#define RX_SLC_QON1_OVRD		(0x8125 << 2)
+#define RX_SLC_EON0_OVRD		(0x8129 << 2)
+#define RX_SLC_EON1_OVRD		(0x812d << 2)
+#define RX_SLC_IEP0_OVRD		(0x8131 << 2)
+#define RX_SLC_IEP1_OVRD		(0x8135 << 2)
+#define RX_SLC_QEP0_OVRD		(0x8139 << 2)
+#define RX_SLC_QEP1_OVRD		(0x813d << 2)
+#define RX_SLC_EEP0_OVRD		(0x8141 << 2)
+#define RX_SLC_EEP1_OVRD		(0x8145 << 2)
+#define RX_SLC_IEN0_OVRD		(0x8149 << 2)
+#define RX_SLC_IEN1_OVRD		(0x814d << 2)
+#define RX_SLC_QEN0_OVRD		(0x8151 << 2)
+#define RX_SLC_QEN1_OVRD		(0x8155 << 2)
+#define RX_SLC_EEN0_OVRD		(0x8159 << 2)
+#define RX_SLC_EEN1_OVRD		(0x815d << 2)
+#define RX_DIAG_SIGDET_TUNE(n)		((0x81dc | ((n) << 9)) << 2)
+#define RX_DIAG_SC2C_DELAY		(0x81e1 << 2)
+
+#define PMA_LANE_CFG			(0xc000 << 2)
+#define PIPE_CMN_CTRL1			(0xc001 << 2)
+#define PIPE_CMN_CTRL2			(0xc002 << 2)
+#define PIPE_COM_LOCK_CFG1		(0xc003 << 2)
+#define PIPE_COM_LOCK_CFG2		(0xc004 << 2)
+#define PIPE_RCV_DET_INH		(0xc005 << 2)
+#define DP_MODE_CTL			(0xc008 << 2)
+#define DP_CLK_CTL			(0xc009 << 2)
+#define STS				(0xc00F << 2)
+#define PHY_ISO_CMN_CTRL		(0xc010 << 2)
+#define PHY_DP_TX_CTL			(0xc408 << 2)
+#define PMA_CMN_CTRL1			(0xc800 << 2)
+#define PHY_PMA_ISO_CMN_CTRL		(0xc810 << 2)
+#define PHY_ISOLATION_CTRL		(0xc81f << 2)
+#define PHY_PMA_ISO_XCVR_CTRL(n)	((0xcc11 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_LINK_MODE(n)	((0xcc12 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_PWRST_CTRL(n)	((0xcc13 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_TX_DATA_LO(n)	((0xcc14 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_TX_DATA_HI(n)	((0xcc15 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_RX_DATA_LO(n)	((0xcc16 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_RX_DATA_HI(n)	((0xcc17 | ((n) << 6)) << 2)
+#define TX_BIST_CTRL(n)			((0x4140 | ((n) << 9)) << 2)
+#define TX_BIST_UDDWR(n)		((0x4141 | ((n) << 9)) << 2)
+
+#define CLK0_PLL_MASK			0x3
+#define PLL0_DIV1			0
+#define CLK1_PLL_MASK			0x30
+#define PLL1_DIV2			0x30
+
+#define CMN_READY			BIT(0)
+
+#define DP_PLL_CLOCK_ENABLE		BIT(2)
+#define DP_PLL_ENABLE			BIT(0)
+#define DP_PLL_DATA_RATE_RBR		((2 << 12) | (4 << 8))
+#define DP_PLL_DATA_RATE_HBR		((2 << 12) | (4 << 8))
+#define DP_PLL_DATA_RATE_HBR2		((1 << 12) | (2 << 8))
+
+#define GRF_SOC_CON26			0x6268
+#define UPHY_DP_SEL			BIT(3)
+#define UPHY_DP_SEL_MASK		BIT(19)
+#define DPTX_HPD_SEL			(3 << 12)
+#define DPTX_HPD_DEL			(2 << 12)
+#define DPTX_HPD_SEL_MASK		(3 << 28)
+
+#define PHY_MODE_SET_TIMEOUT		1000000
+
+#define PIN_ASSIGN_A			BIT(0)
+#define PIN_ASSIGN_B			BIT(1)
+#define PIN_ASSIGN_C			BIT(2)
+#define PIN_ASSIGN_D			BIT(3)
+#define PIN_ASSIGN_E			BIT(4)
+#define PIN_ASSIGN_F			BIT(5)
+
+#define MODE_DISCONNECT			0
+#define MODE_UFP_USB			BIT(0)
+#define MODE_DFP_USB			BIT(1)
+#define MODE_DFP_DP			BIT(2)
+
+struct usb3phy_reg {
+	u32 offset;
+	u32 enable_bit;
+	u32 write_enable;
+};
+
+struct rockchip_usb3phy_port_cfg {
+	struct usb3phy_reg typec_conn_dir;
+	struct usb3phy_reg usb3tousb2_en;
+	struct usb3phy_reg external_psm;
+	struct usb3phy_reg pipe_status;
+	struct usb3phy_reg uphy_dp_sel;
+};
+
+struct rockchip_typec_phy {
+	struct device *dev;
+	void __iomem *base;
+	struct extcon_dev *extcon;
+	struct phy *phy;
+	struct regmap *grf_regs;
+	struct clk *clk_core;
+	struct clk *clk_ref;
+	struct reset_control *uphy_rst;
+	struct reset_control *pipe_rst;
+	struct reset_control *tcphy_rst;
+	struct rockchip_usb3phy_port_cfg port_cfgs;
+
+	bool flip;
+	u8 mode;
+	u8 pin_assign;
+};
+
+struct phy_reg {
+	u16 value;
+	u32 addr;
+};
+
+struct phy_reg usb_pll_cfg[] = {
+	{ 0xf0,		CMN_PLL0_VCOCAL_INIT },
+	{ 0x18,		CMN_PLL0_VCOCAL_ITER },
+	{ 0xd0,		CMN_PLL0_INTDIV },
+	{ 0x4a4a,	CMN_PLL0_FRACDIV },
+	{ 0x34,		CMN_PLL0_HIGH_THR },
+	{ 0x1ee,	CMN_PLL0_SS_CTRL1 },
+	{ 0x7f03,	CMN_PLL0_SS_CTRL2 },
+	{ 0x20,		CMN_PLL0_DSM_DIAG },
+	{ 0,		CMN_DIAG_PLL0_OVRD },
+	{ 0,		CMN_DIAG_PLL0_FBH_OVRD },
+	{ 0,		CMN_DIAG_PLL0_FBL_OVRD },
+	{ 0x7,		CMN_DIAG_PLL0_V2I_TUNE },
+	{ 0x45,		CMN_DIAG_PLL0_CP_TUNE },
+	{ 0x8,		CMN_DIAG_PLL0_LF_PROG },
+};
+
+struct phy_reg dp_pll_cfg[] = {
+	{ 0xf0,		CMN_PLL1_VCOCAL_INIT },
+	{ 0x18,		CMN_PLL1_VCOCAL_ITER },
+	{ 0x30b9,	CMN_PLL1_VCOCAL_START },
+	{ 0x21c,	CMN_PLL1_INTDIV },
+	{ 0,		CMN_PLL1_FRACDIV },
+	{ 0x5,		CMN_PLL1_HIGH_THR },
+	{ 0x35,		CMN_PLL1_SS_CTRL1 },
+	{ 0x7f1e,	CMN_PLL1_SS_CTRL2 },
+	{ 0x20,		CMN_PLL1_DSM_DIAG },
+	{ 0,		CMN_PLLSM1_USER_DEF_CTRL },
+	{ 0,		CMN_DIAG_PLL1_OVRD },
+	{ 0,		CMN_DIAG_PLL1_FBH_OVRD },
+	{ 0,		CMN_DIAG_PLL1_FBL_OVRD },
+	{ 0x6,		CMN_DIAG_PLL1_V2I_TUNE },
+	{ 0x45,		CMN_DIAG_PLL1_CP_TUNE },
+	{ 0x8,		CMN_DIAG_PLL1_LF_PROG },
+	{ 0x100,	CMN_DIAG_PLL1_PTATIS_TUNE1 },
+	{ 0x7,		CMN_DIAG_PLL1_PTATIS_TUNE2 },
+	{ 0x4,		CMN_DIAG_PLL1_INCLK_CTRL },
+};
+
+static void tcphy_cfg_24m(struct rockchip_typec_phy *tcphy,
+			  u32 num_lanes)
+{
+	u32 i;
+
+	/*
+	 * cmn_ref_clk_sel = 3, select the 24Mhz for clk parent
+	 * cmn_psm_clk_dig_div = 2, set the clk division to 2
+	 */
+	writel(0x830, tcphy->base + PMA_CMN_CTRL1);
+	for (i = 0; i < num_lanes; i++) {
+		/*
+		 * The following PHY configuration assumes a 24 MHz reference
+		 * clock.
+		 */
+		writel(0x90, tcphy->base + XCVR_DIAG_LANE_FCM_EN_MGN(i));
+		writel(0x960, tcphy->base + TX_RCVDET_EN_TMR(i));
+		writel(0x30, tcphy->base + TX_RCVDET_ST_TMR(i));
+	}
+}
+
+static void tcphy_cfg_usb_pll(struct rockchip_typec_phy *tcphy)
+{
+	u16 rdata;
+	u32 i;
+
+	/*
+	 * Selects which PLL clock will be driven on the analog high speed
+	 * clock 0: PLL 0 div 1.
+	 */
+	rdata = readl(tcphy->base + CMN_DIAG_HSCLK_SEL);
+	rdata &= ~CLK0_PLL_MASK;
+	rdata |= PLL0_DIV1;
+	writel(rdata, tcphy->base + CMN_DIAG_HSCLK_SEL);
+
+	/* load the configuration of PLL0 */
+	for (i = 0; i < ARRAY_SIZE(usb_pll_cfg); i++)
+		writel(usb_pll_cfg[i].value, tcphy->base + usb_pll_cfg[i].addr);
+}
+
+static void tcphy_cfg_dp_pll(struct rockchip_typec_phy *tcphy)
+{
+	u16 rdata;
+	u32 i;
+
+	/* set the default mode to RBR */
+	writel(DP_PLL_CLOCK_ENABLE | DP_PLL_ENABLE | DP_PLL_DATA_RATE_RBR,
+	       tcphy->base + DP_CLK_CTL);
+
+	/*
+	 * Selects which PLL clock will be driven on the analog high speed
+	 * clock 1: PLL 1 div 2.
+	 */
+	rdata = readl(tcphy->base + CMN_DIAG_HSCLK_SEL);
+	rdata &= ~CLK1_PLL_MASK;
+	rdata |= PLL1_DIV2;
+	writel(rdata, tcphy->base + CMN_DIAG_HSCLK_SEL);
+
+	/* load the configuration of PLL1 */
+	for (i = 0; i < ARRAY_SIZE(dp_pll_cfg); i++)
+		writel(dp_pll_cfg[i].value, tcphy->base + dp_pll_cfg[i].addr);
+}
+
+static void tcphy_tx_usb_cfg_lane(struct rockchip_typec_phy *tcphy,
+				  u32 lane)
+{
+	writel(0x7799, tcphy->base + TX_PSC_A0(lane));
+	writel(0x7798, tcphy->base + TX_PSC_A1(lane));
+	writel(0x5098, tcphy->base + TX_PSC_A2(lane));
+	writel(0x5098, tcphy->base + TX_PSC_A3(lane));
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
+	writel(0xbf, tcphy->base + XCVR_DIAG_BIDI_CTRL(lane));
+}
+
+static void tcphy_rx_usb_cfg_lane(struct rockchip_typec_phy *tcphy,
+				  u32 lane)
+{
+	writel(0xa6fd, tcphy->base + RX_PSC_A0(lane));
+	writel(0xa6fd, tcphy->base + RX_PSC_A1(lane));
+	writel(0xa410, tcphy->base + RX_PSC_A2(lane));
+	writel(0x2410, tcphy->base + RX_PSC_A3(lane));
+	writel(0x23ff, tcphy->base + RX_PSC_CAL(lane));
+	writel(0x13, tcphy->base + RX_SIGDET_HL_FILT_TMR(lane));
+	writel(0x1004, tcphy->base + RX_DIAG_SIGDET_TUNE(lane));
+	writel(0x2010, tcphy->base + RX_PSC_RDY(lane));
+	writel(0xfb, tcphy->base + XCVR_DIAG_BIDI_CTRL(lane));
+}
+
+static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy,
+			      u32 lane)
+{
+	u16 rdata;
+
+	writel(0xbefc, tcphy->base + XCVR_PSM_RCTRL(lane));
+	writel(0x6799, tcphy->base + TX_PSC_A0(lane));
+	writel(0x6798, tcphy->base + TX_PSC_A1(lane));
+	writel(0x98, tcphy->base + TX_PSC_A2(lane));
+	writel(0x98, tcphy->base + TX_PSC_A3(lane));
+
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_001(lane));
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_010(lane));
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_011(lane));
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_100(lane));
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_101(lane));
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_110(lane));
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_111(lane));
+	writel(0, tcphy->base + TX_TXCC_CPOST_MULT_10(lane));
+	writel(0, tcphy->base + TX_TXCC_CPOST_MULT_01(lane));
+	writel(0, tcphy->base + TX_TXCC_CPOST_MULT_00(lane));
+	writel(0, tcphy->base + TX_TXCC_CPOST_MULT_11(lane));
+
+	writel(0x128, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
+	writel(0x700, tcphy->base + TX_DIAG_TX_DRV(lane));
+
+	rdata = readl(tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
+	rdata = (rdata & 0x8fff) | 0x6000;
+	writel(rdata, tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
+}
+
+static void tcphy_cfg_pin_assign(struct rockchip_typec_phy *tcphy)
+{
+	switch (tcphy->pin_assign) {
+	case PIN_ASSIGN_A:
+		writel(0x19d5, tcphy->base + PMA_LANE_CFG);
+		break;
+	case PIN_ASSIGN_B:
+		writel(0x1500, tcphy->base + PMA_LANE_CFG);
+		break;
+	case PIN_ASSIGN_C:
+	case PIN_ASSIGN_E:
+		writel(0x51d9, tcphy->base + PMA_LANE_CFG);
+		break;
+	case PIN_ASSIGN_D:
+	case PIN_ASSIGN_F:
+		writel(0x5100, tcphy->base + PMA_LANE_CFG);
+		break;
+	};
+}
+
+static inline int property_enable(struct rockchip_typec_phy *tcphy,
+				  const struct usb3phy_reg *reg, bool en)
+{
+	u32 mask = 1 << reg->write_enable;
+	u32 val = en << reg->enable_bit;
+
+	return regmap_write(tcphy->grf_regs, reg->offset, val | mask);
+}
+
+static void tcphy_lanes_config(struct rockchip_typec_phy *tcphy)
+{
+	struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+	u32 i;
+
+	property_enable(tcphy, &cfg->typec_conn_dir, tcphy->flip);
+
+	tcphy_cfg_24m(tcphy, 0x4);
+
+	switch (tcphy->mode) {
+	case MODE_UFP_USB:
+	case MODE_DFP_USB:
+		tcphy_cfg_usb_pll(tcphy);
+		tcphy_cfg_dp_pll(tcphy);
+		if (tcphy->flip) {
+			tcphy_tx_usb_cfg_lane(tcphy, 3);
+			tcphy_rx_usb_cfg_lane(tcphy, 2);
+		} else {
+			tcphy_tx_usb_cfg_lane(tcphy, 0);
+			tcphy_rx_usb_cfg_lane(tcphy, 1);
+		}
+		break;
+	case MODE_DFP_DP:
+		tcphy_cfg_dp_pll(tcphy);
+		for (i = 0; i < 4; i++)
+			tcphy_dp_cfg_lane(tcphy, i);
+		break;
+	case MODE_DFP_USB | MODE_DFP_DP:
+		tcphy_cfg_usb_pll(tcphy);
+		tcphy_cfg_dp_pll(tcphy);
+		if (tcphy->flip) {
+			tcphy_tx_usb_cfg_lane(tcphy, 3);
+			tcphy_rx_usb_cfg_lane(tcphy, 2);
+			tcphy_dp_cfg_lane(tcphy, 0);
+			tcphy_dp_cfg_lane(tcphy, 1);
+		} else {
+			tcphy_tx_usb_cfg_lane(tcphy, 0);
+			tcphy_rx_usb_cfg_lane(tcphy, 1);
+			tcphy_dp_cfg_lane(tcphy, 2);
+			tcphy_dp_cfg_lane(tcphy, 3);
+		}
+		break;
+	}
+}
+
+static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy)
+{
+	u16 rdata, rdata2, val;
+
+	/* disable txda_cal_latch_en for rewrite the calibration values */
+	rdata = readl(tcphy->base + TX_ANA_CTRL_REG_1);
+	val = rdata & 0xdfff;
+	writel(val, tcphy->base + TX_ANA_CTRL_REG_1);
+
+	/*
+	 * read a resistor calibration code from CMN_TXPUCAL_CTRL[6:0] and
+	 * write it to TX_DIG_CTRL_REG_2[6:0], and delay 1ms to make sure it
+	 * works.
+	 */
+	rdata = readl(tcphy->base + TX_DIG_CTRL_REG_2);
+	rdata = rdata & 0xffc0;
+
+	rdata2 = readl(tcphy->base + CMN_TXPUCAL_CTRL);
+	rdata2 = rdata2 & 0x3f;
+
+	val = rdata | rdata2;
+	writel(val, tcphy->base + TX_DIG_CTRL_REG_2);
+	usleep_range(1000, 1050);
+
+	/*
+	 * Enable signal for latch that sample and holds calibration values.
+	 * Activate this signal for 1 clock cycle to sample new calibration
+	 * values.
+	 */
+	rdata = readl(tcphy->base + TX_ANA_CTRL_REG_1);
+	val = rdata | 0x2000;
+	writel(val, tcphy->base + TX_ANA_CTRL_REG_1);
+	usleep_range(150, 200);
+
+	/* set TX Voltage Level and TX Deemphasis to 0 */
+	writel(0, tcphy->base + PHY_DP_TX_CTL);
+	/* re-enable decap */
+	writel(0x100, tcphy->base + TX_ANA_CTRL_REG_2);
+	writel(0x300, tcphy->base + TX_ANA_CTRL_REG_2);
+	writel(0x2008, tcphy->base + TX_ANA_CTRL_REG_1);
+	writel(0x2018, tcphy->base + TX_ANA_CTRL_REG_1);
+
+	writel(0, tcphy->base + TX_ANA_CTRL_REG_5);
+
+	/*
+	 * Programs txda_drv_ldo_prog[15:0], Sets driver LDO
+	 * voltage 16'h1001 for DP-AUX-TX and RX
+	 */
+	writel(0x1001, tcphy->base + TX_ANA_CTRL_REG_4);
+
+	/* re-enables Bandgap reference for LDO */
+	writel(0x2098, tcphy->base + TX_ANA_CTRL_REG_1);
+	writel(0x2198, tcphy->base + TX_ANA_CTRL_REG_1);
+
+	/*
+	 * re-enables the transmitter pre-driver, driver data selection MUX,
+	 * and receiver detect circuits.
+	 */
+	writel(0x301, tcphy->base + TX_ANA_CTRL_REG_2);
+	writel(0x303, tcphy->base + TX_ANA_CTRL_REG_2);
+
+	/*
+	 * BIT 12: Controls auxda_polarity, which selects the polarity of the
+	 * xcvr:
+	 * 1, Reverses the polarity (If TYPEC, Pulls ups aux_p and pull
+	 * down aux_m)
+	 * 0, Normal polarity (if TYPE_C, pulls up aux_m and pulls down
+	 * aux_p)
+	 */
+	val = 0xa078;
+	if (!tcphy->flip)
+		val |= BIT(12);
+	writel(val, tcphy->base + TX_ANA_CTRL_REG_1);
+
+	writel(0, tcphy->base + TX_ANA_CTRL_REG_3);
+	writel(0, tcphy->base + TX_ANA_CTRL_REG_4);
+	writel(0, tcphy->base + TX_ANA_CTRL_REG_5);
+
+	/*
+	 * Controls low_power_swing_en, set the voltage swing of the driver
+	 * to 400mv. The values	below are peak to peak (differential) values.
+	 */
+	writel(4, tcphy->base + TXDA_COEFF_CALC_CTRL);
+	writel(0, tcphy->base + TXDA_CYA_AUXDA_CYA);
+
+	/* Controls tx_high_z_tm_en */
+	val = readl(tcphy->base + TX_DIG_CTRL_REG_2);
+	val |= BIT(15);
+	writel(val, tcphy->base + TX_DIG_CTRL_REG_2);
+}
+
+static void tcphy_usb3_init(struct rockchip_typec_phy *tcphy)
+{
+	struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+	const struct usb3phy_reg *reg = &cfg->pipe_status;
+	int timeout;
+	unsigned int val;
+
+	/* wait TCPHY for pipe ready */
+	for (timeout = 0; timeout < 100; timeout++) {
+		regmap_read(tcphy->grf_regs, reg->offset, &val);
+		if (!(val & BIT(reg->enable_bit)))
+			return;
+		usleep_range(10, 20);
+	}
+
+	dev_err(tcphy->dev, "wait pipe ready timeout!\n");
+}
+
+static int tcphy_dp_init(struct rockchip_typec_phy *tcphy)
+{
+	struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+	u16 val;
+	int ret;
+
+	property_enable(tcphy, &cfg->uphy_dp_sel, 1);
+
+	ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
+				 val, val & BIT(6), 1000, PHY_MODE_SET_TIMEOUT);
+	if (ret < 0) {
+		dev_err(tcphy->dev, "failed to wait TCPHY for DP ready\n");
+		return ret;
+	}
+
+	tcphy_dp_aux_calibration(tcphy);
+
+	if (tcphy->mode & MODE_DFP_USB)
+		writel(0xc101, tcphy->base + DP_MODE_CTL);
+	else
+		writel(0x0101, tcphy->base + DP_MODE_CTL);
+
+	ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
+				 val, val & BIT(4), 1000, PHY_MODE_SET_TIMEOUT);
+	if (ret < 0) {
+		dev_err(tcphy->dev, "failed to wait TCPHY enter A0\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int tcphy_phy_init(struct rockchip_typec_phy *tcphy)
+{
+	struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+	u32 val;
+	int ret;
+
+	ret = clk_prepare_enable(tcphy->clk_core);
+	if (ret) {
+		dev_err(tcphy->dev, "Failed to prepare_enable core clock\n");
+		return ret;
+	}
+
+	ret = clk_set_rate(tcphy->clk_core, 50000000);
+	if (ret) {
+		dev_err(tcphy->dev, "set type-c phy core clk rate failed\n");
+		goto err_clk_core;
+	}
+
+	ret = clk_prepare_enable(tcphy->clk_ref);
+	if (ret) {
+		dev_err(tcphy->dev, "Failed to prepare_enable ref clock\n");
+		goto err_clk_core;
+	}
+
+	reset_control_assert(tcphy->tcphy_rst);
+	reset_control_assert(tcphy->uphy_rst);
+	reset_control_assert(tcphy->pipe_rst);
+
+	/* select external psm clock */
+	property_enable(tcphy, &cfg->external_psm, 1);
+	property_enable(tcphy, &cfg->usb3tousb2_en, 0);
+
+	reset_control_deassert(tcphy->tcphy_rst);
+
+	tcphy_lanes_config(tcphy);
+
+	tcphy_cfg_pin_assign(tcphy);
+
+	if (tcphy->mode & MODE_DFP_DP) {
+		if (tcphy->mode & MODE_DFP_USB)
+			writel(0xc104, tcphy->base + DP_MODE_CTL);
+		else
+			writel(0x0104, tcphy->base + DP_MODE_CTL);
+	}
+
+	reset_control_deassert(tcphy->uphy_rst);
+
+	ret = readx_poll_timeout(readl, tcphy->base + PMA_CMN_CTRL1,
+				 val, val & CMN_READY, 10,
+				 PHY_MODE_SET_TIMEOUT);
+	if (ret < 0) {
+		dev_err(tcphy->dev, "wait pma ready timeout\n");
+		goto timeout_pma_ready;
+	}
+
+	reset_control_deassert(tcphy->pipe_rst);
+
+	return 0;
+
+timeout_pma_ready:
+	clk_disable_unprepare(tcphy->clk_ref);
+err_clk_core:
+	clk_disable_unprepare(tcphy->clk_core);
+	return ret;
+}
+
+static void tcphy_phy_deinit(struct rockchip_typec_phy *tcphy)
+{
+	clk_disable_unprepare(tcphy->clk_core);
+	clk_disable_unprepare(tcphy->clk_ref);
+	reset_control_assert(tcphy->tcphy_rst);
+	reset_control_assert(tcphy->uphy_rst);
+	reset_control_assert(tcphy->pipe_rst);
+}
+
+static int rockchip_typec_phy_power_on(struct phy *_phy)
+{
+	struct rockchip_typec_phy *tcphy = phy_get_drvdata(_phy);
+	struct extcon_dev *edev = tcphy->extcon;
+	bool plugged, dfp, ufp, dp;
+	union extcon_property_value property;
+	int ret, id;
+	u8 mode;
+
+	ufp = extcon_get_cable_state_(edev, EXTCON_USB);
+	dfp = extcon_get_cable_state_(edev, EXTCON_USB_HOST);
+	dp = extcon_get_cable_state_(edev, EXTCON_DISP_DP);
+
+	plugged = ufp | dfp | dp;
+
+	if (!plugged) {
+		dev_err(tcphy->dev, "Nothing plugged in\n");
+		return -EINVAL;
+	}
+
+	if (ufp) {
+		mode = MODE_UFP_USB;
+		id = EXTCON_USB;
+	} else if (dfp && !dp) {
+		mode = MODE_DFP_USB;
+		id = EXTCON_USB_HOST;
+	} else if (dfp && dp) {
+		mode = MODE_DFP_USB | MODE_DFP_DP;
+		tcphy->pin_assign = PIN_ASSIGN_D;
+		id = EXTCON_DISP_DP;
+	} else {
+		mode = MODE_DFP_DP;
+		tcphy->pin_assign = PIN_ASSIGN_C;
+		id = EXTCON_DISP_DP;
+	}
+
+	ret = extcon_get_cable_property(edev, id, EXTCON_PROP_TYPEC_POLARITY,
+					&property);
+	if (ret) {
+		dev_err(tcphy->dev, "get property failed\n");
+		return ret;
+	}
+
+	tcphy->flip = property.intval ? 1 : 0;
+
+	if (tcphy->mode == mode)
+		return 0;
+
+	tcphy->mode = mode;
+
+	regmap_write(tcphy->grf_regs, GRF_SOC_CON26,
+		     DPTX_HPD_SEL_MASK | DPTX_HPD_DEL);
+
+	ret = tcphy_phy_init(tcphy);
+	if (ret)
+		return ret;
+
+	if (mode & (MODE_UFP_USB | MODE_DFP_USB))
+		tcphy_usb3_init(tcphy);
+
+	if (mode & MODE_DFP_DP) {
+		ret = tcphy_dp_init(tcphy);
+		if (ret)
+			return ret;
+
+		regmap_write(tcphy->grf_regs, GRF_SOC_CON26,
+			     DPTX_HPD_SEL_MASK | DPTX_HPD_SEL);
+	}
+
+	return 0;
+}
+
+static int rockchip_typec_phy_power_off(struct phy *_phy)
+{
+	struct rockchip_typec_phy *tcphy = phy_get_drvdata(_phy);
+
+	tcphy_phy_deinit(tcphy);
+	tcphy->mode = MODE_DISCONNECT;
+
+	return 0;
+}
+
+static const struct phy_ops rockchip_tcphy_ops = {
+	.power_on	= rockchip_typec_phy_power_on,
+	.power_off	= rockchip_typec_phy_power_off,
+	.owner		= THIS_MODULE,
+};
+
+static int tcphy_get_param(struct device *dev,
+			   struct usb3phy_reg *reg,
+			   const char *name)
+{
+	u32 buffer[3];
+	int ret;
+
+	ret = of_property_read_u32_array(dev->of_node, name, buffer, 3);
+	if (ret) {
+		dev_err(dev, "Can not parse %s\n", name);
+		return ret;
+	}
+
+	reg->offset = buffer[0];
+	reg->enable_bit = buffer[1];
+	reg->write_enable = buffer[2];
+	return 0;
+}
+
+static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
+			  struct device *dev)
+{
+	struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+	int ret;
+
+	ret = tcphy_get_param(dev, &cfg->typec_conn_dir,
+			      "rockchip,typec-conn-dir");
+	if (ret) {
+		dev_err(dev, "could not find rockchip,typec-conn-dir node\n");
+		return ret;
+	}
+
+	ret = tcphy_get_param(dev, &cfg->usb3tousb2_en,
+			      "rockchip,usb3tousb2-en");
+	if (ret) {
+		dev_err(dev, "could not find rockchip,usb3tousb2-en node\n");
+		return ret;
+	}
+
+	ret = tcphy_get_param(dev, &cfg->external_psm,
+			      "rockchip,external-psm");
+	if (ret) {
+		dev_err(dev, "could not find rockchip,external-psm node\n");
+		return ret;
+	}
+
+	ret = tcphy_get_param(dev, &cfg->pipe_status,
+			      "rockchip,pipe-status");
+	if (ret) {
+		dev_err(dev, "could not find rockchip,pipe-status node\n");
+		return ret;
+	}
+
+	ret = tcphy_get_param(dev, &cfg->uphy_dp_sel,
+			      "rockchip,uphy-dp-sel");
+	if (ret) {
+		dev_err(dev, "could not find rockchip,uphy-dp-sel node\n");
+		return ret;
+	}
+
+	tcphy->grf_regs = syscon_regmap_lookup_by_phandle(dev->of_node,
+							  "rockchip,grf");
+	if (IS_ERR(tcphy->grf_regs)) {
+		dev_err(dev, "could not find grf dt node\n");
+		return PTR_ERR(tcphy->grf_regs);
+	}
+
+	tcphy->clk_core = devm_clk_get(dev, "tcpdcore");
+	if (IS_ERR(tcphy->clk_core)) {
+		dev_err(dev, "could not get uphy core clock\n");
+		return PTR_ERR(tcphy->clk_core);
+	}
+
+	tcphy->clk_ref = devm_clk_get(dev, "tcpdphy-ref");
+	if (IS_ERR(tcphy->clk_ref)) {
+		dev_err(dev, "could not get uphy ref clock\n");
+		return PTR_ERR(tcphy->clk_ref);
+	}
+
+	tcphy->uphy_rst = devm_reset_control_get(dev, "uphy");
+	if (IS_ERR(tcphy->uphy_rst)) {
+		dev_err(dev, "no uphy_rst reset control found\n");
+		return PTR_ERR(tcphy->uphy_rst);
+	}
+
+	tcphy->pipe_rst = devm_reset_control_get(dev, "uphy-pipe");
+	if (IS_ERR(tcphy->pipe_rst)) {
+		dev_err(dev, "no pipe_rst reset control found\n");
+		return PTR_ERR(tcphy->pipe_rst);
+	}
+
+	tcphy->tcphy_rst = devm_reset_control_get(dev, "uphy-tcphy");
+	if (IS_ERR(tcphy->tcphy_rst)) {
+		dev_err(dev, "no tcphy_rst reset control found\n");
+		return PTR_ERR(tcphy->tcphy_rst);
+	}
+
+	return 0;
+}
+
+static int rockchip_typec_phy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct rockchip_typec_phy *tcphy;
+	struct phy_provider *phy_provider;
+	struct resource *res;
+	int ret;
+
+	tcphy = devm_kzalloc(dev, sizeof(*tcphy), GFP_KERNEL);
+	if (!tcphy)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	tcphy->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(tcphy->base)) {
+		dev_err(dev, "failed to remap phy regs\n");
+		return PTR_ERR(tcphy->base);
+	}
+
+	ret = tcphy_parse_dt(tcphy, dev);
+	if (ret)
+		return ret;
+
+	tcphy->dev = dev;
+	platform_set_drvdata(pdev, tcphy);
+
+	tcphy->mode = MODE_DISCONNECT;
+
+	tcphy->extcon = extcon_get_edev_by_phandle(dev, 0);
+	if (IS_ERR(tcphy->extcon)) {
+		if (PTR_ERR(tcphy->extcon) != -EPROBE_DEFER)
+			dev_err(dev, "Invalid or missing extcon\n");
+		return PTR_ERR(tcphy->extcon);
+	}
+
+	tcphy->phy = devm_phy_create(dev, NULL, &rockchip_tcphy_ops);
+	if (IS_ERR(tcphy->phy)) {
+		dev_err(dev, "failed to create Tepy-C phy\n");
+		return PTR_ERR(tcphy->phy);
+	}
+
+	phy_set_drvdata(tcphy->phy, tcphy);
+
+	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+	if (IS_ERR(phy_provider)) {
+		dev_err(dev, "Failed to register phy provider\n");
+		return PTR_ERR(phy_provider);
+	}
+
+	return 0;
+}
+
+static const struct of_device_id rockchip_typec_phy_dt_ids[] = {
+	{ .compatible = "rockchip,rk3399-typec-phy" },
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, rockchip_typec_phy_dt_ids);
+
+static struct platform_driver rockchip_typec_phy_driver = {
+	.probe		= rockchip_typec_phy_probe,
+	.driver		= {
+		.name	= "rockchip-typec-phy",
+		.of_match_table = rockchip_typec_phy_dt_ids,
+	},
+};
+
+module_platform_driver(rockchip_typec_phy_driver);
+
+MODULE_AUTHOR("Chris Zhong <zyw at rock-chips.com>");
+MODULE_AUTHOR("Kever Yang <kever.yang at rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip USB TYPE-C PHY driver");
+MODULE_LICENSE("GPL v2");
-- 
2.6.3



From zyw at rock-chips.com  Tue Jul 12 08:09:47 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Tue, 12 Jul 2016 23:09:47 +0800
Subject: [v5 PATCH 4/5] Documentation: bindings: add dt documentation for cdn
 DP controller
In-Reply-To: <1468336188-565-1-git-send-email-zyw@rock-chips.com>
References: <1468336188-565-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1468336188-565-5-git-send-email-zyw@rock-chips.com>

This patch adds a binding that describes the cdn DP controller for
rk3399.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
Acked-by: Rob Herring <robh at kernel.org>

---

Changes in v5: None
Changes in v4:
- add a reset node
- support 2 phys

Changes in v3:
- add SoC specific compatible string
- remove reg = <1>;

Changes in v2: None
Changes in v1:
- add extcon node description
- add #sound-dai-cells description

 .../bindings/display/rockchip/cdn-dp-rockchip.txt  | 67 ++++++++++++++++++++++
 1 file changed, 67 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/rockchip/cdn-dp-rockchip.txt

diff --git a/Documentation/devicetree/bindings/display/rockchip/cdn-dp-rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/cdn-dp-rockchip.txt
new file mode 100644
index 0000000..bf0b2ce
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/rockchip/cdn-dp-rockchip.txt
@@ -0,0 +1,67 @@
+Rockchip RK3399 specific extensions to the cdn Display Port
+================================
+
+Required properties:
+- compatible: must be "rockchip,rk3399-cdn-dp"
+
+- reg: physical base address of the controller and length
+
+- clocks: from common clock binding: handle to dp clock.
+
+- clock-names: from common clock binding:
+	       Required elements: "core-clk" "pclk" "spdif"
+
+- resets : a list of phandle + reset specifier pairs
+- reset-names : string reset name, must be:
+		"spdif"
+
+- rockchip,grf: this soc should set GRF regs, so need get grf here.
+
+- ports: contain a port nodes with endpoint definitions as defined in
+	 Documentation/devicetree/bindings/media/video-interfaces.txt.
+	 contained 2 endpoints, connecting to the output of vop.
+
+- phys: from general PHY binding: the phandle for the PHY device.
+
+- extcon: extcon specifier for the Power Delivery
+
+- #sound-dai-cells = it must be 1 if your system is using 2 DAIs: I2S, SPDIF
+
+-------------------------------------------------------------------------------
+
+Example:
+	cdn_dp: dp at fec00000 {
+		compatible = "rockchip,rk3399-cdn-dp";
+		reg = <0x0 0xfec00000 0x0 0x100000>;
+		interrupts = <GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru SCLK_DP_CORE>, <&cru PCLK_DP_CTRL>,
+			 <&cru SCLK_SPDIF_REC_DPTX>;
+		clock-names = "core-clk", "pclk", "spdif";
+		phys = <&tcphy0>, <&tcphy1>;
+		resets = <&cru SRST_DPTX_SPDIF_REC>;
+		reset-names = "spdif";
+		extcon = <&fusb0>, <&fusb1>;
+		rockchip,grf = <&grf>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		#sound-dai-cells = <1>;
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			dp_in: port {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				dp_in_vopb: endpoint at 0 {
+					reg = <0>;
+					remote-endpoint = <&vopb_out_dp>;
+				};
+
+				dp_in_vopl: endpoint at 1 {
+					reg = <1>;
+					remote-endpoint = <&vopl_out_dp>;
+				};
+			};
+		};
+	};
-- 
2.6.3



From zyw at rock-chips.com  Tue Jul 12 08:09:48 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Tue, 12 Jul 2016 23:09:48 +0800
Subject: [v5 PATCH 5/5] drm/rockchip: cdn-dp: add cdn DP support for rk3399
In-Reply-To: <1468336188-565-1-git-send-email-zyw@rock-chips.com>
References: <1468336188-565-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1468336188-565-6-git-send-email-zyw@rock-chips.com>

Add support for cdn DP controller which is embedded in the rk3399
SoCs. The DP is compliant with DisplayPort Specification,
Version 1.3, This IP is compatible with the rockchip type-c PHY IP.
There is a uCPU in DP controller, it need a firmware to work,
please put the firmware file to /lib/firmware/cdn/dptx.bin. The
uCPU in charge of aux communication and link training, the host use
mailbox to communicate with the ucpu.
The dclk pin_pol of vop must not be invert for DP.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>

---

Changes in v5:
- alphabetical order
- do not use long, use u32 or u64
- return MODE_CLOCK_HIGH when requested > actual
- Optimized Coding Style
- add a formula to get better tu size and symbol value.

Changes in v4:
- use phy framework to control DP phy
- support 2 phys

Changes in v3:
- use EXTCON_DISP_DP and EXTCON_DISP_DP_ALT cable to get dp port state.
- reset spdif before config it
- modify the firmware clk to 100Mhz
- retry load firmware if fw file is requested too early

Changes in v2:
- Alphabetic order
- remove excess error message
- use define clk_rate
- check all return value
- remove dev_set_name(dp->dev, "cdn-dp");
- use schedule_delayed_work
- remove never-called functions
- remove some unnecessary ()

Changes in v1:
- use extcon API
- use hdmi-codec for the DP Asoc
- do not initialize the "ret"
- printk a err log when drm_of_encoder_active_endpoint_id
- modify the dclk pin_pol to a single line

 drivers/gpu/drm/rockchip/Kconfig            |   9 +
 drivers/gpu/drm/rockchip/Makefile           |   1 +
 drivers/gpu/drm/rockchip/cdn-dp-core.c      | 761 ++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/cdn-dp-core.h      | 113 +++++
 drivers/gpu/drm/rockchip/cdn-dp-reg.c       | 740 +++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/cdn-dp-reg.h       | 409 +++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c |   6 +-
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   2 +
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c |   2 +
 9 files changed, 2042 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.c
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.h
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.c
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.h

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index d30bdc3..20da9a8 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -25,6 +25,15 @@ config ROCKCHIP_ANALOGIX_DP
 	  for the Analogix Core DP driver. If you want to enable DP
 	  on RK3288 based SoC, you should selet this option.
 
+config ROCKCHIP_CDN_DP
+        tristate "Rockchip cdn DP"
+        depends on DRM_ROCKCHIP
+        help
+	  This selects support for Rockchip SoC specific extensions
+	  for the cdn Dp driver. If you want to enable Dp on
+	  RK3399 based SoC, you should selet this
+	  option.
+
 config ROCKCHIP_DW_HDMI
         tristate "Rockchip specific extensions for Synopsys DW HDMI"
         depends on DRM_ROCKCHIP
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index 05d0713..abdecd5 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -7,6 +7,7 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
 rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
 
 obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
+obj-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
 obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
 obj-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
 obj-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
new file mode 100644
index 0000000..5b8a15e
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
@@ -0,0 +1,761 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Chris Zhong <zyw at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_of.h>
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/extcon.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/mfd/syscon.h>
+#include <linux/phy/phy.h>
+
+#include <sound/hdmi-codec.h>
+
+#include <video/of_videomode.h>
+#include <video/videomode.h>
+
+#include "cdn-dp-core.h"
+#include "cdn-dp-reg.h"
+#include "rockchip_drm_vop.h"
+
+#define connector_to_dp(c) \
+		container_of(c, struct cdn_dp_device, connector)
+
+#define encoder_to_dp(c) \
+		container_of(c, struct cdn_dp_device, encoder)
+
+/* dp grf register offset */
+#define DP_VOP_SEL		0x6224
+#define DP_SEL_VOP_LIT		BIT(12)
+#define DP_CLK_RATE		100000000
+#define WAIT_HPD_STABLE		300
+
+static int cdn_dp_clk_enable(struct cdn_dp_device *dp)
+{
+	int ret;
+
+	ret = clk_prepare_enable(dp->pclk);
+	if (ret < 0) {
+		dev_err(dp->dev, "cannot enable dp pclk %d\n", ret);
+		goto err_pclk;
+	}
+
+	ret = clk_prepare_enable(dp->core_clk);
+	if (ret < 0) {
+		dev_err(dp->dev, "cannot enable core_clk %d\n", ret);
+		goto err_core_clk;
+	}
+
+	ret = clk_set_rate(dp->core_clk, DP_CLK_RATE);
+	if (ret < 0) {
+		dev_err(dp->dev, "cannot set dp core clk to %d %d\n",
+			DP_CLK_RATE, ret);
+		goto err_set_rate;
+	}
+
+	/* notice fw the clk freq value */
+	cdn_dp_set_fw_clk(dp, DP_CLK_RATE);
+
+	return 0;
+
+err_set_rate:
+	clk_disable_unprepare(dp->core_clk);
+err_core_clk:
+	clk_disable_unprepare(dp->pclk);
+err_pclk:
+	return ret;
+}
+
+static enum drm_connector_status
+cdn_dp_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct cdn_dp_device *dp = connector_to_dp(connector);
+
+	return dp->hpd_status ? connector_status_connected :
+				connector_status_disconnected;
+}
+
+static void cdn_dp_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+static struct drm_connector_funcs cdn_dp_atomic_connector_funcs = {
+	.dpms = drm_atomic_helper_connector_dpms,
+	.detect = cdn_dp_connector_detect,
+	.destroy = cdn_dp_connector_destroy,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int cdn_dp_connector_get_modes(struct drm_connector *connector)
+{
+	struct cdn_dp_device *dp = connector_to_dp(connector);
+	struct edid *edid;
+
+	if (!dp->fw_loaded)
+		return 0;
+
+	edid = drm_do_get_edid(connector, cdn_dp_get_edid_block, dp);
+	if (edid) {
+		dev_dbg(dp->dev, "got edid: width[%d] x height[%d]\n",
+			edid->width_cm, edid->height_cm);
+
+		dp->sink_has_audio = drm_detect_monitor_audio(edid);
+		drm_mode_connector_update_edid_property(connector, edid);
+		drm_add_edid_modes(connector, edid);
+		/* Store the ELD */
+		drm_edid_to_eld(connector, edid);
+		kfree(edid);
+	} else {
+		dev_dbg(dp->dev, "failed to get edid\n");
+	}
+
+	return 0;
+}
+
+static struct drm_encoder *
+	cdn_dp_connector_best_encoder(struct drm_connector *connector)
+{
+	struct cdn_dp_device *dp = connector_to_dp(connector);
+
+	return &dp->encoder;
+}
+
+static int cdn_dp_connector_mode_valid(struct drm_connector *connector,
+				       struct drm_display_mode *mode)
+{
+	struct cdn_dp_device *dp = connector_to_dp(connector);
+	struct drm_display_info *display_info = &dp->connector.display_info;
+	u32 requested = mode->clock * display_info->bpc * 3 / 1000;
+	u32 actual, rate;
+
+	rate = drm_dp_bw_code_to_link_rate(dp->link.rate);
+	actual = rate * dp->link.num_lanes / 100;
+
+	/* efficiency is about 0.8 */
+	actual = actual * 8 / 10;
+
+	if (requested > actual) {
+		dev_dbg(dp->dev, "requested=%d, actual=%d, clock=%d\n",
+			requested, actual, mode->clock);
+		return MODE_CLOCK_HIGH;
+	}
+
+	return MODE_OK;
+}
+
+static struct drm_connector_helper_funcs cdn_dp_connector_helper_funcs = {
+	.get_modes = cdn_dp_connector_get_modes,
+	.best_encoder = cdn_dp_connector_best_encoder,
+	.mode_valid = cdn_dp_connector_mode_valid,
+};
+
+static void cdn_dp_commit(struct drm_encoder *encoder)
+{
+	struct cdn_dp_device *dp = encoder_to_dp(encoder);
+
+	if (cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE))
+		return;
+
+	if (cdn_dp_config_video(dp)) {
+		dev_err(dp->dev, "unable to config video\n");
+		return;
+	}
+
+	if (cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID))
+		return;
+
+	dp->dpms_mode = DRM_MODE_DPMS_ON;
+}
+
+static void cdn_dp_encoder_mode_set(struct drm_encoder *encoder,
+				    struct drm_display_mode *mode,
+				    struct drm_display_mode *adjusted)
+{
+	struct cdn_dp_device *dp = encoder_to_dp(encoder);
+	struct drm_display_info *display_info = &dp->connector.display_info;
+	struct rockchip_crtc_state *state;
+	struct video_info *video = &dp->video_info;
+	int ret, val;
+
+	switch (display_info->bpc) {
+	case 16:
+	case 12:
+	case 10:
+		video->color_depth = 10;
+		break;
+	case 6:
+		video->color_depth = 6;
+		break;
+	default:
+		video->color_depth = 8;
+		break;
+	}
+
+	video->color_fmt = PXL_RGB;
+
+	video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC);
+	video->h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC);
+
+	ret = drm_of_encoder_active_endpoint_id(dp->dev->of_node, encoder);
+	if (ret < 0) {
+		dev_err(dp->dev, "Could not get vop id, %d", ret);
+		return;
+	}
+
+	state = to_rockchip_crtc_state(encoder->crtc->state);
+	if (ret) {
+		val = DP_SEL_VOP_LIT | (DP_SEL_VOP_LIT << 16);
+		state->output_mode = ROCKCHIP_OUT_MODE_P888;
+	} else {
+		val = DP_SEL_VOP_LIT << 16;
+		state->output_mode = ROCKCHIP_OUT_MODE_AAAA;
+	}
+	ret = regmap_write(dp->grf, DP_VOP_SEL, val);
+	if (ret != 0)
+		dev_err(dp->dev, "Could not write to GRF: %d\n", ret);
+
+	memcpy(&dp->mode, adjusted, sizeof(*mode));
+}
+
+static void cdn_dp_encoder_enable(struct drm_encoder *encoder)
+{
+	struct cdn_dp_device *dp = encoder_to_dp(encoder);
+
+	if (dp->dpms_mode != DRM_MODE_DPMS_ON)
+		cdn_dp_commit(encoder);
+}
+
+static void cdn_dp_encoder_disable(struct drm_encoder *encoder)
+{
+	struct cdn_dp_device *dp = encoder_to_dp(encoder);
+
+	if (dp->dpms_mode != DRM_MODE_DPMS_OFF) {
+		cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE);
+		dp->dpms_mode = DRM_MODE_DPMS_OFF;
+	}
+}
+
+static int
+cdn_dp_encoder_atomic_check(struct drm_encoder *encoder,
+			    struct drm_crtc_state *crtc_state,
+			    struct drm_connector_state *conn_state)
+{
+	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+
+	s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
+	s->output_type = DRM_MODE_CONNECTOR_DisplayPort;
+
+	return 0;
+}
+
+static struct drm_encoder_helper_funcs cdn_dp_encoder_helper_funcs = {
+	.mode_set = cdn_dp_encoder_mode_set,
+	.enable = cdn_dp_encoder_enable,
+	.disable = cdn_dp_encoder_disable,
+	.atomic_check = cdn_dp_encoder_atomic_check,
+};
+
+static struct drm_encoder_funcs cdn_dp_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+static int cdn_dp_firmware_init(struct cdn_dp_device *dp)
+{
+	int ret;
+	const u32 *iram_data, *dram_data;
+	const struct firmware *fw = dp->fw;
+	const struct cdn_firmware_header *hdr;
+
+	if (dp->fw_loaded)
+		return 0;
+
+	hdr = (struct cdn_firmware_header *)fw->data;
+	if (fw->size != le32_to_cpu(hdr->size_bytes))
+		return -EINVAL;
+
+	iram_data = (const u32 *)(fw->data + hdr->header_size);
+	dram_data = (const u32 *)(fw->data + hdr->header_size + hdr->iram_size);
+
+	ret = cdn_dp_load_firmware(dp, iram_data, hdr->iram_size,
+				   dram_data, hdr->dram_size);
+	if (ret)
+		return ret;
+
+	release_firmware(fw);
+
+	ret = cdn_dp_active(dp, true);
+	if (ret) {
+		dev_err(dp->dev, "active ucpu failed: %d\n", ret);
+		return ret;
+	}
+
+	return cdn_dp_event_config(dp);
+}
+
+static int cdn_dp_init(struct cdn_dp_device *dp)
+{
+	struct device *dev = dp->dev;
+	struct device_node *np = dev->of_node;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct resource *res;
+	int ret;
+
+	dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+	if (IS_ERR(dp->grf)) {
+		dev_err(dev, "cdn-dp needs rockchip,grf property\n");
+		return PTR_ERR(dp->grf);
+	}
+
+	dp->irq = platform_get_irq(pdev, 0);
+	if (dp->irq < 0) {
+		dev_err(dev, "cdn-dp can not get irq\n");
+		return dp->irq;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	dp->regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(dp->regs)) {
+		dev_err(dev, "ioremap reg failed\n");
+		return PTR_ERR(dp->regs);
+	}
+
+	dp->core_clk = devm_clk_get(dev, "core-clk");
+	if (IS_ERR(dp->core_clk)) {
+		dev_err(dev, "cannot get core_clk_dp\n");
+		return PTR_ERR(dp->core_clk);
+	}
+
+	dp->pclk = devm_clk_get(dev, "pclk");
+	if (IS_ERR(dp->pclk)) {
+		dev_err(dev, "cannot get pclk\n");
+		return PTR_ERR(dp->pclk);
+	}
+
+	dp->spdif_clk = devm_clk_get(dev, "spdif");
+	if (IS_ERR(dp->spdif_clk)) {
+		dev_err(dev, "cannot get spdif_clk\n");
+		return PTR_ERR(dp->spdif_clk);
+	}
+
+	dp->spdif_rst = devm_reset_control_get(dev, "spdif");
+	if (IS_ERR(dp->spdif_rst)) {
+		dev_err(dev, "no spdif reset control found\n");
+		return PTR_ERR(dp->spdif_rst);
+	}
+
+	dp->dpms_mode = DRM_MODE_DPMS_OFF;
+
+	ret = cdn_dp_clk_enable(dp);
+	if (ret < 0)
+		return ret;
+
+	dp_clock_reset_seq(dp);
+
+	return 0;
+}
+
+static int cdn_dp_audio_hw_params(struct device *dev,
+				  struct hdmi_codec_daifmt *daifmt,
+				  struct hdmi_codec_params *params)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	struct audio_info audio = {
+		.sample_width = params->sample_width,
+		.sample_rate = params->sample_rate,
+		.channels = params->channels,
+	};
+	int ret;
+
+	if (!dp->encoder.crtc)
+		return -ENODEV;
+
+	switch (daifmt->fmt) {
+	case HDMI_I2S:
+		audio.format = AFMT_I2S;
+		break;
+	case HDMI_SPDIF:
+		audio.format = AFMT_SPDIF;
+		break;
+	default:
+		dev_err(dev, "%s: Invalid format %d\n", __func__, daifmt->fmt);
+		return -EINVAL;
+	}
+
+	ret = cdn_dp_audio_config_set(dp, &audio);
+	if (!ret)
+		dp->audio_info = audio;
+
+	return ret;
+}
+
+static void cdn_dp_audio_shutdown(struct device *dev)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	int ret = cdn_dp_audio_stop(dp, &dp->audio_info);
+
+	if (!ret)
+		dp->audio_info.format = AFMT_UNUSED;
+}
+
+static int cdn_dp_audio_digital_mute(struct device *dev, bool enable)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+
+	return cdn_dp_audio_mute(dp, enable);
+}
+
+static int cdn_dp_audio_get_eld(struct device *dev, uint8_t *buf, size_t len)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	struct drm_mode_config *config = &dp->encoder.dev->mode_config;
+	struct drm_connector *connector;
+	int ret = -ENODEV;
+
+	mutex_lock(&config->mutex);
+	list_for_each_entry(connector, &config->connector_list, head) {
+		if (&dp->encoder == connector->encoder) {
+			memcpy(buf, connector->eld,
+			       min(sizeof(connector->eld), len));
+			ret = 0;
+		}
+	}
+	mutex_unlock(&config->mutex);
+
+	return ret;
+}
+
+static const struct hdmi_codec_ops audio_codec_ops = {
+	.hw_params = cdn_dp_audio_hw_params,
+	.audio_shutdown = cdn_dp_audio_shutdown,
+	.digital_mute = cdn_dp_audio_digital_mute,
+	.get_eld = cdn_dp_audio_get_eld,
+};
+
+static int cdn_dp_audio_codec_init(struct cdn_dp_device *dp,
+				   struct device *dev)
+{
+	struct hdmi_codec_pdata codec_data = {
+		.i2s = 1,
+		.spdif = 1,
+		.ops = &audio_codec_ops,
+		.max_i2s_channels = 8,
+	};
+
+	dp->audio_pdev = platform_device_register_data(
+			 dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO,
+			 &codec_data, sizeof(codec_data));
+
+	return PTR_ERR_OR_ZERO(dp->audio_pdev);
+}
+
+static void cdn_dp_get_state(struct cdn_dp_device *dp, struct extcon_dev *edev)
+{
+	bool dfp, dptx;
+	u8 cap_lanes;
+
+	dfp = extcon_get_cable_state_(edev, EXTCON_USB_HOST);
+	dptx = extcon_get_cable_state_(edev, EXTCON_DISP_DP);
+
+	if (dfp && dptx)
+		cap_lanes = 2;
+	else if (dptx)
+		cap_lanes = 4;
+	else
+		cap_lanes = 0;
+
+	if (cap_lanes != dp->cap_lanes) {
+		dp->cap_lanes = cap_lanes;
+		schedule_delayed_work(&dp->event_wq, 0);
+	}
+}
+
+static int cdn_dp_pd_event(struct notifier_block *nb,
+			   unsigned long event, void *priv)
+{
+	struct cdn_dp_device *dp;
+	struct extcon_dev *edev = priv;
+	u8 i;
+
+	dp = container_of(nb, struct cdn_dp_device, event_nb);
+
+	for (i = 0; i < MAX_PHY; i++) {
+		if (edev == dp->extcon[i]) {
+			dp->port_id = i;
+			break;
+		}
+	}
+
+	cdn_dp_get_state(dp, edev);
+
+	return 0;
+}
+
+static void cdn_dp_pd_event_wq(struct work_struct *work)
+{
+	struct cdn_dp_device *dp;
+	int ret;
+
+	dp = container_of(work, struct cdn_dp_device, event_wq.work);
+
+	ret = request_firmware(&dp->fw, "cdn/dptx.bin", dp->dev);
+	if (ret == -ENOENT && !dp->fw_loaded) {
+		/*
+		 * If can not find the file, retry to load the firmware in 1
+		 * second, if still failed after 1 minute, give up.
+		 */
+		if (dp->fw_retry++ < 60) {
+			schedule_delayed_work(&dp->event_wq,
+					      msecs_to_jiffies(HZ));
+		}
+
+		dev_err(dp->dev, "failed to request firmware %d\n", ret);
+		return;
+	}
+
+	if (!dp->cap_lanes) {
+		if (dp->phy_status[dp->port_id])
+			phy_power_off(dp->phy[dp->port_id]);
+		dp->phy_status[dp->port_id] = 0;
+		dp->hpd_status = false;
+		drm_helper_hpd_irq_event(dp->drm_dev);
+		return;
+	}
+
+	if (!dp->phy_status[dp->port_id]) {
+		ret = phy_power_on(dp->phy[dp->port_id]);
+		if (ret)
+			return;
+
+		msleep(WAIT_HPD_STABLE);
+	}
+
+	dp->phy_status[dp->port_id] = 1;
+
+	ret = cdn_dp_firmware_init(dp);
+	if (ret)
+		return;
+
+	/* read hpd status failed, or the hpd does not exist. */
+	ret = cdn_dp_get_hpd_status(dp);
+	if (ret <= 0)
+		return;
+
+	/*
+	 * Set the capability and start the training process once
+	 * the hpd is detected.
+	 */
+	ret = cdn_dp_set_host_cap(dp);
+	if (ret) {
+		dev_err(dp->dev, "set host capabilities failed\n");
+		return;
+	}
+
+	ret = cdn_dp_training_start(dp);
+	if (ret) {
+		dev_err(dp->dev, "hw lt err:%d\n", ret);
+		return;
+	}
+
+	ret = cdn_dp_get_lt_status(dp);
+	if (ret) {
+		dev_err(dp->dev, "hw lt get status failed\n");
+		return;
+	}
+
+	dev_info(dp->dev, "rate:%d, lanes:%d\n",
+		 dp->link.rate, dp->link.num_lanes);
+
+	dp->hpd_status = true;
+	drm_helper_hpd_irq_event(dp->drm_dev);
+}
+
+static int cdn_dp_bind(struct device *dev, struct device *master,
+		       void *data)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	struct drm_encoder *encoder;
+	struct drm_connector *connector;
+	struct drm_device *drm_dev = data;
+	int ret, i;
+
+	ret = cdn_dp_init(dp);
+	if (ret < 0)
+		return ret;
+
+	dp->drm_dev = drm_dev;
+
+	encoder = &dp->encoder;
+
+	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
+							     dev->of_node);
+	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
+
+	ret = drm_encoder_init(drm_dev, encoder, &cdn_dp_encoder_funcs,
+			       DRM_MODE_ENCODER_TMDS, NULL);
+	if (ret) {
+		DRM_ERROR("failed to initialize encoder with drm\n");
+		return ret;
+	}
+
+	drm_encoder_helper_add(encoder, &cdn_dp_encoder_helper_funcs);
+
+	connector = &dp->connector;
+	connector->polled = DRM_CONNECTOR_POLL_HPD;
+	connector->dpms = DRM_MODE_DPMS_OFF;
+
+	ret = drm_connector_init(drm_dev, connector,
+				 &cdn_dp_atomic_connector_funcs,
+				 DRM_MODE_CONNECTOR_DisplayPort);
+	if (ret) {
+		DRM_ERROR("failed to initialize connector with drm\n");
+		goto err_free_encoder;
+	}
+
+	drm_connector_helper_add(connector, &cdn_dp_connector_helper_funcs);
+
+	ret = drm_mode_connector_attach_encoder(connector, encoder);
+	if (ret) {
+		DRM_ERROR("failed to attach connector and encoder\n");
+		goto err_free_connector;
+	}
+
+	cdn_dp_audio_codec_init(dp, dev);
+
+	dp->event_nb.notifier_call = cdn_dp_pd_event;
+	INIT_DELAYED_WORK(&dp->event_wq, cdn_dp_pd_event_wq);
+
+	for (i = 0; i < MAX_PHY; i++) {
+		if (IS_ERR(dp->extcon[i]))
+			continue;
+
+		ret = extcon_register_notifier(dp->extcon[i], EXTCON_DISP_DP,
+					       &dp->event_nb);
+		if (ret) {
+			dev_err(dev, "regitster EXTCON_DISP_DP notifier err\n");
+			return ret;
+		}
+
+		cdn_dp_get_state(dp, dp->extcon[i]);
+	}
+
+	return 0;
+
+err_free_connector:
+	drm_connector_cleanup(connector);
+err_free_encoder:
+	drm_encoder_cleanup(encoder);
+	return ret;
+}
+
+static void cdn_dp_unbind(struct device *dev, struct device *master, void *data)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	struct drm_encoder *encoder = &dp->encoder;
+	int i;
+
+	platform_device_unregister(dp->audio_pdev);
+	cdn_dp_encoder_disable(encoder);
+	encoder->funcs->destroy(encoder);
+	drm_connector_unregister(&dp->connector);
+	drm_connector_cleanup(&dp->connector);
+	drm_encoder_cleanup(encoder);
+
+	for (i = 0; i < MAX_PHY; i++) {
+		if (!IS_ERR(dp->extcon[i]))
+			extcon_unregister_notifier(dp->extcon[i], EXTCON_USB,
+						   &dp->event_nb);
+	}
+}
+
+static const struct component_ops cdn_dp_component_ops = {
+	.bind = cdn_dp_bind,
+	.unbind = cdn_dp_unbind,
+};
+
+static int cdn_dp_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct cdn_dp_device *dp;
+	u8 count = 0;
+	int i;
+
+	dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
+	if (!dp)
+		return -ENOMEM;
+	dp->dev = dev;
+
+	for (i = 0; i < MAX_PHY; i++) {
+		dp->extcon[i] = extcon_get_edev_by_phandle(dev, i);
+		dp->phy[i] = devm_of_phy_get_by_index(dev, dev->of_node, i);
+
+		if (!IS_ERR(dp->extcon[i]) && !IS_ERR(dp->phy[i]))
+			count++;
+
+		if (PTR_ERR(dp->extcon[i]) == -EPROBE_DEFER ||
+		    PTR_ERR(dp->phy[i]) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+	}
+
+	if (!count) {
+		dev_err(dev, "missing extcon or phy\n");
+		return -EINVAL;
+	}
+
+	dev_set_drvdata(dev, dp);
+
+	return component_add(dev, &cdn_dp_component_ops);
+}
+
+static int cdn_dp_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &cdn_dp_component_ops);
+
+	return 0;
+}
+
+static const struct of_device_id cdn_dp_dt_ids[] = {
+	{ .compatible = "rockchip,rk3399-cdn-dp" },
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, cdn_dp_dt_ids);
+
+static struct platform_driver cdn_dp_driver = {
+	.probe = cdn_dp_probe,
+	.remove = cdn_dp_remove,
+	.driver = {
+		   .name = "cdn-dp",
+		   .owner = THIS_MODULE,
+		   .of_match_table = of_match_ptr(cdn_dp_dt_ids),
+	},
+};
+
+module_platform_driver(cdn_dp_driver);
+
+MODULE_AUTHOR("Chris Zhong <zyw at rock-chips.com>");
+MODULE_DESCRIPTION("cdn DP Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
new file mode 100644
index 0000000..31ba22d
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2016 Chris Zhong <zyw at rock-chips.com>
+ * Copyright (C) 2016 ROCKCHIP, Inc.
+ *
+ * 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.
+ */
+
+#ifndef _ROCKCHIP_EDP_CORE_H
+#define _ROCKCHIP_EDP_CORE_H
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_panel.h>
+#include "rockchip_drm_drv.h"
+#include "cdn-dp-reg.h"
+
+#define MAX_PHY		2
+
+enum AUDIO_FORMAT {
+	AFMT_I2S = 0,
+	AFMT_SPDIF = 1,
+	AFMT_UNUSED,
+};
+
+struct audio_info {
+	enum AUDIO_FORMAT format;
+	int sample_rate;
+	int channels;
+	int sample_width;
+};
+
+struct video_info {
+	bool h_sync_polarity;
+	bool v_sync_polarity;
+	bool interlaced;
+	int color_depth;
+	enum VIC_PXL_ENCODING_FORMAT color_fmt;
+};
+
+struct cdn_firmware_header {
+	u32 size_bytes; /* size of the entire header+image(s) in bytes */
+	u32 header_size; /* size of just the header in bytes */
+	u32 iram_size; /* size of iram */
+	u32 dram_size; /* size of dram */
+};
+
+struct cdn_dp_device {
+	struct device *dev;
+	struct drm_device *drm_dev;
+	struct drm_connector connector;
+	struct drm_encoder encoder;
+	struct drm_display_mode mode;
+	struct platform_device *audio_pdev;
+
+	const struct firmware *fw;	/* cdn dp firmware */
+	unsigned int fw_version;	/* cdn fw version */
+	u32 fw_retry;
+	bool fw_loaded;
+	void __iomem *regs;
+	struct regmap *grf;
+	unsigned int irq;
+	struct clk *core_clk;
+	struct clk *pclk;
+	struct clk *spdif_clk;
+	struct reset_control *spdif_rst;
+	struct audio_info audio_info;
+	struct video_info video_info;
+	struct extcon_dev *extcon[MAX_PHY];
+	struct phy *phy[MAX_PHY];
+	struct notifier_block event_nb;
+	struct delayed_work event_wq;
+
+	u8 port_id;
+	u8 cap_lanes;
+	bool hpd_status;
+	bool phy_status[MAX_PHY];
+
+	int dpms_mode;
+	struct drm_dp_link link;
+	bool sink_has_audio;
+};
+
+void dp_clock_reset_seq(struct cdn_dp_device *dp);
+
+void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, int clk);
+int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
+			 u32 i_size, const u32 *d_mem, u32 d_size);
+int cdn_dp_active(struct cdn_dp_device *dp, bool enable);
+int cdn_dp_set_host_cap(struct cdn_dp_device *dp);
+int cdn_dp_event_config(struct cdn_dp_device *dp);
+int cdn_dp_get_event(struct cdn_dp_device *dp);
+int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
+int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value);
+int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr);
+int cdn_dp_get_edid_block(void *dp, u8 *edid,
+			  unsigned int block, size_t length);
+int cdn_dp_training_start(struct cdn_dp_device *dp);
+int cdn_dp_get_lt_status(struct cdn_dp_device *dp);
+int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active);
+int cdn_dp_config_video(struct cdn_dp_device *dp);
+int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio);
+int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable);
+int cdn_dp_audio_config_set(struct cdn_dp_device *dp, struct audio_info *audio);
+
+#endif  /* _ROCKCHIP_EDP_CORE_H */
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
new file mode 100644
index 0000000..8d5becd
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
@@ -0,0 +1,740 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Chris Zhong <zyw at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/clk.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/reset.h>
+
+#include "cdn-dp-core.h"
+#include "cdn-dp-reg.h"
+
+#define CDN_DP_SPDIF_CLK		200000000
+#define FW_ALIVE_TIMEOUT_US		1000000
+#define MAILBOX_TIMEOUT_US		5000000
+
+void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, int clk)
+{
+	writel(clk / 1000000, dp->regs + SW_CLK_H);
+}
+
+void dp_clock_reset_seq(struct cdn_dp_device *dp)
+{
+	writel(0xfff, dp->regs + SOURCE_DPTX_CAR);
+	writel(0x7, dp->regs + SOURCE_PHY_CAR);
+	writel(0xf, dp->regs + SOURCE_PKT_CAR);
+	writel(0xff, dp->regs + SOURCE_AIF_CAR);
+	writel(0xf, dp->regs + SOURCE_CIPHER_CAR);
+	writel(0x3, dp->regs + SOURCE_CRYPTO_CAR);
+	writel(0, dp->regs + APB_INT_MASK);
+}
+
+static int cdn_dp_mailbox_read(struct cdn_dp_device *dp)
+{
+	int val, ret;
+
+	if (!dp->fw_loaded)
+		return 0;
+
+	ret = readx_poll_timeout(readl, dp->regs + MAILBOX_EMPTY_ADDR,
+				 val, !val, 1000, MAILBOX_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(dp->dev, "failed to read mailbox, keep alive = %x\n",
+			readl(dp->regs + KEEP_ALIVE));
+		return ret;
+	}
+
+	return readl(dp->regs + MAILBOX0_RD_DATA) & 0xff;
+}
+
+static int cdp_dp_mailbox_write(struct cdn_dp_device *dp, u8 val)
+{
+	int ret, full;
+
+	if (!dp->fw_loaded)
+		return 0;
+
+	ret = readx_poll_timeout(readl, dp->regs + MAILBOX_FULL_ADDR,
+				 full, !full, 1000, MAILBOX_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(dp->dev, "mailbox is full, keep alive = %x\n",
+			readl(dp->regs + KEEP_ALIVE));
+		return ret;
+	}
+
+	writel(val, dp->regs + MAILBOX0_WR_DATA);
+
+	return 0;
+}
+
+static int cdn_dp_mailbox_response(struct cdn_dp_device *dp, u8 module_id,
+				   u8 opcode, u8 *buff, u8 buff_size)
+{
+	int ret, i = 0;
+	u8 header[4];
+
+	/* read the header of the message */
+	while (i < 4) {
+		ret = cdn_dp_mailbox_read(dp);
+		if (ret < 0)
+			return ret;
+
+		header[i++] = ret;
+	}
+
+	if (opcode != header[0] || module_id != header[1] ||
+	    buff_size != ((header[2] << 8) | header[3])) {
+		dev_err(dp->dev, "mailbox response failed");
+
+		/*
+		 * If the message in mailbox is not what we want, we need to
+		 * clear the mailbox by read.
+		 */
+		i = (header[2] << 8) | header[3];
+		while (i--)
+			if (cdn_dp_mailbox_read(dp) < 0)
+				break;
+
+		return -EINVAL;
+	}
+
+	i = 0;
+	while (i < buff_size) {
+		ret = cdn_dp_mailbox_read(dp);
+		if (ret < 0)
+			return ret;
+
+		buff[i++] = ret;
+	}
+
+	return 0;
+}
+
+static int cdn_dp_mailbox_send(struct cdn_dp_device *dp, u8 module_id,
+			       u8 opcode, u16 size, u8 *message)
+{
+	u8 header[4];
+	int ret, i;
+
+	header[0] = opcode;
+	header[1] = module_id;
+	header[2] = size >> 8;
+	header[3] = size & 0xff;
+
+	for (i = 0; i < 4; i++) {
+		ret = cdp_dp_mailbox_write(dp, header[i]);
+		if (ret)
+			return ret;
+	}
+
+	while (size--) {
+		ret = cdp_dp_mailbox_write(dp, *message++);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
+{
+	u8 msg[6];
+
+	msg[0] = (addr >> 8) & 0xff;
+	msg[1] = addr & 0xff;
+	msg[2] = (val >> 24) & 0xff;
+	msg[3] = (val >> 16) & 0xff;
+	msg[4] = (val >> 8) & 0xff;
+	msg[5] = val & 0xff;
+	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_REGISTER,
+				   ARRAY_SIZE(msg), msg);
+}
+
+int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
+			 u32 i_size, const u32 *d_mem, u32 d_size)
+{
+	int i, reg, ret;
+
+	/* reset ucpu before load firmware*/
+	writel(APB_IRAM_PATH | APB_DRAM_PATH | APB_XT_RESET,
+	       dp->regs + APB_CTRL);
+
+	for (i = 0; i < i_size; i += 4)
+		writel(*i_mem++, dp->regs + ADDR_IMEM + i);
+
+	for (i = 0; i < d_size; i += 4)
+		writel(*d_mem++, dp->regs + ADDR_DMEM + i);
+
+	/* un-reset ucpu */
+	writel(0, dp->regs + APB_CTRL);
+
+	/* check the keep alive register to make sure fw working */
+	ret = readx_poll_timeout(readl, dp->regs + KEEP_ALIVE,
+				 reg, reg, 2000, FW_ALIVE_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(dp->dev, "failed to loaded the FW reg = %x\n", reg);
+		return -EINVAL;
+	}
+
+	reg = readl(dp->regs + VER_L) & 0xff;
+	dp->fw_version = reg;
+	reg = readl(dp->regs + VER_H) & 0xff;
+	dp->fw_version |= reg << 8;
+	reg = readl(dp->regs + VER_LIB_L_ADDR) & 0xff;
+	dp->fw_version |= reg << 16;
+	reg = readl(dp->regs + VER_LIB_H_ADDR) & 0xff;
+	dp->fw_version |= reg << 24;
+
+	dp->fw_loaded = 1;
+
+	return 0;
+}
+
+int cdn_dp_reg_write_bit(struct cdn_dp_device *dp, u16 addr, u8 start_bit,
+			 u8 bits_no, u32 val)
+{
+	u8 field[8];
+
+	field[0] = (addr >> 8) & 0xff;
+	field[1] = addr & 0xff;
+	field[2] = start_bit;
+	field[3] = bits_no;
+	field[4] = (val >> 24) & 0xff;
+	field[5] = (val >> 16) & 0xff;
+	field[6] = (val >> 8) & 0xff;
+	field[7] = val & 0xff;
+
+	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_FIELD,
+			    sizeof(field), field);
+}
+
+int cdn_dp_active(struct cdn_dp_device *dp, bool enable)
+{
+	u8 active = enable ? 1 : 0;
+	u8 resp;
+	int ret;
+
+	/* set firmware status, 1: avtive; 0: standby */
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_GENERAL,
+				  GENERAL_MAIN_CONTROL, 1, &active);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_response(dp, MB_MODULE_ID_GENERAL,
+				      GENERAL_MAIN_CONTROL, &resp, 1);
+	if (ret)
+		return ret;
+
+	return resp ? 0 : -EINVAL;
+}
+
+int cdn_dp_set_host_cap(struct cdn_dp_device *dp)
+{
+	u8 msg[8];
+	int ret;
+
+	msg[0] = DP_LINK_BW_5_4;
+	msg[1] = dp->cap_lanes;
+	msg[2] = VOLTAGE_LEVEL_2;
+	msg[3] = PRE_EMPHASIS_LEVEL_3;
+	msg[4] = PRBS7 | D10_2 | TRAINING_PTN1 | TRAINING_PTN2;
+	msg[5] = FAST_LT_NOT_SUPPORT;
+	msg[6] = LANE_MAPPING_NORMAL;
+	msg[7] = ENHANCED;
+
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
+				  DPTX_SET_HOST_CAPABILITIES,
+				  ARRAY_SIZE(msg), msg);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_reg_write(dp, DP_AUX_SWAP_INVERSION_CONTROL,
+			       AUX_HOST_INVERT);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+int cdn_dp_event_config(struct cdn_dp_device *dp)
+{
+	u8 msg[5] = {0, 0, 0, 0, 0};
+
+	msg[0] = DPTX_EVENT_ENABLE_HPD | DPTX_EVENT_ENABLE_TRAINING;
+
+	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_ENABLE_EVENT,
+				    ARRAY_SIZE(msg), msg);
+}
+
+int cdn_dp_get_event(struct cdn_dp_device *dp)
+{
+	return readl(dp->regs + SW_EVENTS0);
+}
+
+int cdn_dp_get_hpd_status(struct cdn_dp_device *dp)
+{
+	u8 status;
+	int ret;
+
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_HPD_STATE,
+				  0, NULL);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_response(dp, MB_MODULE_ID_DP_TX,
+				      DPTX_HPD_STATE, &status, 1);
+	if (ret)
+		return ret;
+
+	return status;
+}
+
+int cdn_dp_get_edid_block(void *data, u8 *edid,
+			  unsigned int block, size_t length)
+{
+	struct cdn_dp_device *dp = data;
+	u8 msg[2], reg[EDID_DATA + EDID_BLOCK_SIZE];
+	int ret;
+
+	if (length != EDID_BLOCK_SIZE)
+		return -EINVAL;
+
+	msg[0] = block / 2;
+	msg[1] = block % 2;
+
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_GET_EDID,
+				  2, msg);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_response(dp, MB_MODULE_ID_DP_TX,
+				      DPTX_GET_EDID, reg,
+				      EDID_DATA + EDID_BLOCK_SIZE);
+	if (ret)
+		return ret;
+
+	if (reg[EDID_LENGTH_BYTE] != EDID_BLOCK_SIZE ||
+	    reg[EDID_SEGMENT_BUMBER] != block / 2) {
+		dev_err(dp->dev, "edid block size err\n");
+		return -EINVAL;
+	}
+
+	memcpy(edid, &reg[EDID_DATA], EDID_BLOCK_SIZE);
+
+	return 0;
+}
+
+int cdn_dp_training_start(struct cdn_dp_device *dp)
+{
+	u8 msg, event[2];
+	unsigned long timeout;
+	int ret;
+
+	msg = LINK_TRAINING_RUN;
+
+	/* start training */
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_TRAINING_CONTROL,
+				  1, &msg);
+	if (ret)
+		return ret;
+
+	/* the whole training should finish in 500ms */
+	timeout = jiffies + msecs_to_jiffies(500);
+	while (1) {
+		msleep(20);
+		ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
+					  DPTX_READ_EVENT, 0, NULL);
+		if (ret)
+			return ret;
+
+		ret = cdn_dp_mailbox_response(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_READ_EVENT, event, 2);
+		if (ret)
+			return ret;
+
+		if (event[1] & EQ_PHASE_FINISHED)
+			break;
+
+		if (time_after(jiffies, timeout))
+			return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+int cdn_dp_get_lt_status(struct cdn_dp_device *dp)
+{
+	u8 status[10];
+	int ret;
+
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_READ_LINK_STAT,
+				  0, NULL);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_response(dp, MB_MODULE_ID_DP_TX,
+				      DPTX_READ_LINK_STAT, status, 10);
+	if (ret)
+		return ret;
+
+	dp->link.rate = status[0];
+	dp->link.num_lanes = status[1];
+
+	return 0;
+}
+
+int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active)
+{
+	u8 msg;
+
+	msg = !!active;
+
+	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_SET_VIDEO,
+				   1, &msg);
+}
+
+static int cdn_dp_get_msa_misc(struct video_info *video,
+			       struct drm_display_mode *mode)
+{
+	u8 val0, val1;
+	u32 msa_misc;
+
+	switch (video->color_fmt) {
+	case PXL_RGB:
+	case Y_ONLY:
+		val0 = 0;
+		break;
+	case YCBCR_4_4_4:
+		/* set default color space conversion to BT601 */
+		val0 = 6 + BT_601 * 8;
+		break;
+	case YCBCR_4_2_2:
+		/* set default color space conversion to BT601 */
+		val0 = 5 + BT_601 * 8;
+		break;
+	case YCBCR_4_2_0:
+		val0 = 5;
+		break;
+	};
+
+	switch (video->color_depth) {
+	case 6:
+		val1 = 0;
+		break;
+	case 8:
+		val1 = 1;
+		break;
+	case 10:
+		val1 = 2;
+		break;
+	case 12:
+		val1 = 3;
+		break;
+	case 16:
+		val1 = 4;
+		break;
+	};
+
+	msa_misc = 2 * val0 + 32 * val1 +
+		   ((video->color_fmt == Y_ONLY) ? (1 << 14) : 0);
+
+	return msa_misc;
+}
+
+int cdn_dp_config_video(struct cdn_dp_device *dp)
+{
+	struct video_info *video = &dp->video_info;
+	struct drm_display_mode *mode = &dp->mode;
+	u64 symbol;
+	u32 val, link_rate;
+	u8 bit_per_pix;
+	u8 tu_size_reg = TU_SIZE;
+	int ret;
+
+	bit_per_pix = (video->color_fmt == YCBCR_4_2_2) ?
+		      (video->color_depth * 2) : (video->color_depth * 3);
+
+	link_rate = drm_dp_bw_code_to_link_rate(dp->link.rate);
+
+	val = VIF_BYPASS_INTERLACE;
+
+	ret = cdn_dp_reg_write(dp, BND_HSYNC2VSYNC, val);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_reg_write(dp, HSYNC2VSYNC_POL_CTRL, 0);
+	if (ret)
+		return ret;
+
+	/* get a best tu_size and symbol */
+	do {
+		tu_size_reg += 2;
+		symbol = tu_size_reg * mode->clock * bit_per_pix;
+		symbol /= dp->link.num_lanes * link_rate * 8;
+	} while ((symbol == 1) || (tu_size_reg - symbol < 4));
+
+	val = symbol + (tu_size_reg << 8);
+
+	ret = cdn_dp_reg_write(dp, DP_FRAMER_TU, val);
+	if (ret)
+		return ret;
+
+	switch (video->color_depth) {
+	case 6:
+		val = BCS_6;
+		break;
+	case 8:
+		val = BCS_8;
+		break;
+	case 10:
+		val = BCS_10;
+		break;
+	case 12:
+		val = BCS_12;
+		break;
+	case 16:
+		val = BCS_16;
+		break;
+	};
+
+	val += video->color_fmt << 8;
+	ret = cdn_dp_reg_write(dp, DP_FRAMER_PXL_REPR, val);
+	if (ret)
+		return ret;
+
+	val = video->h_sync_polarity ? DP_FRAMER_SP_HSP : 0;
+	val |= video->v_sync_polarity ? DP_FRAMER_SP_VSP : 0;
+	ret = cdn_dp_reg_write(dp, DP_FRAMER_SP, val);
+	if (ret)
+		return ret;
+
+	val = (mode->hsync_start - mode->hdisplay) << 16;
+	val |= mode->htotal - mode->hsync_end;
+	ret = cdn_dp_reg_write(dp, DP_FRONT_BACK_PORCH, val);
+	if (ret)
+		return ret;
+
+	val = mode->hdisplay * bit_per_pix / 8;
+	ret = cdn_dp_reg_write(dp, DP_BYTE_COUNT, val);
+	if (ret)
+		return ret;
+
+	val = mode->htotal | ((mode->htotal - mode->hsync_start) << 16);
+	ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_0, val);
+	if (ret)
+		return ret;
+
+	val = mode->hsync_end - mode->hsync_start;
+	val |= (mode->hdisplay << 16) | (video->h_sync_polarity << 15);
+	ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_1, val);
+	if (ret)
+		return ret;
+
+	val = mode->vtotal;
+	val |= ((mode->vtotal - mode->vsync_start) << 16);
+
+	ret = cdn_dp_reg_write(dp, MSA_VERTICAL_0, val);
+	if (ret)
+		return ret;
+
+	val = mode->vsync_end - mode->vsync_start;
+	val |= mode->vdisplay << 16;
+	val |= (video->v_sync_polarity << 15);
+	ret = cdn_dp_reg_write(dp, MSA_VERTICAL_1, val);
+	if (ret)
+		return ret;
+
+	val = cdn_dp_get_msa_misc(video, mode);
+	ret = cdn_dp_reg_write(dp, MSA_MISC, val);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_reg_write(dp, STREAM_CONFIG, 1);
+	if (ret)
+		return ret;
+
+	val = mode->hsync_end - mode->hsync_start;
+	val |= (mode->hdisplay << 16);
+	ret = cdn_dp_reg_write(dp, DP_HORIZONTAL, val);
+	if (ret)
+		return ret;
+
+	val = mode->vtotal;
+	val -= (mode->vtotal - mode->vdisplay);
+	val |= (mode->vtotal - mode->vsync_start) << 16;
+
+	ret = cdn_dp_reg_write(dp, DP_VERTICAL_0, val);
+	if (ret)
+		return ret;
+
+	val = mode->vtotal;
+	ret = cdn_dp_reg_write(dp, DP_VERTICAL_1, val);
+	if (ret)
+		return ret;
+
+	val =  0;
+	return cdn_dp_reg_write_bit(dp, DP_VB_ID, 2, 1, val);
+}
+
+int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio)
+{
+	int ret = cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, AUDIO_PACK_EN(0));
+
+	if (ret)
+		return ret;
+	writel(0x1F0707, dp->regs + SPDIF_CTRL_ADDR);
+	writel(0, dp->regs + AUDIO_SRC_CNTL);
+	writel(0, dp->regs + AUDIO_SRC_CNFG);
+	writel(1, dp->regs + AUDIO_SRC_CNTL);
+	writel(0, dp->regs + AUDIO_SRC_CNTL);
+
+	writel(0, dp->regs + SMPL2PKT_CNTL);
+	writel(1, dp->regs + SMPL2PKT_CNTL);
+	writel(0, dp->regs + SMPL2PKT_CNTL);
+
+	writel(1, dp->regs + FIFO_CNTL);
+	writel(0, dp->regs + FIFO_CNTL);
+
+	if (audio->format == AFMT_SPDIF)
+		clk_disable_unprepare(dp->spdif_clk);
+
+	return 0;
+}
+
+int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable)
+{
+	return cdn_dp_reg_write_bit(dp, DP_VB_ID, 4, 1, enable);
+}
+
+int cdn_dp_audio_config_set(struct cdn_dp_device *dp, struct audio_info *audio)
+{
+	int lanes_param, i2s_port_en_val, val, i;
+	int ret;
+
+	if (audio->channels == 2 && dp->link.num_lanes == 1)
+		lanes_param = 1;
+	else if (audio->channels == 2)
+		lanes_param = 3;
+	else
+		lanes_param = 0;
+
+	if (audio->channels == 2)
+		i2s_port_en_val = 1;
+	else if (audio->channels == 4)
+		i2s_port_en_val = 3;
+	else
+		i2s_port_en_val = 0xf;
+
+	if (audio->format == AFMT_SPDIF) {
+		reset_control_assert(dp->spdif_rst);
+		reset_control_deassert(dp->spdif_rst);
+	}
+
+	ret = cdn_dp_reg_write(dp, CM_LANE_CTRL, 0x8000);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_reg_write(dp, CM_CTRL, 0);
+	if (ret)
+		return ret;
+
+	if (audio->format == AFMT_I2S) {
+		writel(0x0, dp->regs + SPDIF_CTRL_ADDR);
+
+		writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL);
+
+		val = audio->channels - 1;
+		val |= (audio->channels / 2 - 1) << 5;
+		val |= BIT(8);
+		val |= lanes_param << 11;
+		writel(val, dp->regs + SMPL2PKT_CNFG);
+
+		if (audio->sample_width == 16)
+			val = 0;
+		else if (audio->sample_width == 24)
+			val = 1 << 9;
+		else
+			val = 2 << 9;
+
+		val |= (audio->channels - 1) << 2;
+		val |= i2s_port_en_val << 17;
+		val |= 2 << 11;
+		writel(val, dp->regs + AUDIO_SRC_CNFG);
+
+		for (i = 0; i < (audio->channels + 1) / 2; i++) {
+			if (audio->sample_width == 16)
+				val = (0x08 << 8) | (0x08 << 20);
+			else if (audio->sample_width == 24)
+				val = (0x0b << 8) | (0x0b << 20);
+
+			val |= ((2 * i) << 4) | ((2 * i + 1) << 16);
+			writel(val, dp->regs + STTS_BIT_CH(i));
+		}
+
+		switch (audio->sample_rate) {
+		case 32000:
+			val = SAMPLING_FREQ(3) |
+			      ORIGINAL_SAMP_FREQ(0xc);
+			break;
+		case 44100:
+			val = SAMPLING_FREQ(0) |
+			      ORIGINAL_SAMP_FREQ(0xf);
+			break;
+		case 48000:
+			val = SAMPLING_FREQ(2) |
+			      ORIGINAL_SAMP_FREQ(0xd);
+			break;
+		case 88200:
+			val = SAMPLING_FREQ(8) |
+			      ORIGINAL_SAMP_FREQ(0x7);
+			break;
+		case 96000:
+			val = SAMPLING_FREQ(0xa) |
+			      ORIGINAL_SAMP_FREQ(5);
+			break;
+		case 176400:
+			val = SAMPLING_FREQ(0xc) |
+			      ORIGINAL_SAMP_FREQ(3);
+			break;
+		case 192000:
+			val = SAMPLING_FREQ(0xe) |
+			      ORIGINAL_SAMP_FREQ(1);
+			break;
+		}
+		val |= 4;
+		writel(val, dp->regs + COM_CH_STTS_BITS);
+
+		writel(2, dp->regs + SMPL2PKT_CNTL);
+		writel(2, dp->regs + AUDIO_SRC_CNTL);
+	} else {
+		val = 0x1F0707;
+		writel(val, dp->regs + SPDIF_CTRL_ADDR);
+
+		writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL);
+
+		val = 0x101 | (3 << 11);
+		writel(val, dp->regs + SMPL2PKT_CNFG);
+		writel(2, dp->regs + SMPL2PKT_CNTL);
+
+		val = 0x3F0707;
+		writel(val, dp->regs + SPDIF_CTRL_ADDR);
+
+		clk_prepare_enable(dp->spdif_clk);
+		clk_set_rate(dp->spdif_clk, CDN_DP_SPDIF_CLK);
+	}
+
+	return cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, AUDIO_PACK_EN(1));
+}
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
new file mode 100644
index 0000000..b33793e
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Chris Zhong <zyw at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _CDN_DP_REG_H
+#define _CDN_DP_REG_H
+
+#include <linux/bitops.h>
+
+#define ADDR_IMEM		0x10000
+#define ADDR_DMEM		0x20000
+
+/* APB CFG addr */
+#define APB_CTRL			0
+#define XT_INT_CTRL			0x04
+#define MAILBOX_FULL_ADDR		0x08
+#define MAILBOX_EMPTY_ADDR		0x0c
+#define MAILBOX0_WR_DATA		0x10
+#define MAILBOX0_RD_DATA		0x14
+#define KEEP_ALIVE			0x18
+#define VER_L				0x1c
+#define VER_H				0x20
+#define VER_LIB_L_ADDR			0x24
+#define VER_LIB_H_ADDR			0x28
+#define SW_DEBUG_L			0x2c
+#define SW_DEBUG_H			0x30
+#define MAILBOX_INT_MASK		0x34
+#define MAILBOX_INT_STATUS		0x38
+#define SW_CLK_L			0x3c
+#define SW_CLK_H			0x40
+#define SW_EVENTS0			0x44
+#define SW_EVENTS1			0x48
+#define SW_EVENTS2			0x4c
+#define SW_EVENTS3			0x50
+#define XT_OCD_CTRL			0x60
+#define APB_INT_MASK			0x6c
+#define APB_STATUS_MASK			0x70
+
+/* audio decoder addr */
+#define AUDIO_SRC_CNTL			0x30000
+#define AUDIO_SRC_CNFG			0x30004
+#define COM_CH_STTS_BITS		0x30008
+#define STTS_BIT_CH(x)			(0x3000c + ((x) << 2))
+#define SPDIF_CTRL_ADDR			0x3004c
+#define SPDIF_CH1_CS_3100_ADDR		0x30050
+#define SPDIF_CH1_CS_6332_ADDR		0x30054
+#define SPDIF_CH1_CS_9564_ADDR		0x30058
+#define SPDIF_CH1_CS_12796_ADDR		0x3005c
+#define SPDIF_CH1_CS_159128_ADDR	0x30060
+#define SPDIF_CH1_CS_191160_ADDR	0x30064
+#define SPDIF_CH2_CS_3100_ADDR		0x30068
+#define SPDIF_CH2_CS_6332_ADDR		0x3006c
+#define SPDIF_CH2_CS_9564_ADDR		0x30070
+#define SPDIF_CH2_CS_12796_ADDR		0x30074
+#define SPDIF_CH2_CS_159128_ADDR	0x30078
+#define SPDIF_CH2_CS_191160_ADDR	0x3007c
+#define SMPL2PKT_CNTL			0x30080
+#define SMPL2PKT_CNFG			0x30084
+#define FIFO_CNTL			0x30088
+#define FIFO_STTS			0x3008c
+
+/* source pif addr */
+#define SOURCE_PIF_WR_ADDR		0x30800
+#define SOURCE_PIF_WR_REQ		0x30804
+#define SOURCE_PIF_RD_ADDR		0x30808
+#define SOURCE_PIF_RD_REQ		0x3080c
+#define SOURCE_PIF_DATA_WR		0x30810
+#define SOURCE_PIF_DATA_RD		0x30814
+#define SOURCE_PIF_FIFO1_FLUSH		0x30818
+#define SOURCE_PIF_FIFO2_FLUSH		0x3081c
+#define SOURCE_PIF_STATUS		0x30820
+#define SOURCE_PIF_INTERRUPT_SOURCE	0x30824
+#define SOURCE_PIF_INTERRUPT_MASK	0x30828
+#define SOURCE_PIF_PKT_ALLOC_REG	0x3082c
+#define SOURCE_PIF_PKT_ALLOC_WR_EN	0x30830
+#define SOURCE_PIF_SW_RESET		0x30834
+
+/* bellow registers need access by mailbox */
+/* source car addr */
+#define SOURCE_HDTX_CAR			0x0900
+#define SOURCE_DPTX_CAR			0x0904
+#define SOURCE_PHY_CAR			0x0908
+#define SOURCE_CEC_CAR			0x090c
+#define SOURCE_CBUS_CAR			0x0910
+#define SOURCE_PKT_CAR			0x0918
+#define SOURCE_AIF_CAR			0x091c
+#define SOURCE_CIPHER_CAR		0x0920
+#define SOURCE_CRYPTO_CAR		0x0924
+
+/* clock meters addr */
+#define CM_CTRL				0x0a00
+#define CM_I2S_CTRL			0x0a04
+#define CM_SPDIF_CTRL			0x0a08
+#define CM_VID_CTRL			0x0a0c
+#define CM_LANE_CTRL			0x0a10
+#define I2S_NM_STABLE			0x0a14
+#define I2S_NCTS_STABLE			0x0a18
+#define SPDIF_NM_STABLE			0x0a1c
+#define SPDIF_NCTS_STABLE		0x0a20
+#define NMVID_MEAS_STABLE		0x0a24
+#define I2S_MEAS			0x0a40
+#define SPDIF_MEAS			0x0a80
+#define NMVID_MEAS			0x0ac0
+
+/* source vif addr */
+#define BND_HSYNC2VSYNC			0x0b00
+#define HSYNC2VSYNC_F1_L1		0x0b04
+#define HSYNC2VSYNC_F2_L1		0x0b08
+#define HSYNC2VSYNC_STATUS		0x0b0c
+#define HSYNC2VSYNC_POL_CTRL		0x0b10
+
+/* dptx phy addr */
+#define DP_TX_PHY_CONFIG_REG		0x2000
+#define DP_TX_PHY_STATUS_REG		0x2004
+#define DP_TX_PHY_SW_RESET		0x2008
+#define DP_TX_PHY_SCRAMBLER_SEED	0x200c
+#define DP_TX_PHY_TRAINING_01_04	0x2010
+#define DP_TX_PHY_TRAINING_05_08	0x2014
+#define DP_TX_PHY_TRAINING_09_10	0x2018
+#define TEST_COR			0x23fc
+
+/* dptx hpd addr */
+#define HPD_IRQ_DET_MIN_TIMER		0x2100
+#define HPD_IRQ_DET_MAX_TIMER		0x2104
+#define HPD_UNPLGED_DET_MIN_TIMER	0x2108
+#define HPD_STABLE_TIMER		0x210c
+#define HPD_FILTER_TIMER		0x2110
+#define HPD_EVENT_MASK			0x211c
+#define HPD_EVENT_DET			0x2120
+
+/* dpyx framer addr */
+#define DP_FRAMER_GLOBAL_CONFIG		0x2200
+#define DP_SW_RESET			0x2204
+#define DP_FRAMER_TU			0x2208
+#define DP_FRAMER_PXL_REPR		0x220c
+#define DP_FRAMER_SP			0x2210
+#define AUDIO_PACK_CONTROL		0x2214
+#define DP_VC_TABLE(x)			(0x2218 + ((x) << 2))
+#define DP_VB_ID			0x2258
+#define DP_MTPH_LVP_CONTROL		0x225c
+#define DP_MTPH_SYMBOL_VALUES		0x2260
+#define DP_MTPH_ECF_CONTROL		0x2264
+#define DP_MTPH_ACT_CONTROL		0x2268
+#define DP_MTPH_STATUS			0x226c
+#define DP_INTERRUPT_SOURCE		0x2270
+#define DP_INTERRUPT_MASK		0x2274
+#define DP_FRONT_BACK_PORCH		0x2278
+#define DP_BYTE_COUNT			0x227c
+
+/* dptx stream addr */
+#define MSA_HORIZONTAL_0		0x2280
+#define MSA_HORIZONTAL_1		0x2284
+#define MSA_VERTICAL_0			0x2288
+#define MSA_VERTICAL_1			0x228c
+#define MSA_MISC			0x2290
+#define STREAM_CONFIG			0x2294
+#define AUDIO_PACK_STATUS		0x2298
+#define VIF_STATUS			0x229c
+#define PCK_STUFF_STATUS_0		0x22a0
+#define PCK_STUFF_STATUS_1		0x22a4
+#define INFO_PACK_STATUS		0x22a8
+#define RATE_GOVERNOR_STATUS		0x22ac
+#define DP_HORIZONTAL			0x22b0
+#define DP_VERTICAL_0			0x22b4
+#define DP_VERTICAL_1			0x22b8
+#define DP_BLOCK_SDP			0x22bc
+
+/* dptx glbl addr */
+#define DPTX_LANE_EN			0x2300
+#define DPTX_ENHNCD			0x2304
+#define DPTX_INT_MASK			0x2308
+#define DPTX_INT_STATUS			0x230c
+
+/* dp aux addr */
+#define DP_AUX_HOST_CONTROL		0x2800
+#define DP_AUX_INTERRUPT_SOURCE		0x2804
+#define DP_AUX_INTERRUPT_MASK		0x2808
+#define DP_AUX_SWAP_INVERSION_CONTROL	0x280c
+#define DP_AUX_SEND_NACK_TRANSACTION	0x2810
+#define DP_AUX_CLEAR_RX			0x2814
+#define DP_AUX_CLEAR_TX			0x2818
+#define DP_AUX_TIMER_STOP		0x281c
+#define DP_AUX_TIMER_CLEAR		0x2820
+#define DP_AUX_RESET_SW			0x2824
+#define DP_AUX_DIVIDE_2M		0x2828
+#define DP_AUX_TX_PREACHARGE_LENGTH	0x282c
+#define DP_AUX_FREQUENCY_1M_MAX		0x2830
+#define DP_AUX_FREQUENCY_1M_MIN		0x2834
+#define DP_AUX_RX_PRE_MIN		0x2838
+#define DP_AUX_RX_PRE_MAX		0x283c
+#define DP_AUX_TIMER_PRESET		0x2840
+#define DP_AUX_NACK_FORMAT		0x2844
+#define DP_AUX_TX_DATA			0x2848
+#define DP_AUX_RX_DATA			0x284c
+#define DP_AUX_TX_STATUS		0x2850
+#define DP_AUX_RX_STATUS		0x2854
+#define DP_AUX_RX_CYCLE_COUNTER		0x2858
+#define DP_AUX_MAIN_STATES		0x285c
+#define DP_AUX_MAIN_TIMER		0x2860
+#define DP_AUX_AFE_OUT			0x2864
+
+/* crypto addr */
+#define CRYPTO_HDCP_REVISION		0x5800
+#define HDCP_CRYPTO_CONFIG		0x5804
+#define CRYPTO_INTERRUPT_SOURCE		0x5808
+#define CRYPTO_INTERRUPT_MASK		0x580c
+#define CRYPTO22_CONFIG			0x5818
+#define CRYPTO22_STATUS			0x581c
+#define SHA_256_DATA_IN			0x583c
+#define SHA_256_DATA_OUT_(x)		(0x5850 + ((x) << 2))
+#define AES_32_KEY_(x)			(0x5870 + ((x) << 2))
+#define AES_32_DATA_IN			0x5880
+#define AES_32_DATA_OUT_(x)		(0x5884 + ((x) << 2))
+#define CRYPTO14_CONFIG			0x58a0
+#define CRYPTO14_STATUS			0x58a4
+#define CRYPTO14_PRNM_OUT		0x58a8
+#define CRYPTO14_KM_0			0x58ac
+#define CRYPTO14_KM_1			0x58b0
+#define CRYPTO14_AN_0			0x58b4
+#define CRYPTO14_AN_1			0x58b8
+#define CRYPTO14_YOUR_KSV_0		0x58bc
+#define CRYPTO14_YOUR_KSV_1		0x58c0
+#define CRYPTO14_MI_0			0x58c4
+#define CRYPTO14_MI_1			0x58c8
+#define CRYPTO14_TI_0			0x58cc
+#define CRYPTO14_KI_0			0x58d0
+#define CRYPTO14_KI_1			0x58d4
+#define CRYPTO14_BLOCKS_NUM		0x58d8
+#define CRYPTO14_KEY_MEM_DATA_0		0x58dc
+#define CRYPTO14_KEY_MEM_DATA_1		0x58e0
+#define CRYPTO14_SHA1_MSG_DATA		0x58e4
+#define CRYPTO14_SHA1_V_VALUE_(x)	(0x58e8 + ((x) << 2))
+#define TRNG_CTRL			0x58fc
+#define TRNG_DATA_RDY			0x5900
+#define TRNG_DATA			0x5904
+
+/* cipher addr */
+#define HDCP_REVISION			0x60000
+#define INTERRUPT_SOURCE		0x60004
+#define INTERRUPT_MASK			0x60008
+#define HDCP_CIPHER_CONFIG		0x6000c
+#define AES_128_KEY_0			0x60010
+#define AES_128_KEY_1			0x60014
+#define AES_128_KEY_2			0x60018
+#define AES_128_KEY_3			0x6001c
+#define AES_128_RANDOM_0		0x60020
+#define AES_128_RANDOM_1		0x60024
+#define CIPHER14_KM_0			0x60028
+#define CIPHER14_KM_1			0x6002c
+#define CIPHER14_STATUS			0x60030
+#define CIPHER14_RI_PJ_STATUS		0x60034
+#define CIPHER_MODE			0x60038
+#define CIPHER14_AN_0			0x6003c
+#define CIPHER14_AN_1			0x60040
+#define CIPHER22_AUTH			0x60044
+#define CIPHER14_R0_DP_STATUS		0x60048
+#define CIPHER14_BOOTSTRAP		0x6004c
+
+#define APB_IRAM_PATH			BIT(2)
+#define APB_DRAM_PATH			BIT(1)
+#define APB_XT_RESET			BIT(0)
+
+/* mailbox */
+#define MB_OPCODE_ID			0
+#define MB_MODULE_ID			1
+#define MB_SIZE_MSB_ID			2
+#define MB_SIZE_LSB_ID			3
+#define MB_DATA_ID			4
+
+#define MB_MODULE_ID_DP_TX		0x01
+#define MB_MODULE_ID_HDCP_TX		0x07
+#define MB_MODULE_ID_HDCP_RX		0x08
+#define MB_MODULE_ID_HDCP_GENERAL	0x09
+#define MB_MODULE_ID_GENERAL		0x0a
+
+/* general opcode */
+#define GENERAL_MAIN_CONTROL            0x01
+#define GENERAL_TEST_ECHO               0x02
+#define GENERAL_BUS_SETTINGS            0x03
+#define GENERAL_TEST_ACCESS             0x04
+
+#define DPTX_SET_POWER_MNG			0x00
+#define DPTX_SET_HOST_CAPABILITIES		0x01
+#define DPTX_GET_EDID				0x02
+#define DPTX_READ_DPCD				0x03
+#define DPTX_WRITE_DPCD				0x04
+#define DPTX_ENABLE_EVENT			0x05
+#define DPTX_WRITE_REGISTER			0x06
+#define DPTX_READ_REGISTER			0x07
+#define DPTX_WRITE_FIELD			0x08
+#define DPTX_TRAINING_CONTROL			0x09
+#define DPTX_READ_EVENT				0x0a
+#define DPTX_READ_LINK_STAT			0x0b
+#define DPTX_SET_VIDEO				0x0c
+#define DPTX_SET_AUDIO				0x0d
+#define DPTX_GET_LAST_AUX_STAUS			0x0e
+#define DPTX_SET_LINK_BREAK_POINT		0x0f
+#define DPTX_FORCE_LANES			0x10
+#define DPTX_HPD_STATE				0x11
+
+#define DPTX_EVENT_ENABLE_HPD			BIT(0)
+#define DPTX_EVENT_ENABLE_TRAINING		BIT(1)
+
+#define LINK_TRAINING_NOT_ACTIVE		0
+#define LINK_TRAINING_RUN			1
+#define LINK_TRAINING_RESTART			2
+
+#define CONTROL_VIDEO_IDLE			0
+#define CONTROL_VIDEO_VALID			1
+
+#define VIF_BYPASS_INTERLACE			BIT(13)
+#define INTERLACE_FMT_DET			BIT(12)
+#define INTERLACE_DTCT_WIN			0x20
+
+#define DP_FRAMER_SP_INTERLACE_EN		BIT(2)
+#define DP_FRAMER_SP_HSP			BIT(1)
+#define DP_FRAMER_SP_VSP			BIT(0)
+
+/* capability */
+#define AUX_HOST_INVERT				3
+#define	FAST_LT_SUPPORT				1
+#define FAST_LT_NOT_SUPPORT			0
+#define LANE_MAPPING_NORMAL			0xe4
+#define LANE_MAPPING_FLIPPED			0x1b
+#define ENHANCED				1
+
+#define	FULL_LT_STARTED				BIT(0)
+#define FASE_LT_STARTED				BIT(1)
+#define CLK_RECOVERY_FINISHED			BIT(2)
+#define EQ_PHASE_FINISHED			BIT(3)
+#define FASE_LT_START_FINISHED			BIT(4)
+#define CLK_RECOVERY_FAILED			BIT(5)
+#define EQ_PHASE_FAILED				BIT(6)
+#define FASE_LT_FAILED				BIT(7)
+
+#define DPTX_HPD_EVENT				BIT(0)
+#define DPTX_TRAINING_EVENT			BIT(1)
+#define HDCP_TX_STATUS_EVENT			BIT(4)
+#define HDCP2_TX_IS_KM_STORED_EVENT		BIT(5)
+#define HDCP2_TX_STORE_KM_EVENT			BIT(6)
+#define HDCP_TX_IS_RECEIVER_ID_VALID_EVENT	BIT(7)
+
+#define EDID_LENGTH_BYTE			0
+#define EDID_SEGMENT_BUMBER			1
+#define EDID_DATA				2
+#define EDID_BLOCK_SIZE				128
+
+#define TU_SIZE					30
+
+/* audio */
+#define AUDIO_PACK_EN(x)			((x) << 8)
+#define SAMPLING_FREQ(x)			((x) << 16)
+#define ORIGINAL_SAMP_FREQ(x)			((x) << 24)
+#define SYNC_WR_TO_CH_ZERO			BIT(1)
+
+enum voltage_swing_level {
+	VOLTAGE_LEVEL_0,
+	VOLTAGE_LEVEL_1,
+	VOLTAGE_LEVEL_2,
+	VOLTAGE_LEVEL_3,
+};
+
+enum pre_emphasis_level {
+	PRE_EMPHASIS_LEVEL_0,
+	PRE_EMPHASIS_LEVEL_1,
+	PRE_EMPHASIS_LEVEL_2,
+	PRE_EMPHASIS_LEVEL_3,
+};
+
+enum pattern_set {
+	PRBS7		= BIT(0),
+	D10_2		= BIT(1),
+	TRAINING_PTN1	= BIT(2),
+	TRAINING_PTN2	= BIT(3),
+	DP_NONE		= BIT(4)
+};
+
+enum VIC_PXL_ENCODING_FORMAT {
+	PXL_RGB = 0x1,
+	YCBCR_4_4_4 = 0x2,
+	YCBCR_4_2_2 = 0x4,
+	YCBCR_4_2_0 = 0x8,
+	Y_ONLY = 0x10,
+};
+
+enum VIC_COLOR_DEPTH {
+	BCS_6 = 0x1,
+	BCS_8 = 0x2,
+	BCS_10 = 0x4,
+	BCS_12 = 0x8,
+	BCS_16 = 0x10,
+};
+
+enum VIC_BT_TYPE {
+	BT_601 = 0x0,
+	BT_709 = 0x1,
+};
+
+#endif /* _CDN_DP_REG_H */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index edd7ec2..98302b3 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -969,7 +969,7 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
 		vop_dsp_hold_valid_irq_disable(vop);
 	}
 
-	pin_pol = 0x8;
+	pin_pol = (s->output_type == DRM_MODE_CONNECTOR_DisplayPort) ? 0 : 0x8;
 	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1;
 	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : (1 << 1);
 	VOP_CTRL_SET(vop, pin_pol, pin_pol);
@@ -991,6 +991,10 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
 		VOP_CTRL_SET(vop, mipi_pin_pol, pin_pol);
 		VOP_CTRL_SET(vop, mipi_en, 1);
 		break;
+	case DRM_MODE_CONNECTOR_DisplayPort:
+		VOP_CTRL_SET(vop, dp_pin_pol, pin_pol);
+		VOP_CTRL_SET(vop, dp_en, 1);
+		break;
 	default:
 		DRM_ERROR("unsupport connector_type[%d]\n", s->output_type);
 	}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index ff4f52e..50a045c 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -45,6 +45,7 @@ struct vop_ctrl {
 	struct vop_reg edp_en;
 	struct vop_reg hdmi_en;
 	struct vop_reg mipi_en;
+	struct vop_reg dp_en;
 	struct vop_reg out_mode;
 	struct vop_reg dither_down;
 	struct vop_reg dither_up;
@@ -53,6 +54,7 @@ struct vop_ctrl {
 	struct vop_reg hdmi_pin_pol;
 	struct vop_reg edp_pin_pol;
 	struct vop_reg mipi_pin_pol;
+	struct vop_reg dp_pin_pol;
 
 	struct vop_reg htotal_pw;
 	struct vop_reg hact_st_end;
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 5b1ae1f..dcf172e 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -281,6 +281,7 @@ static const struct vop_data rk3288_vop = {
 static const struct vop_ctrl rk3399_ctrl_data = {
 	.standby = VOP_REG(RK3399_SYS_CTRL, 0x1, 22),
 	.gate_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 23),
+	.dp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 11),
 	.rgb_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 12),
 	.hdmi_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 13),
 	.edp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 14),
@@ -290,6 +291,7 @@ static const struct vop_ctrl rk3399_ctrl_data = {
 	.data_blank = VOP_REG(RK3399_DSP_CTRL0, 0x1, 19),
 	.out_mode = VOP_REG(RK3399_DSP_CTRL0, 0xf, 0),
 	.rgb_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16),
+	.dp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16),
 	.hdmi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 20),
 	.edp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 24),
 	.mipi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 28),
-- 
2.6.3



From tfiga at chromium.org  Tue Jul 12 08:10:13 2016
From: tfiga at chromium.org (Tomasz Figa)
Date: Wed, 13 Jul 2016 00:10:13 +0900
Subject: [PATCH v3 2/4] drm/rockchip: add an common abstracted PSR driver
In-Reply-To: <20160712123826.GU23520@phenom.ffwll.local>
References: <1467364685-21390-1-git-send-email-ykk@rock-chips.com>
 <1467364754-21674-1-git-send-email-ykk@rock-chips.com>
 <CAOw6vbKMWEEDEqw0_ba_FGO-2mOBg3uanQzk5nbnCtowWdLu9w@mail.gmail.com>
 <20160712123826.GU23520@phenom.ffwll.local>
Message-ID: <CAAFQd5CrT_bM+VLpzqZqu7p=32YYkTrNZw75FkPwx-LteUCBGw@mail.gmail.com>

On Tue, Jul 12, 2016 at 9:38 PM, Daniel Vetter <daniel at ffwll.ch> wrote:
> On Fri, Jul 01, 2016 at 02:00:00PM -0400, Sean Paul wrote:
>> On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk at rock-chips.com> wrote:
>> > The PSR driver have exported four symbols for specific device driver:
>> > - rockchip_drm_psr_register()
>> > - rockchip_drm_psr_unregister()
>> > - rockchip_drm_psr_enable()
>> > - rockchip_drm_psr_disable()
>> > - rockchip_drm_psr_flush()
>> >
>> > Encoder driver should call the register/unregister interfaces to hook
>> > itself into common PSR driver, encoder have implement the 'psr_set'
>> > callback which use the set PSR state in hardware side.
>> >
>> > Crtc driver would call the enable/disable interfaces when vblank is
>> > enable/disable, after that the common PSR driver would call the encoder
>> > registered callback to set the PSR state.
>> >
>>
>> This feels overly complicated. It seems like you could cut out a bunch
>> of code by just coding the psr functions into vop and
>> analogix_dp-rockchip. I suppose the only reason to keep it abstracted
>> would be if you plan on supporting psr in a different encoder or crtc
>> in rockchip, or if you're planning on moving this into drm core.
>
> Agreed on the layers of indirection. Also, you end up with 3 delayed
> timers in total:
> - defio timer from fbdev emulation
> - timer in this abstraction
> - delayed work in the psr backend driver

Maybe I'm missing something obvious (don't know how the PSR is
implemented in hardware or in other drivers), but why do we need all
these timers? Couldn't we just trigger a fake page flip on fb dirty
call?

Best regards,
Tomasz


From seanpaul at chromium.org  Tue Jul 12 08:29:38 2016
From: seanpaul at chromium.org (Sean Paul)
Date: Tue, 12 Jul 2016 08:29:38 -0700
Subject: [PATCH v3 3/4] drm/bridge: analogix_dp: add the PSR function
 support
In-Reply-To: <577F0F53.9060404@rock-chips.com>
References: <1467364685-21390-1-git-send-email-ykk@rock-chips.com>
 <1467364768-21741-1-git-send-email-ykk@rock-chips.com>
 <CAOw6vbJ4H9xzeaA-2OKsG+pGwdvehS91TZDPfUyq8R-=e70bPQ@mail.gmail.com>
 <577F0F53.9060404@rock-chips.com>
Message-ID: <CAOw6vbJWdrzHwa9cEintihco8=UYC+irPXOomAbjWeruwUW9Nw@mail.gmail.com>

On Thu, Jul 7, 2016 at 7:26 PM, Yakir Yang <ykk at rock-chips.com> wrote:
> Sean,
>
> Thanks for your review.
>
>
> On 07/02/2016 03:46 AM, Sean Paul wrote:
>>
>> On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk at rock-chips.com> wrote:
>>>
>>> The full name of PSR is Panel Self Refresh, panel device could refresh
>>> itself with the hardware framebuffer in panel, this would make lots of
>>> sense to save the power consumption.
>>>
>>> This patch have exported two symbols for platform driver to implement
>>> the PSR function in hardware side:
>>> - analogix_dp_active_psr()
>>> - analogix_dp_inactive_psr()
>>>
>>> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
>>> ---
>>> Changes in v3:
>>> - split analogix_dp_enable_psr(), make it more clearly
>>>      analogix_dp_detect_sink_psr()
>>>      analogix_dp_enable_sink_psr()
>>> - remove some nosie register setting comments
>>>
>>> Changes in v2:
>>> - introduce in v2, splite the common Analogix DP changes out
>>>
>>>   drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 64
>>> ++++++++++++++++++++++
>>>   drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |  4 ++
>>>   drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  | 54
>>> ++++++++++++++++++
>>>   drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h  | 28 ++++++++++
>>>   include/drm/bridge/analogix_dp.h                   |  3 +
>>>   5 files changed, 153 insertions(+)
>>>
>>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>>> b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>>> index 32715da..b557097 100644
>>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>>> @@ -97,6 +97,66 @@ static int analogix_dp_detect_hpd(struct
>>> analogix_dp_device *dp)
>>>          return 0;
>>>   }
>>>
>>> +int analogix_dp_active_psr(struct device *dev)
>>> +{
>>> +       struct analogix_dp_device *dp = dev_get_drvdata(dev);
>>> +
>>> +       if (!dp->psr_support)
>>> +               return -EINVAL;
>>> +
>>> +       analogix_dp_send_psr_spd(dp, EDP_VSC_PSR_STATE_ACTIVE |
>>> +                                EDP_VSC_PSR_CRC_VALUES_VALID);
>>> +       return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(analogix_dp_active_psr);
>>> +
>>> +int analogix_dp_inactive_psr(struct device *dev)
>>> +{
>>> +       struct analogix_dp_device *dp = dev_get_drvdata(dev);
>>> +
>>> +       if (!dp->psr_support)
>>> +               return -EINVAL;
>>> +
>>> +       analogix_dp_send_psr_spd(dp, 0);
>>> +       return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(analogix_dp_inactive_psr);
>>> +
>>> +static bool analogix_dp_detect_sink_psr(struct analogix_dp_device *dp)
>>> +{
>>> +       unsigned char psr_version;
>>> +
>>> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_SUPPORT,
>>> &psr_version);
>>> +       dev_info(dp->dev, "Panel PSR version : %x\n", psr_version);
>>> +
>>
>> This info message is likely to be spammy since it's printed everytime
>> the panel toggle on. Perhaps downgrade to debug level.
>
>
> Okay, done.
>
>>> +       return (psr_version & DP_PSR_IS_SUPPORTED) ? true : false;
>>> +}
>>> +
>>> +static int analogix_dp_enable_sink_psr(struct analogix_dp_device *dp)
>>
>> Return type is int, but the function never fails and you don't check
>> the return value when calling it. Seems like this should be void.
>
>
> Done.
>
>>> +{
>>> +       unsigned char psr_en;
>>> +
>>> +       /* Disable psr function */
>>> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
>>> +       psr_en &= ~DP_PSR_ENABLE;
>>> +       analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
>>> +
>>> +       /* Main-Link transmitter remains active during PSR active states
>>> */
>>> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
>>> +       psr_en = DP_PSR_MAIN_LINK_ACTIVE | DP_PSR_CRC_VERIFICATION;
>>
>> Why read psr_en if you're just going to overwrite it? Perhaps you meant |=
>> here.
>>
>
> Yes, it's my mistaken, no need to read the DP_PSR_EN_CFG, just configure it
> directly is enough.
>
>>> +       analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
>>> +
>>> +       /* Enable psr function */
>>> +       analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
>>> +       psr_en = DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE |
>>> +                DP_PSR_CRC_VERIFICATION;
>>
>> Again, no need to read if you're just overwriting.
>
>
> Yes, ditto
>
>
>>> +       analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
>>> +
>>> +       analogix_dp_enable_psr_crc(dp);
>>> +
>>> +       return 0;
>>> +}
>>> +
>>>   static unsigned char analogix_dp_calc_edid_check_sum(unsigned char
>>> *edid_data)
>>>   {
>>>          int i;
>>> @@ -921,6 +981,10 @@ static void analogix_dp_commit(struct
>>> analogix_dp_device *dp)
>>>
>>>          /* Enable video */
>>>          analogix_dp_start_video(dp);
>>> +
>>> +       dp->psr_support = analogix_dp_detect_sink_psr(dp);
>>> +       if (dp->psr_support)
>>> +               analogix_dp_enable_sink_psr(dp);
>>>   }
>>>
>>>   int analogix_dp_get_modes(struct drm_connector *connector)
>>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
>>> b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
>>> index b456380..6ca5dde 100644
>>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
>>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
>>> @@ -177,6 +177,7 @@ struct analogix_dp_device {
>>>          int                     hpd_gpio;
>>>          bool                    force_hpd;
>>>          unsigned char           edid[EDID_BLOCK_LENGTH * 2];
>>> +       bool                    psr_support;
>>>
>>>          struct analogix_dp_plat_data *plat_data;
>>>   };
>>> @@ -278,4 +279,7 @@ int analogix_dp_is_video_stream_on(struct
>>> analogix_dp_device *dp);
>>>   void analogix_dp_config_video_slave_mode(struct analogix_dp_device
>>> *dp);
>>>   void analogix_dp_enable_scrambling(struct analogix_dp_device *dp);
>>>   void analogix_dp_disable_scrambling(struct analogix_dp_device *dp);
>>> +void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp);
>>> +void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, int db1);
>>> +
>>>   #endif /* _ANALOGIX_DP_CORE_H */
>>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
>>> b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
>>> index 48030f0..e8372c7 100644
>>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
>>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
>>> @@ -1322,3 +1322,57 @@ void analogix_dp_disable_scrambling(struct
>>> analogix_dp_device *dp)
>>>          reg |= SCRAMBLING_DISABLE;
>>>          writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
>>>   }
>>> +
>>> +void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp)
>>> +{
>>> +       writel(PSR_VID_CRC_FLUSH | PSR_VID_CRC_ENABLE,
>>> +              dp->reg_base + ANALOGIX_DP_CRC_CON);
>>> +
>>> +       usleep_range(10, 20);
>>
>> Is this sleep arbitrary, or documented somewhere? Could you add a
>> comment explaining how this was arrived at?
>>
>
> Yes, this sleep is arbitrary, there is no document about the
>

Ok, so I assume we actually need the delay for things to work? If not,
delete the delay. If we need it, please add a comment saying the wait
is required and the value was chosen via trial and error.

>
>>> +
>>> +       writel(PSR_VID_CRC_ENABLE, dp->reg_base + ANALOGIX_DP_CRC_CON);
>>> +}
>>> +
>>> +void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, int db1)
>>> +{
>>> +       unsigned int val;
>>> +
>>> +       /* don't send info frame */
>>> +       val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>>> +       val &= ~IF_EN;
>>> +       writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>>> +
>>> +       /* configure single frame update mode */
>>> +       writel(0x3, dp->reg_base + ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL);
>>> +
>>> +       /* configure VSC HB0 ~ HB3 */
>>> +       writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_HB0);
>>> +       writel(0x07, dp->reg_base + ANALOGIX_DP_SPD_HB1);
>>> +       writel(0x02, dp->reg_base + ANALOGIX_DP_SPD_HB2);
>>> +       writel(0x08, dp->reg_base + ANALOGIX_DP_SPD_HB3);
>>> +
>>> +       /* configure VSC HB0 ~ HB3 */
>>> +       writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_PB0);
>>> +       writel(0x16, dp->reg_base + ANALOGIX_DP_SPD_PB1);
>>> +       writel(0xCE, dp->reg_base + ANALOGIX_DP_SPD_PB2);
>>> +       writel(0x5D, dp->reg_base + ANALOGIX_DP_SPD_PB3);
>>> +
>>> +       /* configure DB0 / DB1 values */
>>> +       writel(0x00, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB0);
>>
>> Lots of hardcoded values here. I think this could be cleaned up.
>
>
> For "HB0~HB3", "PB0~PB3" and "DB1", I don't understand very well. Those
> seems to be a kind of head number, I got those magic values from our IC
> side. So I think those should be okay to keep the hardcode values here :-D
>

Hmm. Well, I'd probably pull them out into some HBX_MAGIC_VALUE
define, but I suppose it's fine to keep them as-is. Please at least
add a comment above explaining that they're magic values provided by
your IC team.

> But for the "0x3" in "ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL", I would fix it
> with suitable macro.
>
>
>>> +       writel(db1, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB1);
>>> +
>>> +       /* set reuse spd inforframe */
>>> +       val = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
>>> +       val |= REUSE_SPD_EN;
>>> +       writel(val, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
>>> +
>>> +       /* mark info frame update */
>>> +       val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>>> +       val = (val | IF_UP) & ~IF_EN;
>>> +       writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>>> +
>>> +       /* send info frame */
>>> +       val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>>> +       val |= IF_EN;
>>> +       writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>>> +}
>>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
>>> b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
>>> index cdcc6c5..a2698e4 100644
>>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
>>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
>>> @@ -22,6 +22,8 @@
>>>   #define ANALOGIX_DP_VIDEO_CTL_8                        0x3C
>>>   #define ANALOGIX_DP_VIDEO_CTL_10               0x44
>>>
>>> +#define ANALOGIX_DP_SPDIF_AUDIO_CTL_0          0xD8
>>> +
>>>   #define ANALOGIX_DP_PLL_REG_1                  0xfc
>>>   #define ANALOGIX_DP_PLL_REG_2                  0x9e4
>>>   #define ANALOGIX_DP_PLL_REG_3                  0x9e8
>>> @@ -30,6 +32,21 @@
>>>
>>>   #define ANALOGIX_DP_PD                         0x12c
>>>
>>> +#define ANALOGIX_DP_IF_TYPE                    0x244
>>> +#define ANALOGIX_DP_IF_PKT_DB1                 0x254
>>> +#define ANALOGIX_DP_IF_PKT_DB2                 0x258
>>> +#define ANALOGIX_DP_SPD_HB0                    0x2F8
>>> +#define ANALOGIX_DP_SPD_HB1                    0x2FC
>>> +#define ANALOGIX_DP_SPD_HB2                    0x300
>>> +#define ANALOGIX_DP_SPD_HB3                    0x304
>>> +#define ANALOGIX_DP_SPD_PB0                    0x308
>>> +#define ANALOGIX_DP_SPD_PB1                    0x30C
>>> +#define ANALOGIX_DP_SPD_PB2                    0x310
>>> +#define ANALOGIX_DP_SPD_PB3                    0x314
>>> +#define ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL      0x318
>>> +#define ANALOGIX_DP_VSC_SHADOW_DB0             0x31C
>>> +#define ANALOGIX_DP_VSC_SHADOW_DB1             0x320
>>> +
>>>   #define ANALOGIX_DP_LANE_MAP                   0x35C
>>>
>>>   #define ANALOGIX_DP_ANALOG_CTL_1               0x370
>>> @@ -103,6 +120,8 @@
>>>
>>>   #define ANALOGIX_DP_SOC_GENERAL_CTL            0x800
>>>
>>> +#define ANALOGIX_DP_CRC_CON                    0x890
>>> +
>>>   /* ANALOGIX_DP_TX_SW_RESET */
>>>   #define RESET_DP_TX                            (0x1 << 0)
>>>
>>> @@ -151,6 +170,7 @@
>>>   #define VID_CHK_UPDATE_TYPE_SHIFT              (4)
>>>   #define VID_CHK_UPDATE_TYPE_1                  (0x1 << 4)
>>>   #define VID_CHK_UPDATE_TYPE_0                  (0x0 << 4)
>>> +#define REUSE_SPD_EN                           (0x1 << 3)
>>>
>>>   /* ANALOGIX_DP_VIDEO_CTL_8 */
>>>   #define VID_HRES_TH(x)                         (((x) & 0xf) << 4)
>>> @@ -376,4 +396,12 @@
>>>   #define VIDEO_MODE_SLAVE_MODE                  (0x1 << 0)
>>>   #define VIDEO_MODE_MASTER_MODE                 (0x0 << 0)
>>>
>>> +/* ANALOGIX_DP_PKT_SEND_CTL */
>>> +#define IF_UP                                  (0x1 << 4)
>>> +#define IF_EN                                  (0x1 << 0)
>>> +
>>> +/* ANALOGIX_DP_CRC_CON */
>>> +#define PSR_VID_CRC_FLUSH                      (0x1 << 2)
>>> +#define PSR_VID_CRC_ENABLE                     (0x1 << 0)
>>> +
>>>   #endif /* _ANALOGIX_DP_REG_H */
>>> diff --git a/include/drm/bridge/analogix_dp.h
>>> b/include/drm/bridge/analogix_dp.h
>>> index 261b86d..183a336 100644
>>> --- a/include/drm/bridge/analogix_dp.h
>>> +++ b/include/drm/bridge/analogix_dp.h
>>> @@ -38,6 +38,9 @@ struct analogix_dp_plat_data {
>>>                           struct drm_connector *);
>>>   };
>>>
>>> +int analogix_dp_active_psr(struct device *dev);
>>> +int analogix_dp_inactive_psr(struct device *dev);
>>
>> Why active/inactive instead of enable/disable, which is used everywhere
>> else?
>
>
> Done
>
> Thanks,
> - Yakir
>
>
>>> +
>>>   int analogix_dp_resume(struct device *dev);
>>>   int analogix_dp_suspend(struct device *dev);
>>>
>>> --
>>> 1.9.1
>>>
>>>
>>
>>
>
>


From daniel at ffwll.ch  Tue Jul 12 09:10:26 2016
From: daniel at ffwll.ch (Daniel Vetter)
Date: Tue, 12 Jul 2016 18:10:26 +0200
Subject: [PATCH v3 2/4] drm/rockchip: add an common abstracted PSR driver
In-Reply-To: <CAAFQd5CrT_bM+VLpzqZqu7p=32YYkTrNZw75FkPwx-LteUCBGw@mail.gmail.com>
References: <1467364685-21390-1-git-send-email-ykk@rock-chips.com>
 <1467364754-21674-1-git-send-email-ykk@rock-chips.com>
 <CAOw6vbKMWEEDEqw0_ba_FGO-2mOBg3uanQzk5nbnCtowWdLu9w@mail.gmail.com>
 <20160712123826.GU23520@phenom.ffwll.local>
 <CAAFQd5CrT_bM+VLpzqZqu7p=32YYkTrNZw75FkPwx-LteUCBGw@mail.gmail.com>
Message-ID: <20160712161026.GV23520@phenom.ffwll.local>

On Wed, Jul 13, 2016 at 12:10:13AM +0900, Tomasz Figa wrote:
> On Tue, Jul 12, 2016 at 9:38 PM, Daniel Vetter <daniel at ffwll.ch> wrote:
> > On Fri, Jul 01, 2016 at 02:00:00PM -0400, Sean Paul wrote:
> >> On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk at rock-chips.com> wrote:
> >> > The PSR driver have exported four symbols for specific device driver:
> >> > - rockchip_drm_psr_register()
> >> > - rockchip_drm_psr_unregister()
> >> > - rockchip_drm_psr_enable()
> >> > - rockchip_drm_psr_disable()
> >> > - rockchip_drm_psr_flush()
> >> >
> >> > Encoder driver should call the register/unregister interfaces to hook
> >> > itself into common PSR driver, encoder have implement the 'psr_set'
> >> > callback which use the set PSR state in hardware side.
> >> >
> >> > Crtc driver would call the enable/disable interfaces when vblank is
> >> > enable/disable, after that the common PSR driver would call the encoder
> >> > registered callback to set the PSR state.
> >> >
> >>
> >> This feels overly complicated. It seems like you could cut out a bunch
> >> of code by just coding the psr functions into vop and
> >> analogix_dp-rockchip. I suppose the only reason to keep it abstracted
> >> would be if you plan on supporting psr in a different encoder or crtc
> >> in rockchip, or if you're planning on moving this into drm core.
> >
> > Agreed on the layers of indirection. Also, you end up with 3 delayed
> > timers in total:
> > - defio timer from fbdev emulation
> > - timer in this abstraction
> > - delayed work in the psr backend driver
> 
> Maybe I'm missing something obvious (don't know how the PSR is
> implemented in hardware or in other drivers), but why do we need all
> these timers? Couldn't we just trigger a fake page flip on fb dirty
> call?

Page flips are ratelimited to vrefresh, and not all drivers support it.
The other issue is that dirty ioctl supports dirty rectangles, but page
flip is still a full-screen upload. We discussed adding a dirty rectangle
to the kms properties to fix this gap, but until that's done I don't think
it makes much sense to remap dirty to an (atomic) page flip. One small
technicality is that dirty works on framebuffers, but page flips
(especially atomic ones) work on crtcs. That makes the remapping a notch
more tricky.

But in principle, fully agreed. And once we have atomic dirty rectangles
we could type up a small helper that drivers can plug into the dirty
ioctl. Since new properties requires new userspace we could just track the
dirty rectangle internally to get this helper landed, if there's
interested for that. I'll certainly be happy to review patches.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


From kyle at infradead.org  Tue Jul 12 12:48:23 2016
From: kyle at infradead.org (Kyle McMartin)
Date: Tue, 12 Jul 2016 15:48:23 -0400
Subject: [PATCH] Add dptx firmware for rockchip rk3399
In-Reply-To: <1468335519-28245-1-git-send-email-zyw@rock-chips.com>
References: <1468335519-28245-1-git-send-email-zyw@rock-chips.com>
Message-ID: <20160712194823.GJ8680@merlin.infradead.org>

On Tue, Jul 12, 2016 at 10:58:38PM +0800, Chris Zhong wrote:
> This dptx firmware is required by rk3399 Type-C DisplayPort controller.
> 
> Signed-off-by: Chris Zhong <zyw at rock-chips.com>
> 

Applied, thanks Chris.

regards, --Kyle

> ---
> 
>  WHENCE            |  18 ++++++++++++++++++
>  rockchip/dptx.bin | Bin 0 -> 98320 bytes
>  2 files changed, 18 insertions(+)
>  create mode 100644 rockchip/dptx.bin
> 
> diff --git a/WHENCE b/WHENCE
> index d8b1eaa..f463e26 100644
> --- a/WHENCE
> +++ b/WHENCE
> @@ -3294,3 +3294,21 @@ General Public License.
>  These firmware files are distributed in the hope that they will be useful, but
>  are provided WITHOUT ANY WARRANTY, INCLUDING BUT NOT LIMITED TO IMPLIED WARRANTY
>  OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
> +
> +--------------------------------------------------------------------------
> +
> +Driver: rk3399-dptx - ROCKCHIP rk3399 dptx firmware
> +
> +File: rockchip/dptx.bin
> +
> +Licence: Redistributable.
> +
> +Rockchip Inc. grants permission to use and redistribute aforementioned firmware
> +files for the use with devices containing Rockchip chipsets, but not as part of
> +the Linux kernel or in any other form which would require these files themselves
> +to be covered by the terms of the GNU General Public License or the GNU Lesser
> +General Public License.
> +
> +These firmware files are distributed in the hope that they will be useful, but
> +are provided WITHOUT ANY WARRANTY, INCLUDING BUT NOT LIMITED TO IMPLIED WARRANTY
> +OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
> diff --git a/rockchip/dptx.bin b/rockchip/dptx.bin
> new file mode 100644
> index 0000000000000000000000000000000000000000..042ec48d748a4a1e9b8174fa0d3fcc64bf2c562f


From shawn.lin at rock-chips.com  Tue Jul 12 18:10:15 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Wed, 13 Jul 2016 09:10:15 +0800
Subject: [PATCH v6 1/2] Documentation: bindings: add dt doc for Rockchip
 PCIe controller
In-Reply-To: <20160707003912.GB100467@google.com>
References: <1467789398-13501-1-git-send-email-shawn.lin@rock-chips.com>
 <20160707003912.GB100467@google.com>
Message-ID: <59b328c9-3a21-36fa-7f28-4ba24d77fef0@rock-chips.com>

? 2016/7/7 8:39, Brian Norris ??:
> Hi Shawn,
>
> On Wed, Jul 06, 2016 at 03:16:37PM +0800, Shawn Lin wrote:
>> This patch adds a binding that describes the Rockchip PCIe controller
>> found on Rockchip SoCs PCIe interface.
>>
>> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
>>
>> Acked-by: Rob Herring <robh at kernel.org>
>> ---
>>
>> Changes in v6:
>> - add ack tag from Rob
>>
>> Changes in v5:
>> - fix wrong example reported by Marc
>> - add seperate section to describe the interrupt controller child
>>   node
>>
>> Changes in v4:
>> - fix example of adding intermediate interrupt controller for pcie
>>   legacy interrrupt
>>
>> Changes in v3:
>> - fix example dts code suggested by Rob and Marc
>> - remove driver's behaviour of regulator
>>
>> Changes in v2:
>> - fix lots clk/reset stuff suggested by Heiko
>> - remove msi-parent and add msi-map suggested by Marc
>> - drop phy related stuff
>> - some others minor fixes
>>
>>  .../devicetree/bindings/pci/rockchip-pcie.txt      | 104 +++++++++++++++++++++
>>  1 file changed, 104 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/pci/rockchip-pcie.txt
>>
>> diff --git a/Documentation/devicetree/bindings/pci/rockchip-pcie.txt b/Documentation/devicetree/bindings/pci/rockchip-pcie.txt
>> new file mode 100644
>> index 0000000..7616ecc
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/pci/rockchip-pcie.txt
>> @@ -0,0 +1,104 @@
>> +* Rockchip AXI PCIe Root Port Bridge DT description
>> +
>> +Required properties:
>> +- #address-cells: Address representation for root ports, set to <3>
>> +- #size-cells: Size representation for root ports, set to <2>
>> +- #interrupt-cells: specifies the number of cells needed to encode an
>> +		interrupt source. The value must be 1.
>> +- compatible: Should contain "rockchip,rk3399-pcie"
>> +- reg: Two register ranges as listed in the reg-names property
>> +- reg-names: Must include the following names
>> +	- "axi-base"
>> +	- "apb-base"
>> +- clocks: Must contain an entry for each entry in clock-names.
>> +		See ../clocks/clock-bindings.txt for details.
>> +- clock-names: Must include the following entries:
>> +	- "aclk"
>> +	- "aclk-perf"
>> +	- "hclk"
>> +	- "pm"
>> +- msi-map: Maps a Requester ID to an MSI controller and associated.
>> +		See ./pci-msi.txt
>> +- phys: From PHY bindings: Phandle for the Generic PHY for PCIe.
>> +- phy-names:  MUST be "pcie-phy".
>> +- interrupts: Three interrupt entries must be specified.
>> +- interrupt-names: Must include the following names
>> +	- "sys"
>> +	- "legacy"
>> +	- "client"
>> +- resets: Must contain five entries for each entry in reset-names.
>> +	   See ../reset/reset.txt for details.
>> +- reset-names: Must include the following names
>> +	- "core"
>> +	- "mgmt"
>> +	- "mgmt-sticky"
>> +	- "pipe"
>> +- pinctrl-names : The pin control state names
>> +- pinctrl-0: The "default" pinctrl state
>> +- #interrupt-cells: specifies the number of cells needed to encode an
>> +	interrupt source. The value must be 1.
>> +- interrupt-map-mask and interrupt-map: standard PCI properties
>> +
>> +*Interrupt controller child node*
>> +The core controller provides a single interrupt for legacy INTx. So,
>> +pcie node should create a interrupt controller node to support 'interrupt-map'
>> +DT functionality. The driver will create an IRQ domain for this map, decode
>> +the four INTx interrupts in ISR and route them to this domain.
>
> Where in your driver do you actually handle this child node? I don't see
> anything, but perhaps I'm missing something. I see how your earlier
> revisions of this driver used of_get_next_child() to acquire the child
> node, for use with irq_domain_add_linear(). But that's not in this
> version...
>
>> +
>> +Required properties for Interrupt controller child node:
>> +- interrupt-controller: identifies the node as an interrupt controller
>> +- #address-cells: specifies the number of cells needed to encode an
>> +	address. The value must be 0.
>> +- #interrupt-cells: specifies the number of cells needed to encode an
>> +	interrupt source. The value must be 1.
>> +
>> +Optional Property:
>
> These optional properties apply to the pcie node, not the interrupt
> controller child, right? Seems like the subnode and its properties
> should be last (i.e., the 'Optional Property' section should be above
> 'Interrupt controller child node').

okay, i will move it ahead.

>
>> +- ep-gpios: contain the entry for pre-reset gpio
>> +- num-lanes: number of lanes to use
>> +- vpcie3v3-supply: The phandle to the 3.3v regulator to use for pcie.
>> +- vpcie1v8-supply: The phandle to the 1.8v regulator to use for pcie.
>> +- vpcie0v9-supply: The phandle to the 0.9v regulator to use for pcie.
>> +
>> +Example:
>> +
>> +pcie0: pcie at f8000000 {
>> +	compatible = "rockchip,rk3399-pcie";
>> +	#address-cells = <3>;
>> +	#size-cells = <2>;
>> +	clocks = <&cru ACLK_PCIE>, <&cru ACLK_PERF_PCIE>,
>> +		 <&cru PCLK_PCIE>, <&cru SCLK_PCIE_PM>;
>> +	clock-names = "aclk", "aclk-perf",
>> +		      "hclk", "pm";
>> +	bus-range = <0x0 0x1>;
>> +	interrupts = <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 50 IRQ_TYPE_LEVEL_HIGH>,
>> +		     <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>;
>> +	interrupt-names = "sys", "legacy", "client";
>> +	assigned-clocks = <&cru SCLK_PCIEPHY_REF>;
>> +	assigned-clock-parents = <&cru SCLK_PCIEPHY_REF100M>;
>> +	assigned-clock-rates = <100000000>;
>> +	ep-gpios = <&gpio3 13 GPIO_ACTIVE_HIGH>;
>> +	ranges = <0x83000000 0x0 0xfa000000 0x0 0xfa000000 0x0 0x600000
>> +		  0x81000000 0x0 0xfa600000 0x0 0xfa600000 0x0 0x100000>;
>> +	num-lanes = <4>;
>> +	msi-map = <0x0 &its 0x0 0x1000>;
>> +	reg = < 0x0 0xf8000000 0x0 0x2000000 >, < 0x0 0xfd000000 0x0 0x1000000 >;
>> +	reg-names = "axi-base", "apb-base";
>> +	resets = <&cru SRST_PCIE_CORE>, <&cru SRST_PCIE_MGMT>,
>> +		 <&cru SRST_PCIE_MGMT_STICKY>, <&cru SRST_PCIE_PIPE>;
>> +	reset-names = "core", "mgmt", "mgmt-sticky", "pipe";
>> +	phys = <&pcie_phy>;
>> +	phy-names = "pcie-phy";
>> +	pinctrl-names = "default";
>> +	pinctrl-0 = <&pcie_clkreq>;
>> +	#interrupt-cells = <1>;
>> +	interrupt-map-mask = <0 0 0 7>;
>> +	interrupt-map = <0 0 0 1 &pcie0_intc 1>,
>> +			<0 0 0 2 &pcie0_intc 2>,
>> +			<0 0 0 3 &pcie0_intc 3>,
>> +			<0 0 0 4 &pcie0_intc 4>;
>
> I'm a little lost on this one, so forgive my ignorance; how did you
> determine the last value in each entry (i.e., the 1, 2, 3, and 4 IRQ
> numbers for pcie0_intc)? IIUC, those are supposed to represent indeces
> into the IRQ status register found in the PCIe interrupt status
> register, and so they should be 0-based (i.e., 0, 1, 2, 3). And then
> you'd have:
>
> 	interrupt-map = <0 0 0 1 &pcie0_intc 0>,
> 			<0 0 0 2 &pcie0_intc 1>,
> 			<0 0 0 3 &pcie0_intc 2>,
> 			<0 0 0 4 &pcie0_intc 3>;
>
> But then, I never got this sub-node binding to work quite right, so I
> may be missing something.
>
> EDIT: ooh, I see what's going on! I'll comment on the driver as well,
> but it looks like you're translating the register status to a HW IRQ
> number with 'ffs(reg)', which yields a 1-based index. I think it is most
> sensible to use a 0-based index (i.e., 'ffs(reg) - 1'). Now, that only
> will work if you get the whole interrupt-map + interrupt-controller
> thing right (i.e., using a subnode for the interrupt controller) --
> otherwise, IRQ mapping might not work right. I suspect that's one reason
> the original driver writer might have used 1-based indexing in the first
> place.

yes, I got it but.....what's the difference?
You still need to get the whole interrupt-map + interrupt-controller
things right and the code(ffs(reg) - 1)if applied your suggestion.

Look at most of the docs for pcie bindings, I saw they also take 0-base 
index, how about?

>
> Brian
>
>> +	pcie0_intc: interrupt-controller {
>> +		interrupt-controller;
>> +		#address-cells = <0>;
>> +		#interrupt-cells = <1>;
>> +	};
>> +};
>> --
>> 2.3.7
>>
>>
>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip
>


-- 
Best Regards
Shawn Lin



From cw00.choi at samsung.com  Tue Jul 12 18:11:14 2016
From: cw00.choi at samsung.com (Chanwoo Choi)
Date: Wed, 13 Jul 2016 10:11:14 +0900
Subject: [v5 PATCH 1/5] extcon: Add Type-C and DP support
In-Reply-To: <1468336188-565-2-git-send-email-zyw@rock-chips.com>
References: <1468336188-565-1-git-send-email-zyw@rock-chips.com>
 <CGME20160712151003epcas1p3d60b6754866b984f943b7bea4be6d33f@epcas1p3.samsung.com>
 <1468336188-565-2-git-send-email-zyw@rock-chips.com>
Message-ID: <57859532.1010002@samsung.com>

Hi Chris,

I'm now developing the extcon property on extcon-test branch.
But, it has not been completed.

On next version, I'll remove the notification about extcon property
and only support the following two functions.
- extcon_set_cable_property()
- extcon_get_cable_property()

Because the number of properties would be risen and the all properties
depend on the specific external connector(e.g., EXTCON_PROP_USB_VBUS
depend on the EXTCON_TYPE_USB type). When the specific external connector
is detached, extcon framework should make the property state as default state.

It may send the too many notification for extcon property.
For example, Assume that EXTCON_TYPE_USB has the over 20 properties,
when EXTCON_USB or EXTCON_USB_HOST is detached, extcon should send
the notification for the over 20 properties and one more notificaiton
for state of external connector.

So, I'll send the RFC patchset without the notification of proerty.

Lastly,
I have a comment on below.

Thanks,
Chanwoo Choi

On 2016? 07? 13? 00:09, Chris Zhong wrote:
> Add EXTCON_DISP_DP for the Display external connector. For Type-C
> connector the DisplayPort can work as an Alternate Mode(VESA DisplayPort
> Alt Mode on USB Type-C Standard). The Type-C support both normal and
> flipped orientation, so add a property to extcon.
> 
> Signe-off-by: Chris Zhong <zyw at rock-chips.com>
> 
> Signed-off-by: Chris Zhong <zyw at rock-chips.com>
> ---
> 
> Changes in v5:
> - support get property
> 
> Changes in v4: None
> Changes in v3: None
> Changes in v2: None
> Changes in v1: None
> 
>  drivers/extcon/extcon.c | 28 ++++++++++++++++++++++++++++
>  include/linux/extcon.h  | 13 +++++++++++++
>  2 files changed, 41 insertions(+)
> 
> diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c
> index a1117db..2591b28 100644
> --- a/drivers/extcon/extcon.c
> +++ b/drivers/extcon/extcon.c
> @@ -157,6 +157,11 @@ struct __extcon_info {
>  		.id = EXTCON_DISP_VGA,
>  		.name = "VGA",
>  	},
> +	[EXTCON_DISP_DP] = {
> +		.type = EXTCON_TYPE_DISP,
> +		.id = EXTCON_DISP_DP,
> +		.name = "DP",
> +	},
>  
>  	/* Miscellaneous external connector */
>  	[EXTCON_DOCK] = {
> @@ -270,6 +275,7 @@ static bool is_extcon_property_supported(unsigned int id,
>  		switch (prop) {
>  		case EXTCON_PROP_USB_ID:
>  		case EXTCON_PROP_USB_VBUS:
> +		case EXTCON_PROP_TYPEC_POLARITY:
>  			return true;
>  		default:
>  			break;
> @@ -286,6 +292,8 @@ static bool is_extcon_property_supported(unsigned int id,
>  		}
>  	case EXTCON_TYPE_DISP:
>  		switch (prop) {
> +		case EXTCON_PROP_TYPEC_POLARITY:

Should EXTCON_PROP_TYPEC_POLARITY property add to both EXTCON_TYPE_USB and EXTCON_TYP_DISP?
EXTCON_PROP_TYPEC_POLARITY is the property of USB C-type?

> +			return true;
>  		default:
>  			break;
>  		}
> @@ -547,6 +555,26 @@ int extcon_get_cable_property(struct extcon_dev *edev, unsigned int id,
>  				enum extcon_property prop,
>  				union extcon_property_value *val)
>  {
> +	struct extcon_cable *cable;
> +	int index;
> +
> +	if (!edev)
> +		return -EINVAL;
> +
> +	/* Check the property whether is supported or not */
> +	if (!is_extcon_property_supported(id, prop))
> +		return -EINVAL;
> +
> +	/* Find the cable index of external connector by using id */
> +	index = find_cable_index_by_id(edev, id);
> +	if (index < 0)
> +		return index;
> +
> +	/* Store the property value */
> +	cable = &edev->cables[index];
> +
> +	val->intval = cable->propval[prop].intval;
> +
>  	return 0;
>  }

After I develop it about get_cable_property, I'll send RFC patchset.

>  
> diff --git a/include/linux/extcon.h b/include/linux/extcon.h
> index f6f0a8d..50ef87f 100644
> --- a/include/linux/extcon.h
> +++ b/include/linux/extcon.h
> @@ -77,6 +77,7 @@ enum extcon_type {
>  #define EXTCON_DISP_MHL		41	/* Mobile High-Definition Link */
>  #define EXTCON_DISP_DVI		42	/* Digital Visual Interface */
>  #define EXTCON_DISP_VGA		43	/* Video Graphics Array */
> +#define EXTCON_DISP_DP		44	/* DisplayPort */
>  
>  /* Miscellaneous external connector */
>  #define EXTCON_DOCK		60
> @@ -108,9 +109,13 @@ enum extcon_property {
>  	 * - EXTCON_PROP_USB_USB
>  	 * @type:	integer (int value)
>  	 * @value:	0 (low) or 1 (high)
> +	 * - EXTCON_PROP_TYPEC_POLARITY,
> +	 * @type:	integer (int value)
> +	 * @value:	0 (normal) or 1 (flip)
>  	 */
>  	EXTCON_PROP_USB_ID = 0,
>  	EXTCON_PROP_USB_VBUS,
> +	EXTCON_PROP_TYPEC_POLARITY,
>  
>  	/* Properties of EXTCON_TYPE_CHG. */
>  	/* Properties of EXTCON_TYPE_JACK. */
> @@ -225,6 +230,14 @@ extern int extcon_get_cable_state_(struct extcon_dev *edev, unsigned int id);
>  extern int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id,
>  				   bool cable_state);
>  
> +extern int extcon_get_cable_property(struct extcon_dev *edev, unsigned int id,
> +				     enum extcon_property prop,
> +				     union extcon_property_value *val);
> +
> +extern int extcon_set_cable_property(struct extcon_dev *edev, unsigned int id,
> +				     enum extcon_property prop,
> +				     union extcon_property_value val);
> +

ditto.

Thanks,
Chanwoo Choi


From briannorris at chromium.org  Tue Jul 12 18:31:18 2016
From: briannorris at chromium.org (Brian Norris)
Date: Tue, 12 Jul 2016 18:31:18 -0700
Subject: [PATCH v6 1/2] Documentation: bindings: add dt doc for Rockchip
 PCIe controller
In-Reply-To: <59b328c9-3a21-36fa-7f28-4ba24d77fef0@rock-chips.com>
References: <1467789398-13501-1-git-send-email-shawn.lin@rock-chips.com>
 <20160707003912.GB100467@google.com>
 <59b328c9-3a21-36fa-7f28-4ba24d77fef0@rock-chips.com>
Message-ID: <20160713013117.GA130126@google.com>

Hi Shawn,

On Wed, Jul 13, 2016 at 09:10:15AM +0800, Shawn Lin wrote:
> ? 2016/7/7 8:39, Brian Norris ??:
> >On Wed, Jul 06, 2016 at 03:16:37PM +0800, Shawn Lin wrote:
> >>+	#interrupt-cells = <1>;
> >>+	interrupt-map-mask = <0 0 0 7>;
> >>+	interrupt-map = <0 0 0 1 &pcie0_intc 1>,
> >>+			<0 0 0 2 &pcie0_intc 2>,
> >>+			<0 0 0 3 &pcie0_intc 3>,
> >>+			<0 0 0 4 &pcie0_intc 4>;
> >
> >I'm a little lost on this one, so forgive my ignorance; how did you
> >determine the last value in each entry (i.e., the 1, 2, 3, and 4 IRQ
> >numbers for pcie0_intc)? IIUC, those are supposed to represent indeces
> >into the IRQ status register found in the PCIe interrupt status
> >register, and so they should be 0-based (i.e., 0, 1, 2, 3). And then
> >you'd have:
> >
> >	interrupt-map = <0 0 0 1 &pcie0_intc 0>,
> >			<0 0 0 2 &pcie0_intc 1>,
> >			<0 0 0 3 &pcie0_intc 2>,
> >			<0 0 0 4 &pcie0_intc 3>;
> >
> >But then, I never got this sub-node binding to work quite right, so I
> >may be missing something.
> >
> >EDIT: ooh, I see what's going on! I'll comment on the driver as well,
> >but it looks like you're translating the register status to a HW IRQ
> >number with 'ffs(reg)', which yields a 1-based index. I think it is most
> >sensible to use a 0-based index (i.e., 'ffs(reg) - 1'). Now, that only
> >will work if you get the whole interrupt-map + interrupt-controller
> >thing right (i.e., using a subnode for the interrupt controller) --
> >otherwise, IRQ mapping might not work right. I suspect that's one reason
> >the original driver writer might have used 1-based indexing in the first
> >place.
> 
> yes, I got it but.....what's the difference?

At some level, it's a matter of preference. But when you're talking
about the rk3399 PCIe "interrupt controller" domain, it seems that you
should be talking about HW bits in the controller -- i.e., you have a
4-bit interrupt status bitfield, that we typically call [0:3]. If you
use [1:4], then you have to remember to subtract 1 mentally when mapping
to the actual HW bit. I believe that confusion (since bitfields normally
count from 0) might have helped cause the infinite loop bug I noticed
too. And I also think that counting from 0 helps clarify the fact that
your interrupt controller indexing is an independent numbering from the
PCI interrupt numbering, even though they happen to map 1:1.

But then, PCI INTx numbering is kinda weird already, as it starts from
1. So maybe it's just as valid to say our domain starts from 1 as well.

> You still need to get the whole interrupt-map + interrupt-controller
> things right and the code(ffs(reg) - 1)if applied your suggestion.

Yes, of course. And I already sent you patches that do that.

> Look at most of the docs for pcie bindings, I saw they also take
> 0-base index, how about?

I don't know which ones you're referring to. I see that altera-pcie.txt
supports interrupt indeces counting from 1, but that's probably because
they're using the same broken binding that was in your ~v3 patches
(where the pcie node has both 'interrupt-controller' and
'interrupt-map', with phandles to itself), so they had no other choice.

If you still think it makes more sense to count from 1, then I won't
stop you.

Regards,
Brian


From zyw at rock-chips.com  Tue Jul 12 18:39:38 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Wed, 13 Jul 2016 09:39:38 +0800
Subject: [v5 PATCH 1/5] extcon: Add Type-C and DP support
In-Reply-To: <57859532.1010002@samsung.com>
References: <1468336188-565-1-git-send-email-zyw@rock-chips.com>
 <CGME20160712151003epcas1p3d60b6754866b984f943b7bea4be6d33f@epcas1p3.samsung.com>
 <1468336188-565-2-git-send-email-zyw@rock-chips.com>
 <57859532.1010002@samsung.com>
Message-ID: <57859BDA.7080309@rock-chips.com>

Hi Chanwoo Choi


On 07/13/2016 09:11 AM, Chanwoo Choi wrote:
> Hi Chris,
>
> I'm now developing the extcon property on extcon-test branch.
> But, it has not been completed.
>
> On next version, I'll remove the notification about extcon property
> and only support the following two functions.
> - extcon_set_cable_property()
> - extcon_get_cable_property()
>
> Because the number of properties would be risen and the all properties
> depend on the specific external connector(e.g., EXTCON_PROP_USB_VBUS
> depend on the EXTCON_TYPE_USB type). When the specific external connector
> is detached, extcon framework should make the property state as default state.

Yes, I think getting the notification from cable state is enough, 
actually I am using it like you said.

>
> It may send the too many notification for extcon property.
> For example, Assume that EXTCON_TYPE_USB has the over 20 properties,
> when EXTCON_USB or EXTCON_USB_HOST is detached, extcon should send
> the notification for the over 20 properties and one more notificaiton
> for state of external connector.
>
> So, I'll send the RFC patchset without the notification of proerty.
>
> Lastly,
> I have a comment on below.
>
> Thanks,
> Chanwoo Choi
>
> On 2016? 07? 13? 00:09, Chris Zhong wrote:
>> Add EXTCON_DISP_DP for the Display external connector. For Type-C
>> connector the DisplayPort can work as an Alternate Mode(VESA DisplayPort
>> Alt Mode on USB Type-C Standard). The Type-C support both normal and
>> flipped orientation, so add a property to extcon.
>>
>> Signe-off-by: Chris Zhong <zyw at rock-chips.com>
>>
>> Signed-off-by: Chris Zhong <zyw at rock-chips.com>
>> ---
>>
>> Changes in v5:
>> - support get property
>>
>> Changes in v4: None
>> Changes in v3: None
>> Changes in v2: None
>> Changes in v1: None
>>
>>   drivers/extcon/extcon.c | 28 ++++++++++++++++++++++++++++
>>   include/linux/extcon.h  | 13 +++++++++++++
>>   2 files changed, 41 insertions(+)
>>
>> diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c
>> index a1117db..2591b28 100644
>> --- a/drivers/extcon/extcon.c
>> +++ b/drivers/extcon/extcon.c
>> @@ -157,6 +157,11 @@ struct __extcon_info {
>>   		.id = EXTCON_DISP_VGA,
>>   		.name = "VGA",
>>   	},
>> +	[EXTCON_DISP_DP] = {
>> +		.type = EXTCON_TYPE_DISP,
>> +		.id = EXTCON_DISP_DP,
>> +		.name = "DP",
>> +	},
>>   
>>   	/* Miscellaneous external connector */
>>   	[EXTCON_DOCK] = {
>> @@ -270,6 +275,7 @@ static bool is_extcon_property_supported(unsigned int id,
>>   		switch (prop) {
>>   		case EXTCON_PROP_USB_ID:
>>   		case EXTCON_PROP_USB_VBUS:
>> +		case EXTCON_PROP_TYPEC_POLARITY:
>>   			return true;
>>   		default:
>>   			break;
>> @@ -286,6 +292,8 @@ static bool is_extcon_property_supported(unsigned int id,
>>   		}
>>   	case EXTCON_TYPE_DISP:
>>   		switch (prop) {
>> +		case EXTCON_PROP_TYPEC_POLARITY:
> Should EXTCON_PROP_TYPEC_POLARITY property add to both EXTCON_TYPE_USB and EXTCON_TYP_DISP?
> EXTCON_PROP_TYPEC_POLARITY is the property of USB C-type?

it is for USB Type-C, But at Display Port alt mode, both EXTCON_USB and 
EXTCON_USB_HOST may be detached. Does it support set the property to a 
detached cable, if so, I think move this case to EXTCON_USB is fine.

Thanks
Chris
>
>> +			return true;
>>   		default:
>>   			break;
>>   		}
>> @@ -547,6 +555,26 @@ int extcon_get_cable_property(struct extcon_dev *edev, unsigned int id,
>>   				enum extcon_property prop,
>>   				union extcon_property_value *val)
>>   {
>> +	struct extcon_cable *cable;
>> +	int index;
>> +
>> +	if (!edev)
>> +		return -EINVAL;
>> +
>> +	/* Check the property whether is supported or not */
>> +	if (!is_extcon_property_supported(id, prop))
>> +		return -EINVAL;
>> +
>> +	/* Find the cable index of external connector by using id */
>> +	index = find_cable_index_by_id(edev, id);
>> +	if (index < 0)
>> +		return index;
>> +
>> +	/* Store the property value */
>> +	cable = &edev->cables[index];
>> +
>> +	val->intval = cable->propval[prop].intval;
>> +
>>   	return 0;
>>   }
> After I develop it about get_cable_property, I'll send RFC patchset.
>
>>   
>> diff --git a/include/linux/extcon.h b/include/linux/extcon.h
>> index f6f0a8d..50ef87f 100644
>> --- a/include/linux/extcon.h
>> +++ b/include/linux/extcon.h
>> @@ -77,6 +77,7 @@ enum extcon_type {
>>   #define EXTCON_DISP_MHL		41	/* Mobile High-Definition Link */
>>   #define EXTCON_DISP_DVI		42	/* Digital Visual Interface */
>>   #define EXTCON_DISP_VGA		43	/* Video Graphics Array */
>> +#define EXTCON_DISP_DP		44	/* DisplayPort */
>>   
>>   /* Miscellaneous external connector */
>>   #define EXTCON_DOCK		60
>> @@ -108,9 +109,13 @@ enum extcon_property {
>>   	 * - EXTCON_PROP_USB_USB
>>   	 * @type:	integer (int value)
>>   	 * @value:	0 (low) or 1 (high)
>> +	 * - EXTCON_PROP_TYPEC_POLARITY,
>> +	 * @type:	integer (int value)
>> +	 * @value:	0 (normal) or 1 (flip)
>>   	 */
>>   	EXTCON_PROP_USB_ID = 0,
>>   	EXTCON_PROP_USB_VBUS,
>> +	EXTCON_PROP_TYPEC_POLARITY,
>>   
>>   	/* Properties of EXTCON_TYPE_CHG. */
>>   	/* Properties of EXTCON_TYPE_JACK. */
>> @@ -225,6 +230,14 @@ extern int extcon_get_cable_state_(struct extcon_dev *edev, unsigned int id);
>>   extern int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id,
>>   				   bool cable_state);
>>   
>> +extern int extcon_get_cable_property(struct extcon_dev *edev, unsigned int id,
>> +				     enum extcon_property prop,
>> +				     union extcon_property_value *val);
>> +
>> +extern int extcon_set_cable_property(struct extcon_dev *edev, unsigned int id,
>> +				     enum extcon_property prop,
>> +				     union extcon_property_value val);
>> +
> ditto.
>
> Thanks,
> Chanwoo Choi
>
>
>




From shawn.lin at rock-chips.com  Tue Jul 12 18:45:43 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Wed, 13 Jul 2016 09:45:43 +0800
Subject: [PATCH v6 1/2] Documentation: bindings: add dt doc for Rockchip
 PCIe controller
In-Reply-To: <20160713013117.GA130126@google.com>
References: <1467789398-13501-1-git-send-email-shawn.lin@rock-chips.com>
 <20160707003912.GB100467@google.com>
 <59b328c9-3a21-36fa-7f28-4ba24d77fef0@rock-chips.com>
 <20160713013117.GA130126@google.com>
Message-ID: <95a7ffdd-becc-bb3f-c026-d07c691b7a53@rock-chips.com>

? 2016/7/13 9:31, Brian Norris ??:
> Hi Shawn,
>
> On Wed, Jul 13, 2016 at 09:10:15AM +0800, Shawn Lin wrote:
>> ? 2016/7/7 8:39, Brian Norris ??:
>>> On Wed, Jul 06, 2016 at 03:16:37PM +0800, Shawn Lin wrote:
>>>> +	#interrupt-cells = <1>;
>>>> +	interrupt-map-mask = <0 0 0 7>;
>>>> +	interrupt-map = <0 0 0 1 &pcie0_intc 1>,
>>>> +			<0 0 0 2 &pcie0_intc 2>,
>>>> +			<0 0 0 3 &pcie0_intc 3>,
>>>> +			<0 0 0 4 &pcie0_intc 4>;
>>>
>>> I'm a little lost on this one, so forgive my ignorance; how did you
>>> determine the last value in each entry (i.e., the 1, 2, 3, and 4 IRQ
>>> numbers for pcie0_intc)? IIUC, those are supposed to represent indeces
>>> into the IRQ status register found in the PCIe interrupt status
>>> register, and so they should be 0-based (i.e., 0, 1, 2, 3). And then
>>> you'd have:
>>>
>>> 	interrupt-map = <0 0 0 1 &pcie0_intc 0>,
>>> 			<0 0 0 2 &pcie0_intc 1>,
>>> 			<0 0 0 3 &pcie0_intc 2>,
>>> 			<0 0 0 4 &pcie0_intc 3>;
>>>
>>> But then, I never got this sub-node binding to work quite right, so I
>>> may be missing something.
>>>
>>> EDIT: ooh, I see what's going on! I'll comment on the driver as well,
>>> but it looks like you're translating the register status to a HW IRQ
>>> number with 'ffs(reg)', which yields a 1-based index. I think it is most
>>> sensible to use a 0-based index (i.e., 'ffs(reg) - 1'). Now, that only
>>> will work if you get the whole interrupt-map + interrupt-controller
>>> thing right (i.e., using a subnode for the interrupt controller) --
>>> otherwise, IRQ mapping might not work right. I suspect that's one reason
>>> the original driver writer might have used 1-based indexing in the first
>>> place.
>>
>> yes, I got it but.....what's the difference?
>
> At some level, it's a matter of preference. But when you're talking
> about the rk3399 PCIe "interrupt controller" domain, it seems that you
> should be talking about HW bits in the controller -- i.e., you have a
> 4-bit interrupt status bitfield, that we typically call [0:3]. If you
> use [1:4], then you have to remember to subtract 1 mentally when mapping
> to the actual HW bit. I believe that confusion (since bitfields normally
> count from 0) might have helped cause the infinite loop bug I noticed
> too. And I also think that counting from 0 helps clarify the fact that
> your interrupt controller indexing is an independent numbering from the
> PCI interrupt numbering, even though they happen to map 1:1.

If that's the fact of how we should numbering our index base, we should
probably start if from 5 as the layout of INTx is
PCIE_CLIENT_INT_STATUS[5:8]... ?

>
> But then, PCI INTx numbering is kinda weird already, as it starts from
> 1. So maybe it's just as valid to say our domain starts from 1 as well.
>
>> You still need to get the whole interrupt-map + interrupt-controller
>> things right and the code(ffs(reg) - 1)if applied your suggestion.
>
> Yes, of course. And I already sent you patches that do that.
>
>> Look at most of the docs for pcie bindings, I saw they also take
>> 0-base index, how about?
>
> I don't know which ones you're referring to. I see that altera-pcie.txt
> supports interrupt indeces counting from 1, but that's probably because
> they're using the same broken binding that was in your ~v3 patches
> (where the pcie node has both 'interrupt-controller' and
> 'interrupt-map', with phandles to itself), so they had no other choice.
>
> If you still think it makes more sense to count from 1, then I won't
> stop you.

I don't have a hard opinion for the index base as I think it's trivial.
So if it's more sensible to you, I will apply your suggestion.

>
> Regards,
> Brian
>
>
>


-- 
Best Regards
Shawn Lin



From briannorris at chromium.org  Tue Jul 12 19:05:31 2016
From: briannorris at chromium.org (Brian Norris)
Date: Tue, 12 Jul 2016 19:05:31 -0700
Subject: [PATCH v6 1/2] Documentation: bindings: add dt doc for Rockchip
 PCIe controller
In-Reply-To: <95a7ffdd-becc-bb3f-c026-d07c691b7a53@rock-chips.com>
References: <1467789398-13501-1-git-send-email-shawn.lin@rock-chips.com>
 <20160707003912.GB100467@google.com>
 <59b328c9-3a21-36fa-7f28-4ba24d77fef0@rock-chips.com>
 <20160713013117.GA130126@google.com>
 <95a7ffdd-becc-bb3f-c026-d07c691b7a53@rock-chips.com>
Message-ID: <20160713020531.GA14833@google.com>

Hi,

On Wed, Jul 13, 2016 at 09:45:43AM +0800, Shawn Lin wrote:
> ? 2016/7/13 9:31, Brian Norris ??:
> >On Wed, Jul 13, 2016 at 09:10:15AM +0800, Shawn Lin wrote:
> >At some level, it's a matter of preference. But when you're talking
> >about the rk3399 PCIe "interrupt controller" domain, it seems that you
> >should be talking about HW bits in the controller -- i.e., you have a
> >4-bit interrupt status bitfield, that we typically call [0:3]. If you
> >use [1:4], then you have to remember to subtract 1 mentally when mapping
> >to the actual HW bit. I believe that confusion (since bitfields normally
> >count from 0) might have helped cause the infinite loop bug I noticed
> >too. And I also think that counting from 0 helps clarify the fact that
> >your interrupt controller indexing is an independent numbering from the
> >PCI interrupt numbering, even though they happen to map 1:1.
> 
> If that's the fact of how we should numbering our index base, we should
> probably start if from 5 as the layout of INTx is
> PCIE_CLIENT_INT_STATUS[5:8]... ?

Possibly better than starting from 1, but IMO also doesn't make sense,
because the other bits aren't interrupts you want to translate on behalf
of other devices (are they?) -- they're interrupt bits consumed by the
host controller itself. (If they are possibly needed for translation,
then sure, index the entire status register and handle it in the driver,
and start the INTx mapping from 5 here.)

[...]

> >If you still think it makes more sense to count from 1, then I won't
> >stop you.
> 
> I don't have a hard opinion for the index base as I think it's trivial.

It's simple, but I think it influenced code understanding and bugginess.

> So if it's more sensible to you, I will apply your suggestion.

Well, I was just offering my opinion. I think it makes more sense, but
maybe it doesn't to you.

Brian


From cw00.choi at samsung.com  Tue Jul 12 19:05:39 2016
From: cw00.choi at samsung.com (Chanwoo Choi)
Date: Wed, 13 Jul 2016 11:05:39 +0900
Subject: [v5 PATCH 1/5] extcon: Add Type-C and DP support
In-Reply-To: <57859BDA.7080309@rock-chips.com>
References: <1468336188-565-1-git-send-email-zyw@rock-chips.com>
 <CGME20160712151003epcas1p3d60b6754866b984f943b7bea4be6d33f@epcas1p3.samsung.com>
 <1468336188-565-2-git-send-email-zyw@rock-chips.com>
 <57859532.1010002@samsung.com> <57859BDA.7080309@rock-chips.com>
Message-ID: <5785A1F3.1020703@samsung.com>

Hi Chris,

On 2016? 07? 13? 10:39, Chris Zhong wrote:
> Hi Chanwoo Choi
> 
> 
> On 07/13/2016 09:11 AM, Chanwoo Choi wrote:
>> Hi Chris,
>>
>> I'm now developing the extcon property on extcon-test branch.
>> But, it has not been completed.
>>
>> On next version, I'll remove the notification about extcon property
>> and only support the following two functions.
>> - extcon_set_cable_property()
>> - extcon_get_cable_property()
>>
>> Because the number of properties would be risen and the all properties
>> depend on the specific external connector(e.g., EXTCON_PROP_USB_VBUS
>> depend on the EXTCON_TYPE_USB type). When the specific external connector
>> is detached, extcon framework should make the property state as default state.
> 
> Yes, I think getting the notification from cable state is enough, actually I am using it like you said.

OK. 

> 
>>
>> It may send the too many notification for extcon property.
>> For example, Assume that EXTCON_TYPE_USB has the over 20 properties,
>> when EXTCON_USB or EXTCON_USB_HOST is detached, extcon should send
>> the notification for the over 20 properties and one more notificaiton
>> for state of external connector.
>>
>> So, I'll send the RFC patchset without the notification of proerty.
>>
>> Lastly,
>> I have a comment on below.
>>
>> Thanks,
>> Chanwoo Choi
>>
>> On 2016? 07? 13? 00:09, Chris Zhong wrote:
>>> Add EXTCON_DISP_DP for the Display external connector. For Type-C
>>> connector the DisplayPort can work as an Alternate Mode(VESA DisplayPort
>>> Alt Mode on USB Type-C Standard). The Type-C support both normal and
>>> flipped orientation, so add a property to extcon.
>>>
>>> Signe-off-by: Chris Zhong <zyw at rock-chips.com>
>>>
>>> Signed-off-by: Chris Zhong <zyw at rock-chips.com>
>>> ---
>>>
>>> Changes in v5:
>>> - support get property
>>>
>>> Changes in v4: None
>>> Changes in v3: None
>>> Changes in v2: None
>>> Changes in v1: None
>>>
>>>   drivers/extcon/extcon.c | 28 ++++++++++++++++++++++++++++
>>>   include/linux/extcon.h  | 13 +++++++++++++
>>>   2 files changed, 41 insertions(+)
>>>
>>> diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c
>>> index a1117db..2591b28 100644
>>> --- a/drivers/extcon/extcon.c
>>> +++ b/drivers/extcon/extcon.c
>>> @@ -157,6 +157,11 @@ struct __extcon_info {
>>>           .id = EXTCON_DISP_VGA,
>>>           .name = "VGA",
>>>       },
>>> +    [EXTCON_DISP_DP] = {
>>> +        .type = EXTCON_TYPE_DISP,
>>> +        .id = EXTCON_DISP_DP,
>>> +        .name = "DP",
>>> +    },
>>>         /* Miscellaneous external connector */
>>>       [EXTCON_DOCK] = {
>>> @@ -270,6 +275,7 @@ static bool is_extcon_property_supported(unsigned int id,
>>>           switch (prop) {
>>>           case EXTCON_PROP_USB_ID:
>>>           case EXTCON_PROP_USB_VBUS:
>>> +        case EXTCON_PROP_TYPEC_POLARITY:
>>>               return true;
>>>           default:
>>>               break;
>>> @@ -286,6 +292,8 @@ static bool is_extcon_property_supported(unsigned int id,
>>>           }
>>>       case EXTCON_TYPE_DISP:
>>>           switch (prop) {
>>> +        case EXTCON_PROP_TYPEC_POLARITY:
>> Should EXTCON_PROP_TYPEC_POLARITY property add to both EXTCON_TYPE_USB and EXTCON_TYP_DISP?
>> EXTCON_PROP_TYPEC_POLARITY is the property of USB C-type?
> 
> it is for USB Type-C, But at Display Port alt mode, both EXTCON_USB and EXTCON_USB_HOST may be detached. Does it support set the property to a detached cable, if so, I think move this case to EXTCON_USB is fine.

One external connector can set the state of one more external connector
if the one connector support the various functions.
For example, EXTCON_USB and EXTCON_CHG_USB_SDP
The existing extcon driver[1](e.g., max14577/max77693 etc.) set the state of both EXTCON_USB and EXTCON_CHG_USB_SDP connector at the same time
when usb cable is attached. Because in this case, the usb connector uses as both power supply(EXTCON_CHG_USB_SDP) and data transfer(EXTCON_USB).
[1] https://git.kernel.org/cgit/linux/kernel/git/chanwoo/extcon.git/commit/?h=extcon-next&id=8b45b6a0741678902810d7be95e635c210fbb198

So, DP Alt mode uses the USB Type-C. So, When USB C-type connector is attached for DP Alt mode,
Maybe, you can set the following two state of connector and one property:
- extcon_set_cable_state(edev, [EXTCON_USB or EXTCON_USB_HOST], 1);
- extcon_set_cable_state(edev, EXTCON_DISP_DP, 1);
- extcon_set_cable_state(edev, [EXTCON_USB or EXTCON_USB_HOST], EXTCON_PROP_TYPEC_POLARITY, 0 or 1);

Thanks,
Chanwoo Choi



From zyw at rock-chips.com  Tue Jul 12 19:54:25 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Wed, 13 Jul 2016 10:54:25 +0800
Subject: [v5 PATCH 1/5] extcon: Add Type-C and DP support
In-Reply-To: <5785A1F3.1020703@samsung.com>
References: <1468336188-565-1-git-send-email-zyw@rock-chips.com>
 <CGME20160712151003epcas1p3d60b6754866b984f943b7bea4be6d33f@epcas1p3.samsung.com>
 <1468336188-565-2-git-send-email-zyw@rock-chips.com>
 <57859532.1010002@samsung.com> <57859BDA.7080309@rock-chips.com>
 <5785A1F3.1020703@samsung.com>
Message-ID: <5785AD61.3070307@rock-chips.com>

Hi Chanwoo Choi

On 07/13/2016 10:05 AM, Chanwoo Choi wrote:
> Hi Chris,
>
> On 2016? 07? 13? 10:39, Chris Zhong wrote:
>> Hi Chanwoo Choi
>>
>>
>> On 07/13/2016 09:11 AM, Chanwoo Choi wrote:
>>> Hi Chris,
>>>
>>> I'm now developing the extcon property on extcon-test branch.
>>> But, it has not been completed.
>>>
>>> On next version, I'll remove the notification about extcon property
>>> and only support the following two functions.
>>> - extcon_set_cable_property()
>>> - extcon_get_cable_property()
>>>
>>> Because the number of properties would be risen and the all properties
>>> depend on the specific external connector(e.g., EXTCON_PROP_USB_VBUS
>>> depend on the EXTCON_TYPE_USB type). When the specific external connector
>>> is detached, extcon framework should make the property state as default state.
>> Yes, I think getting the notification from cable state is enough, actually I am using it like you said.
> OK.
>
>>> It may send the too many notification for extcon property.
>>> For example, Assume that EXTCON_TYPE_USB has the over 20 properties,
>>> when EXTCON_USB or EXTCON_USB_HOST is detached, extcon should send
>>> the notification for the over 20 properties and one more notificaiton
>>> for state of external connector.
>>>
>>> So, I'll send the RFC patchset without the notification of proerty.
>>>
>>> Lastly,
>>> I have a comment on below.
>>>
>>> Thanks,
>>> Chanwoo Choi
>>>
>>> On 2016? 07? 13? 00:09, Chris Zhong wrote:
>>>> Add EXTCON_DISP_DP for the Display external connector. For Type-C
>>>> connector the DisplayPort can work as an Alternate Mode(VESA DisplayPort
>>>> Alt Mode on USB Type-C Standard). The Type-C support both normal and
>>>> flipped orientation, so add a property to extcon.
>>>>
>>>> Signe-off-by: Chris Zhong <zyw at rock-chips.com>
>>>>
>>>> Signed-off-by: Chris Zhong <zyw at rock-chips.com>
>>>> ---
>>>>
>>>> Changes in v5:
>>>> - support get property
>>>>
>>>> Changes in v4: None
>>>> Changes in v3: None
>>>> Changes in v2: None
>>>> Changes in v1: None
>>>>
>>>>    drivers/extcon/extcon.c | 28 ++++++++++++++++++++++++++++
>>>>    include/linux/extcon.h  | 13 +++++++++++++
>>>>    2 files changed, 41 insertions(+)
>>>>
>>>> diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c
>>>> index a1117db..2591b28 100644
>>>> --- a/drivers/extcon/extcon.c
>>>> +++ b/drivers/extcon/extcon.c
>>>> @@ -157,6 +157,11 @@ struct __extcon_info {
>>>>            .id = EXTCON_DISP_VGA,
>>>>            .name = "VGA",
>>>>        },
>>>> +    [EXTCON_DISP_DP] = {
>>>> +        .type = EXTCON_TYPE_DISP,
>>>> +        .id = EXTCON_DISP_DP,
>>>> +        .name = "DP",
>>>> +    },
>>>>          /* Miscellaneous external connector */
>>>>        [EXTCON_DOCK] = {
>>>> @@ -270,6 +275,7 @@ static bool is_extcon_property_supported(unsigned int id,
>>>>            switch (prop) {
>>>>            case EXTCON_PROP_USB_ID:
>>>>            case EXTCON_PROP_USB_VBUS:
>>>> +        case EXTCON_PROP_TYPEC_POLARITY:
>>>>                return true;
>>>>            default:
>>>>                break;
>>>> @@ -286,6 +292,8 @@ static bool is_extcon_property_supported(unsigned int id,
>>>>            }
>>>>        case EXTCON_TYPE_DISP:
>>>>            switch (prop) {
>>>> +        case EXTCON_PROP_TYPEC_POLARITY:
>>> Should EXTCON_PROP_TYPEC_POLARITY property add to both EXTCON_TYPE_USB and EXTCON_TYP_DISP?
>>> EXTCON_PROP_TYPEC_POLARITY is the property of USB C-type?
>> it is for USB Type-C, But at Display Port alt mode, both EXTCON_USB and EXTCON_USB_HOST may be detached. Does it support set the property to a detached cable, if so, I think move this case to EXTCON_USB is fine.
> One external connector can set the state of one more external connector
> if the one connector support the various functions.
> For example, EXTCON_USB and EXTCON_CHG_USB_SDP
> The existing extcon driver[1](e.g., max14577/max77693 etc.) set the state of both EXTCON_USB and EXTCON_CHG_USB_SDP connector at the same time
> when usb cable is attached. Because in this case, the usb connector uses as both power supply(EXTCON_CHG_USB_SDP) and data transfer(EXTCON_USB).
> [1] https://git.kernel.org/cgit/linux/kernel/git/chanwoo/extcon.git/commit/?h=extcon-next&id=8b45b6a0741678902810d7be95e635c210fbb198
>
> So, DP Alt mode uses the USB Type-C. So, When USB C-type connector is attached for DP Alt mode,
> Maybe, you can set the following two state of connector and one property:
> - extcon_set_cable_state(edev, [EXTCON_USB or EXTCON_USB_HOST], 1);
> - extcon_set_cable_state(edev, EXTCON_DISP_DP, 1);
> - extcon_set_cable_state(edev, [EXTCON_USB or EXTCON_USB_HOST], EXTCON_PROP_TYPEC_POLARITY, 0 or 1);
>
> Thanks,
> Chanwoo Choi

There are 4 modes for Type-C DP alt mode:
1) USB host only  :

extcon_set_cable_state(edev, EXTCON_USB_HOST, 1);
extcon_set_cable_state(edev, EXTCON_USB, 0);
extcon_set_cable_state(edev, EXTCON_DISP_DP, 0);

2) USB device only

extcon_set_cable_state(edev, EXTCON_USB_HOST, 0);
extcon_set_cable_state(edev, EXTCON_USB, 1);
extcon_set_cable_state(edev, EXTCON_DISP_DP, 0);

3) DP only

extcon_set_cable_state(edev, EXTCON_USB_HOST, 0);
extcon_set_cable_state(edev, EXTCON_USB, 0);
extcon_set_cable_state(edev, EXTCON_DISP_DP, 1);

4) USB + DP

extcon_set_cable_state(edev, EXTCON_USB_HOST, 1);
extcon_set_cable_state(edev, EXTCON_USB, 0);
extcon_set_cable_state(edev, EXTCON_DISP_DP, 1);


for 3rd mode: DP only, there is only EXTCON_DISP_DP is attached, the 
EXTCON_USB_HOST
and EXTCON_USB are detached, Can I set the property into these 2 
detached cable?
or just call extcon_set_cable_state(edev, EXTCON_DISP_DP, 
EXTCON_PROP_TYPEC_POLARITY, 0 or 1);

>
>
>
>




From William.wu at rock-chips.com  Tue Jul 12 20:21:59 2016
From: William.wu at rock-chips.com (William.wu)
Date: Wed, 13 Jul 2016 11:21:59 +0800
Subject: [PATCH v6 3/5] usb: dwc3: add phyif_utmi_quirk
In-Reply-To: <4648785.SDx8KzkhqW@phil>
References: <1467860066-15142-1-git-send-email-william.wu@rock-chips.com>
 <87d1moi7g7.fsf@linux.intel.com>
 <f5daa697-3125-cfbf-7318-d4900f666362@rock-chips.com>
 <4648785.SDx8KzkhqW@phil>
Message-ID: <620c4bcc-04db-143d-c2a1-74ab5c6c51f0@rock-chips.com>

Dear Heiko,


On 2016/7/10 7:47, Heiko Stuebner wrote:
> Am Samstag, 9. Juli 2016, 11:38:00 schrieb William.wu:
>> Dear Heiko & Balbi?
>>
>> On 2016/7/8 21:29, Felipe Balbi wrote:
>>> Hi,
>>>
>>> Heiko Stuebner <heiko at sntech.de> writes:
>>>> Am Donnerstag, 7. Juli 2016, 10:54:24 schrieb William Wu:
>>>>> Add a quirk to configure the core to support the
>>>>> UTMI+ PHY with an 8- or 16-bit interface. UTMI+ PHY
>>>>> interface is hardware property, and it's platform
>>>>> dependent. Normall, the PHYIf can be configured
>>>>> during coreconsultant. But for some specific usb
>>>>> cores(e.g. rk3399 soc dwc3), the default PHYIf
>>>>> configuration value is fault, so we need to
>>>>> reconfigure it by software.
>>>>>
>>>>> And refer to the dwc3 databook, the GUSB2PHYCFG.USBTRDTIM
>>>>> must be set to the corresponding value according to
>>>>> the UTMI+ PHY interface.
>>>>>
>>>>> Signed-off-by: William Wu <william.wu at rock-chips.com>
>>>>> ---
>>>> [...]
>>>>
>>>>> diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt
>>>>> b/Documentation/devicetree/bindings/usb/dwc3.txt index
>>>>> 020b0e9..8d7317d
>>>>> 100644
>>>>> --- a/Documentation/devicetree/bindings/usb/dwc3.txt
>>>>> +++ b/Documentation/devicetree/bindings/usb/dwc3.txt
>>>>>
>>>>> @@ -42,6 +42,10 @@ Optional properties:
>>>>>     - snps,dis-u2-freeclk-exists-quirk: when set, clear the
>>>>>
>>>>> u2_freeclk_exists in GUSB2PHYCFG, specify that USB2 PHY doesn't
>>>>> provide
>>>>>
>>>>>    			a free-running PHY clock.
>>>>>
>>>>> + - snps,phyif-utmi-quirk: when set core will set phyif UTMI+
>>>>> interface.
>>>>> + - snps,phyif-utmi: the value to configure the core to support a
>>>>> UTMI+
>>>>> PHY +			with an 8- or 16-bit interface. Value 0
> select 8-bit
>>>>> +			interface, value 1 select 16-bit interface.
>>>> maybe
>>>>
>>>> 	snps,phyif-utmi-width = <8> or <16>;
>>>>
>>>> devicetree is about describing the hardware, not the things that get
>>>> written to registers :-) . The conversion from the described width to
>>>> the register value can easily be done in the driver.
>> Thanks for your suggestion:-)
>> Yes? ?snps,phyif-utmi-width = <8> or <16>? is much clearer and easier to
>> understand.
>> And I have considered the same dts property for phyif-utmi, but I have
>> no good idea about
>> the conversion from described width to the registers value for the time
>> being.
>>
>> About phyif utmi width configuration? we need to set two places in
>> GUSB2PHYCFG register?
>> according to DWC3 USB3.0 controller databook version3.00a?6.3.46
>> GUSB2PHYCFG
>>
>> --------------------------------------------------------------------------
>> -------------------- Bits   |  Name                 |     Description
>> --------------------------------------------------------------------------
>> -------------------- 13:10  |   USBTRDTIM       |     Sets the turnaround
>> time in PHY clocks.
>>               |                            |     4'h5: When the MAC
>>
>> interface is 16-bit UTMI+
>>
>>               |                            |     4'h9: When the MAC
>>
>> interface is 8-bit UTMI+/ULPI.
>> --------------------------------------------------------------------------
>> -------------------- 3        |   PHYIF                |    If UTMI+ is
>> selected, the application uses this bit to configure
>>
>>               |                            |    core to support a UTMI+
>>
>> PHY with an 8- or 16-bit interface.
>>
>>               |                            |    1'b0: 8 bits
>>               |                            |    1'b1: 16 bits
>>
>> --------------------------------------------------------------------------
>> --------------------
>>
>> And I think maybe I can try to do this:
>> change it in dts:
>>           snps,phyif-utmi-width = <8> or <16>;
>>
>> Then convert to register value like this?
>>          device_property_read_u8(dev, "snps,phyif-utmi-width",
>>                                                &phyif_utmi_width);
>>
>>          dwc->phyif_utmi = phyif_utmi_width >> 4;
>>
>>    Ater the conversion? dwc->phyif_utmi value 0 means 8 bits, value 1
>> means 16 bits,
>>    and it's easier for us to config GUSB2PHYCFG.
>>
>> Is it OK?
> or you could just store the actual width value read from the dts and make
> the core handle accordingly, making everything a bit more explicit.
>
> I guess personally I'd do something like:
>
> make dwc->phyif_utmi a regular unsigned int
>
> in probe:
>        	ret = device_property_read_u8(dev, "snps,phyif-utmi-width",
>                                                &dwc->phyif_utmi);
> 	if (ret < 0) {
> 		dwc->phyif_utmi = 0;
> 	else if (dwc->phyif_utmi != 16 && dwc->phyif_utmi != 8) {
> 		dev_err(dev, "unsupported utmi interface width %d\n",
> 			dwc->phyif_utmi);
> 		return -EINVAL;
> 	}
>
>
> when setting your GUSB2PHYCFG register:
>
>         if (dwc->phyif_utmi > 0) {
>                 reg &= ~(DWC3_GUSB2PHYCFG_PHYIF_MASK |
>                        DWC3_GUSB2PHYCFG_USBTRDTIM_MASK);
>                 usbtrdtim = (dwc->phyif_utmi == 16) ? USBTRDTIM_UTMI_16_BIT :
>                             USBTRDTIM_UTMI_8_BIT;
> 		phyif = (dwc->phyif_utmi == 16) ? 1 : 0;
>                 reg |= DWC3_GUSB2PHYCFG_PHYIF(phyif) |
>                        DWC3_GUSB2PHYCFG_USBTRDTIM(usbtrdtim) |
>         }
>
Ah yes? it seems very good to me, very explicit.
I'll upload a new patch according to your suggestion today.
Thanks a lot~:-D
>>>> Also I don't think you need two properties for this. If the
>>>> snps,phyif-utmi property is specified it indicates that you want to
>>>> manually set the width and if it is absent you want to use the IC
>>>> default. All functions reading property-values indicate if the
>>>> property is missing.
>> Ah, it seems very good to me. One dts property "snps,phyif-utmi" can help
>> to reconfig phyif utmi width. And it seems that Felipe likes it very much
>> too. :-D
> yes, but please name it snps,phyif-utmi-width :-)

Yeah, thank you very much:-)

Best Regards
         William Wu
>
>
> Heiko
>
>
>




From ykk at rock-chips.com  Tue Jul 12 20:36:27 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Wed, 13 Jul 2016 11:36:27 +0800
Subject: [PATCH v3 2/4] drm/rockchip: add an common abstracted PSR driver
In-Reply-To: <20160712123826.GU23520@phenom.ffwll.local>
References: <1467364685-21390-1-git-send-email-ykk@rock-chips.com>
 <1467364754-21674-1-git-send-email-ykk@rock-chips.com>
 <CAOw6vbKMWEEDEqw0_ba_FGO-2mOBg3uanQzk5nbnCtowWdLu9w@mail.gmail.com>
 <20160712123826.GU23520@phenom.ffwll.local>
Message-ID: <5785B73B.6000301@rock-chips.com>

Daniel,

On 07/12/2016 08:38 PM, Daniel Vetter wrote:
> On Fri, Jul 01, 2016 at 02:00:00PM -0400, Sean Paul wrote:
>> On Fri, Jul 1, 2016 at 5:19 AM, Yakir Yang <ykk at rock-chips.com> wrote:
>>> The PSR driver have exported four symbols for specific device driver:
>>> - rockchip_drm_psr_register()
>>> - rockchip_drm_psr_unregister()
>>> - rockchip_drm_psr_enable()
>>> - rockchip_drm_psr_disable()
>>> - rockchip_drm_psr_flush()
>>>
>>> Encoder driver should call the register/unregister interfaces to hook
>>> itself into common PSR driver, encoder have implement the 'psr_set'
>>> callback which use the set PSR state in hardware side.
>>>
>>> Crtc driver would call the enable/disable interfaces when vblank is
>>> enable/disable, after that the common PSR driver would call the encoder
>>> registered callback to set the PSR state.
>>>
>> This feels overly complicated. It seems like you could cut out a bunch
>> of code by just coding the psr functions into vop and
>> analogix_dp-rockchip. I suppose the only reason to keep it abstracted
>> would be if you plan on supporting psr in a different encoder or crtc
>> in rockchip, or if you're planning on moving this into drm core.
> Agreed on the layers of indirection. Also, you end up with 3 delayed
> timers in total:
> - defio timer from fbdev emulation
> - timer in this abstraction
> - delayed work in the psr backend driver
>
> I'd cut out at least the middle one.
>
> But since this seems to correctly use the ->dirty callback it gets my Ack
> either way ;-)

Aha, thanks :-D

- Yakir

> Cheers, Daniel
>
>> Perhaps others will disagree with this sentiment and this is the right
>> thing to do.
>>
>>> Fb driver would call the flush interface in 'fb->dirty' callback, this
>>> helper function would force all PSR enabled encoders to exit from PSR
>>> for 3 seconds.
>>>
>>> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
>>> ---
>>> Changes in v3:
>>> - split the psr flow into an common abstracted PSR driver
>>> - implement the 'fb->dirty' callback function (Daniel)
>>> - avoid to use notify to acqiure for vact event (Daniel)
>>> - remove psr_active() callback which introduce in v2
>>>
>>> Changes in v2: None
>>>
>>>   drivers/gpu/drm/rockchip/Makefile           |   2 +-
>>>   drivers/gpu/drm/rockchip/rockchip_drm_fb.c  |  12 ++
>>>   drivers/gpu/drm/rockchip/rockchip_drm_psr.c | 200 ++++++++++++++++++++++++++++
>>>   drivers/gpu/drm/rockchip/rockchip_drm_psr.h |  12 ++
>>>   drivers/gpu/drm/rockchip/rockchip_drm_vop.c |  24 ++++
>>>   5 files changed, 249 insertions(+), 1 deletion(-)
>>>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.c
>>>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.h
>>>
>>> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
>>> index 05d0713..9746365 100644
>>> --- a/drivers/gpu/drm/rockchip/Makefile
>>> +++ b/drivers/gpu/drm/rockchip/Makefile
>>> @@ -3,7 +3,7 @@
>>>   # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
>>>
>>>   rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
>>> -               rockchip_drm_gem.o rockchip_drm_vop.o
>>> +               rockchip_drm_gem.o rockchip_drm_psr.o rockchip_drm_vop.o
>>>   rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
>>>
>>>   obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
>>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>>> index 20f12bc..0fec18f 100644
>>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>>> @@ -21,6 +21,7 @@
>>>
>>>   #include "rockchip_drm_drv.h"
>>>   #include "rockchip_drm_gem.h"
>>> +#include "rockchip_drm_psr.h"
>>>
>>>   #define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
>>>
>>> @@ -66,9 +67,20 @@ static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
>>>                                       rockchip_fb->obj[0], handle);
>>>   }
>>>
>>> +static int rockchip_drm_fb_dirty(struct drm_framebuffer *fb,
>>> +                                struct drm_file *file,
>>> +                                unsigned int flags, unsigned int color,
>>> +                                struct drm_clip_rect *clips,
>>> +                                unsigned int num_clips)
>>> +{
>>> +       rockchip_drm_psr_flush();
>>> +       return 0;
>>> +}
>>> +
>>>   static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
>>>          .destroy        = rockchip_drm_fb_destroy,
>>>          .create_handle  = rockchip_drm_fb_create_handle,
>>> +       .dirty          = rockchip_drm_fb_dirty,
>>>   };
>>>
>>>   static struct rockchip_drm_fb *
>>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
>>> new file mode 100644
>>> index 0000000..c044443
>>> --- /dev/null
>>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
>>> @@ -0,0 +1,200 @@
>>> +#include <drm/drm_crtc_helper.h>
>>> +
>>> +#include "rockchip_drm_psr.h"
>>> +
>>> +#define PSR_FLUSH_TIMEOUT      msecs_to_jiffies(3000) /* 3 seconds */
>>> +
>>> +static LIST_HEAD(psr_list);
>>> +static DEFINE_MUTEX(psr_list_mutex);
>> I'm not crazy about these globals. Perhaps you can initialize them
>> with the rockchip driver and tuck them in a driver-level struct
>> (rockchip_drm_private or something).
>>
>>
>>> +
>>> +enum psr_state {
>>> +       PSR_FLUSH,
>>> +       PSR_ENABLE,
>>> +       PSR_DISABLE,
>>> +};
>>> +
>>> +struct psr_drv {
>>> +       struct list_head list;
>>> +       enum psr_state state;
>>> +       struct mutex state_mutex;
>>> +
>>> +       struct timer_list flush_timer;
>>> +
>>> +       struct drm_encoder *encoder;
>>> +       int (*set)(struct drm_encoder *encoder, bool enable);
>>> +};
>>> +
>>> +static struct psr_drv *find_psr_by_crtc(struct drm_crtc *crtc)
>>> +{
>>> +       struct psr_drv *psr;
>>> +
>>> +       mutex_lock(&psr_list_mutex);
>>> +       list_for_each_entry(psr, &psr_list, list) {
>>> +               if (psr->encoder->crtc == crtc) {
>>> +                       mutex_unlock(&psr_list_mutex);
>>> +                       return psr;
>>> +               }
>>> +       }
>>> +       mutex_unlock(&psr_list_mutex);
>>> +
>>> +       return ERR_PTR(-ENODEV);
>>> +}
>>> +
>>> +static void psr_enable(struct psr_drv *psr)
>>> +{
>>> +       if (psr->state == PSR_ENABLE)
>>> +               return;
>> Should you be worried about races by accessing this outside of the state mutex?
>>
>>> +
>>> +       mutex_lock(&psr->state_mutex);
>>> +       psr->state = PSR_ENABLE;
>>> +       psr->set(psr->encoder, true);
>>> +       mutex_unlock(&psr->state_mutex);
>>> +}
>>> +
>>> +static void psr_disable(struct psr_drv *psr)
>>> +{
>>> +       if (psr->state == PSR_DISABLE)
>>> +               return;
>>> +
>>> +       mutex_lock(&psr->state_mutex);
>>> +       psr->state = PSR_DISABLE;
>>> +       psr->set(psr->encoder, false);
>>> +       mutex_unlock(&psr->state_mutex);
>>> +}
>>> +
>>> +static void psr_flush_handler(unsigned long data)
>>> +{
>>> +       struct psr_drv *psr = (struct psr_drv *)data;
>>> +
>>> +       if (!psr || psr->state != PSR_FLUSH)
>>> +               return;
>>> +
>>> +       psr_enable(psr);
>>> +}
>>> +
>>> +/**
>>> + * rockchip_drm_psr_enable - enable the encoder PSR which bind to given CRTC
>>> + * @crtc: CRTC to obtain the PSR encoder
>>> + *
>>> + * Returns:
>>> + * Zero on success, negative errno on failure.
>>> + */
>>> +int rockchip_drm_psr_enable(struct drm_crtc *crtc)
>>> +{
>>> +       struct psr_drv *psr = find_psr_by_crtc(crtc);
>>> +
>>> +       if (IS_ERR(psr))
>>> +               return PTR_ERR(psr);
>>> +
>>> +       psr_enable(psr);
>>> +       return 0;
>>> +}
>>> +EXPORT_SYMBOL(rockchip_drm_psr_enable);
>>> +
>>> +/**
>>> + * rockchip_drm_psr_disable - disable the encoder PSR which bind to given CRTC
>>> + * @crtc: CRTC to obtain the PSR encoder
>>> + *
>>> + * Returns:
>>> + * Zero on success, negative errno on failure.
>>> + */
>>> +int rockchip_drm_psr_disable(struct drm_crtc *crtc)
>>> +{
>>> +       struct psr_drv *psr = find_psr_by_crtc(crtc);
>>> +
>>> +       if (IS_ERR(psr))
>>> +               return PTR_ERR(psr);
>>> +
>>> +       psr_disable(psr);
>>> +       return 0;
>>> +}
>>> +EXPORT_SYMBOL(rockchip_drm_psr_disable);
>>> +
>>> +/**
>>> + * rockchip_drm_psr_flush - force to flush all registered PSR encoders
>>> + *
>>> + * Disable the PSR function for all registered encoders, and then enable the
>>> + * PSR function back after 5 second. If encoder PSR state have been changed
>> s/5 second/PSR_FLUSH_TIMEOUT/
>>
>>> + * during flush time, then keep the state no change after flush timeout.
>>> + *
>>> + * Returns:
>>> + * Zero on success, negative errno on failure.
>>> + */
>>> +void rockchip_drm_psr_flush(void)
>>> +{
>>> +       struct psr_drv *psr;
>>> +
>>> +       mutex_lock(&psr_list_mutex);
>>> +       list_for_each_entry(psr, &psr_list, list) {
>>> +               if (psr->state == PSR_DISABLE)
>>> +                       continue;
>>> +
>>> +               mod_timer(&psr->flush_timer,
>>> +                         round_jiffies_up(jiffies + PSR_FLUSH_TIMEOUT));
>>> +
>>> +               psr_disable(psr);
>>> +               psr->state = PSR_FLUSH;
>> This is set outside of state_mutex, is that intentional?
>>
>>> +       }
>>> +       mutex_unlock(&psr_list_mutex);
>>> +}
>>> +EXPORT_SYMBOL(rockchip_drm_psr_flush);
>>> +
>>> +/**
>>> + * rockchip_drm_psr_register - register encoder to psr driver
>>> + * @encoder: encoder that obtain the PSR function
>>> + * @psr_set: call back to set PSR state
>>> + *
>>> + * Returns:
>>> + * Zero on success, negative errno on failure.
>>> + */
>>> +int rockchip_drm_psr_register(struct drm_encoder *encoder,
>>> +                             int (*psr_set)(struct drm_encoder *, bool enable))
>>> +{
>>> +       struct psr_drv *psr;
>>> +
>>> +       if (!encoder || !psr_set)
>>> +               return -EINVAL;
>>> +
>>> +       psr = kzalloc(sizeof(struct psr_drv), GFP_KERNEL);
>>> +       if (!psr)
>>> +               return -ENOMEM;
>>> +
>>> +       setup_timer(&psr->flush_timer, psr_flush_handler, (unsigned long)psr);
>>> +
>>> +       mutex_init(&psr->state_mutex);
>>> +
>>> +       psr->state = PSR_DISABLE;
>>> +       psr->encoder = encoder;
>>> +       psr->set = psr_set;
>>> +
>>> +       mutex_lock(&psr_list_mutex);
>>> +       list_add_tail(&psr->list, &psr_list);
>>> +       mutex_unlock(&psr_list_mutex);
>>> +
>>> +       return 0;
>>> +}
>>> +EXPORT_SYMBOL(rockchip_drm_psr_register);
>>> +
>>> +/**
>>> + * rockchip_drm_psr_unregister - unregister encoder to psr driver
>>> + * @encoder: encoder that obtain the PSR function
>>> + * @psr_set: call back to set PSR state
>>> + *
>>> + * Returns:
>>> + * Zero on success, negative errno on failure.
>>> + */
>>> +void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
>>> +{
>>> +       struct psr_drv *psr;
>>> +
>>> +       mutex_lock(&psr_list_mutex);
>>> +       list_for_each_entry(psr, &psr_list, list) {
>>> +               if (psr->encoder == encoder) {
>>> +                       del_timer(&psr->flush_timer);
>>> +                       list_del(&psr->list);
>>> +                       kfree(psr);
>>> +               }
>>> +       }
>>> +       mutex_unlock(&psr_list_mutex);
>>> +}
>>> +EXPORT_SYMBOL(rockchip_drm_psr_unregister);
>>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.h b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
>>> new file mode 100644
>>> index 0000000..622f605
>>> --- /dev/null
>>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
>>> @@ -0,0 +1,12 @@
>>> +#ifndef __ROCKCHIP_DRM_PSR___
>>> +#define __ROCKCHIP_DRM_PSR___
>>> +
>>> +void rockchip_drm_psr_flush(void);
>>> +int rockchip_drm_psr_enable(struct drm_crtc *crtc);
>>> +int rockchip_drm_psr_disable(struct drm_crtc *crtc);
>>> +
>>> +int rockchip_drm_psr_register(struct drm_encoder *encoder,
>>> +                           int (*psr_set)(struct drm_encoder *, bool enable));
>>> +void rockchip_drm_psr_unregister(struct drm_encoder *encoder);
>>> +
>>> +#endif /* __ROCKCHIP_DRM_PSR__ */
>>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>>> index cd3cac5..3c6dfc5 100644
>>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>>> @@ -34,6 +34,7 @@
>>>   #include "rockchip_drm_drv.h"
>>>   #include "rockchip_drm_gem.h"
>>>   #include "rockchip_drm_fb.h"
>>> +#include "rockchip_drm_psr.h"
>>>   #include "rockchip_drm_vop.h"
>>>
>>>   #define __REG_SET_RELAXED(x, off, mask, shift, v, write_mask) \
>>> @@ -121,6 +122,9 @@ struct vop {
>>>          /* protected by dev->event_lock */
>>>          struct drm_pending_vblank_event *event;
>>>
>>> +       bool psr_enabled;
>>> +       struct delayed_work psr_work;
>>> +
>>>          struct completion line_flag_completion;
>>>
>>>          const struct vop_data *data;
>>> @@ -911,6 +915,16 @@ static const struct drm_plane_funcs vop_plane_funcs = {
>>>          .atomic_destroy_state = vop_atomic_plane_destroy_state,
>>>   };
>>>
>>> +static void vop_psr_work(struct work_struct *work)
>>> +{
>>> +       struct vop *vop = container_of(work, typeof(*vop), psr_work.work);
>>> +
>>> +       if (vop->psr_enabled)
>>> +               rockchip_drm_psr_enable(&vop->crtc);
>>> +       else
>>> +               rockchip_drm_psr_disable(&vop->crtc);
>>> +}
>>> +
>>>   static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
>>>   {
>>>          struct vop *vop = to_vop(crtc);
>>> @@ -925,6 +939,9 @@ static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
>>>
>>>          spin_unlock_irqrestore(&vop->irq_lock, flags);
>>>
>>> +       vop->psr_enabled = false;
>>> +       schedule_delayed_work(&vop->psr_work, msecs_to_jiffies(10));
>>> +
>>>          return 0;
>>>   }
>>>
>>> @@ -941,6 +958,9 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc)
>>>          VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0);
>>>
>>>          spin_unlock_irqrestore(&vop->irq_lock, flags);
>>> +
>>> +       vop->psr_enabled = true;
>>> +       schedule_delayed_work(&vop->psr_work, msecs_to_jiffies(10));
>>>   }
>>>
>>>   static void vop_crtc_wait_for_update(struct drm_crtc *crtc)
>>> @@ -1582,6 +1602,10 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
>>>                  return ret;
>>>
>>>          pm_runtime_enable(&pdev->dev);
>>> +
>>> +       vop->psr_enabled = false;
>>> +       INIT_DELAYED_WORK(&vop->psr_work, vop_psr_work);
>>> +
>>>          return 0;
>>>   }
>>>
>>> --
>>> 1.9.1
>>>
>>>




From William.wu at rock-chips.com  Tue Jul 12 20:39:16 2016
From: William.wu at rock-chips.com (William.wu)
Date: Wed, 13 Jul 2016 11:39:16 +0800
Subject: [PATCH v6 3/5] usb: dwc3: add phyif_utmi_quirk
In-Reply-To: <20160711145447.GA16636@rob-hp-laptop>
References: <1467860066-15142-1-git-send-email-william.wu@rock-chips.com>
 <1467860066-15142-4-git-send-email-william.wu@rock-chips.com>
 <2213342.0Nx5tnEW8p@phil> <20160711145447.GA16636@rob-hp-laptop>
Message-ID: <77a15788-a460-7f59-cb52-88ce012f9eca@rock-chips.com>

Dear Rob,


On 2016/7/11 22:54, Rob Herring wrote:
> On Fri, Jul 08, 2016 at 02:33:09PM +0200, Heiko Stuebner wrote:
>> Hi William,
>>
>> Am Donnerstag, 7. Juli 2016, 10:54:24 schrieb William Wu:
>>> Add a quirk to configure the core to support the
>>> UTMI+ PHY with an 8- or 16-bit interface. UTMI+ PHY
>>> interface is hardware property, and it's platform
>>> dependent. Normall, the PHYIf can be configured
>>> during coreconsultant. But for some specific usb
>>> cores(e.g. rk3399 soc dwc3), the default PHYIf
>>> configuration value is fault, so we need to
>>> reconfigure it by software.
>>>
>>> And refer to the dwc3 databook, the GUSB2PHYCFG.USBTRDTIM
>>> must be set to the corresponding value according to
>>> the UTMI+ PHY interface.
>>>
>>> Signed-off-by: William Wu <william.wu at rock-chips.com>
>>> ---
>> [...]
>>> diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt
>>> b/Documentation/devicetree/bindings/usb/dwc3.txt index 020b0e9..8d7317d
>>> 100644
>>> --- a/Documentation/devicetree/bindings/usb/dwc3.txt
>>> +++ b/Documentation/devicetree/bindings/usb/dwc3.txt
>>> @@ -42,6 +42,10 @@ Optional properties:
>>>    - snps,dis-u2-freeclk-exists-quirk: when set, clear the
>>> u2_freeclk_exists in GUSB2PHYCFG, specify that USB2 PHY doesn't provide
>>>   			a free-running PHY clock.
>>> + - snps,phyif-utmi-quirk: when set core will set phyif UTMI+ interface.
>>> + - snps,phyif-utmi: the value to configure the core to support a UTMI+
>>> PHY +			with an 8- or 16-bit interface. Value 0 select 8-bit
>>> +			interface, value 1 select 16-bit interface.
>> maybe
>> 	snps,phyif-utmi-width = <8> or <16>;
> Seems like this could be common. Any other bindings have something
> similar already? If not "utmi-width" is fine.

It seems that there's not any dwc3 binding similar to this.
So I prefer to use ?utmi-width?. :-)

>
>> devicetree is about describing the hardware, not the things that get written
>> to registers :-) . The conversion from the described width to the register
>> value can easily be done in the driver.
>>
>>
>> Also I don't think you need two properties for this. If the snps,phyif-utmi
>> property is specified it indicates that you want to manually set the width
>> and if it is absent you want to use the IC default. All functions reading
>> property-values indicate if the property is missing.
> Agreed.
>
> Rob
>
>
>




From William.wu at rock-chips.com  Tue Jul 12 21:02:29 2016
From: William.wu at rock-chips.com (William.wu)
Date: Wed, 13 Jul 2016 12:02:29 +0800
Subject: [PATCH v6 3/5] usb: dwc3: add phyif_utmi_quirk
In-Reply-To: <20160711145854.GB16636@rob-hp-laptop>
References: <1467860066-15142-1-git-send-email-william.wu@rock-chips.com>
 <1467860066-15142-4-git-send-email-william.wu@rock-chips.com>
 <20160711145854.GB16636@rob-hp-laptop>
Message-ID: <8c62e88e-aa13-a978-1b92-2fe942671c04@rock-chips.com>

Dear Rob,


On 2016/7/11 22:58, Rob Herring wrote:
> On Thu, Jul 07, 2016 at 10:54:24AM +0800, William Wu wrote:
>> Add a quirk to configure the core to support the
>> UTMI+ PHY with an 8- or 16-bit interface. UTMI+ PHY
>> interface is hardware property, and it's platform
>> dependent. Normall, the PHYIf can be configured
> s/Normall/Normally/
Yeah?I'll fix it.:-)
> s/PHYIf/PHYIF/
Refer to DWC3 controller databook, "PHY Interface" called "PHYIf",
so I describe "PHYIf" here. However? "PHYIF?seems more the norm,
I'll fix it.:-)
>
>> during coreconsultant. But for some specific usb
> s/usb/USB/

Thanks, I'll fix it.:-)

>
>> cores(e.g. rk3399 soc dwc3), the default PHYIf
>> configuration value is fault, so we need to
>> reconfigure it by software.
>>
>> And refer to the dwc3 databook, the GUSB2PHYCFG.USBTRDTIM
> s/dwc3/DWC3/
Thanks, I'll fix it too.
>
>> must be set to the corresponding value according to
>> the UTMI+ PHY interface.
> And wrap your lines at 70-74 characters.

Thanks for your suggestion, I'll pay attention to this problem next 
patch.:-)

Best Regards?
         William Wu
>
> Rob
>
>
>




From William.wu at rock-chips.com  Tue Jul 12 21:05:45 2016
From: William.wu at rock-chips.com (William.wu)
Date: Wed, 13 Jul 2016 12:05:45 +0800
Subject: [PATCH v6 5/5] usb: dwc3: rockchip: add devicetree bindings
 documentation
In-Reply-To: <20160711151332.GA28672@rob-hp-laptop>
References: <1467860066-15142-1-git-send-email-william.wu@rock-chips.com>
 <1467860324-15341-1-git-send-email-william.wu@rock-chips.com>
 <20160711151332.GA28672@rob-hp-laptop>
Message-ID: <c2c2ee38-d420-6ddb-fa31-59cc00e58fcc@rock-chips.com>

Dear Rob,


On 2016/7/11 23:13, Rob Herring wrote:
> On Thu, Jul 07, 2016 at 10:58:44AM +0800, William Wu wrote:
>> This patch adds the devicetree documentation required for Rockchip
>> USB3.0 core wrapper consisting of USB3.0 IP from Synopsys.
>>
>> It supports DRD mode, and could operate in device mode (SS, HS, FS)
>> and host mode (SS, HS, FS, LS).
>>
>> Signed-off-by: William Wu <william.wu at rock-chips.com>
>> ---
>> Changes in v6:
>> - rename bus_clk, and add usbdrd3_1 node as an example (Heiko)
>>
>> Changes in v5:
>> - rename clock-names, and remove unnecessary clocks (Heiko)
>>
>> Changes in v4:
>> - modify commit log, and add phy documentation location (Sergei)
>>
>> Changes in v3:
>> - add dwc3 address (balbi)
>>
>> Changes in v2:
>> - add rockchip,dwc3.txt to Documentation/devicetree/bindings/ (balbi, Brian)
>>
>>   .../devicetree/bindings/usb/rockchip,dwc3.txt      | 59 ++++++++++++++++++++++
>>   1 file changed, 59 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/usb/rockchip,dwc3.txt
> Acked-by: Rob Herring <robh at kernel.org>

Thanks a lot. I'll add Acked-by next patch.

>
>
>




From ykk at rock-chips.com  Wed Jul 13 00:30:17 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Wed, 13 Jul 2016 15:30:17 +0800
Subject: [PATCH v1 4/6] drm/rockchip: dw_hdmi: add RK3399 HDMI support
In-Reply-To: <1468237912.3048.26.camel@pengutronix.de>
References: <1468235079-29152-1-git-send-email-ykk@rock-chips.com>
 <1468235146-29575-1-git-send-email-ykk@rock-chips.com>
 <1468237912.3048.26.camel@pengutronix.de>
Message-ID: <5785EE09.3070406@rock-chips.com>

Philipp,

On 07/11/2016 07:51 PM, Philipp Zabel wrote:
> Am Montag, den 11.07.2016, 19:05 +0800 schrieb Yakir Yang:
>> RK3399 and RK3288 shared the same HDMI IP controller, only some light
>> difference with GRF configure.
>>
>> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
> Reviewed-by: Philipp Zabel <p.zabel at pengutronix.de>

Thanks for your fast respond :-D
- Yakir

> regards
> Philipp
>
>
>
>




From ykk at rock-chips.com  Wed Jul 13 00:45:06 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Wed, 13 Jul 2016 15:45:06 +0800
Subject: [PATCH v1 5/6] drm/rockchip: dw_hdmi: introduce the VPLL clock
 setting
In-Reply-To: <3636683.EuFECY2EQB@diego>
References: <1468235079-29152-1-git-send-email-ykk@rock-chips.com>
 <1468235149-29625-1-git-send-email-ykk@rock-chips.com>
 <3636683.EuFECY2EQB@diego>
Message-ID: <5785F182.5070901@rock-chips.com>

Heiko,

On 07/12/2016 10:27 PM, Heiko St?bner wrote:
> Hi Yakir,
>
> Am Montag, 11. Juli 2016, 19:05:49 schrieb Yakir Yang:
>> For RK3399 HDMI, there is an external clock need for HDMI PHY,
>> and it should keep the same clock rate with VOP DCLK.
>>
>> VPLL have supported the clock for HDMI PHY, but there is no
>> clock divider bewteen VPLL and HDMI PHY. So we need to set the
>> VPLL rate manually in HDMI driver.
> I don't think reserving the vpll for the hdmi at all times is the right way to
> go.
>
> While I do agree that reserving the VPLL (and NPLL on the rk3288) for graphics
> use looks like the right way, I think the core Rockchip drm driver should be
> responsible for managing it and also for deciding which output encoder gets to
> use it.
> While true that on the Chromebook device-types the edp is static and hdmi
> needs the broad range of dynamic frequencies, this is not necessarily the case
> for all future device types and/or socs.
>
> In the other thread we discussed adding that as rockchip,dclk-pll = <&...>; to
> the base display-subsystem node, Doug didn't manage to find time to respond yet
> though - and is on vacation right now.

Great idea. Let a separate drm-pll driver to maintain all connector's 
clock requirement.

> I still believe that would be the best solution :-) .
>
>
> That property could list 1 or even 2 plls, depending on the soc or board
> layout - maybe someone frees up the cpll in some special layout or something.
> If none are listed, then the drm driver would need to cope with its available
> clocks.
>
> Implementation-wise you could even still keep your shortcut until we encounter
> a device with different , let the drm use the pll for hdmi only until someone
> comes up with a better concept, but still the binding should be correct and
> versatile from the start.

Someone should start to prepare the drm core pll driver, it's great idea.

BR,
- Yakir

>
> Heiko
>
>> ---
>>   .../bindings/display/rockchip/dw_hdmi-rockchip.txt |  3 ++-
>>   drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c        | 25
>> +++++++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-)
>>
>> diff --git
>> a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
>> b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
>> index 4e573d2..4e23ca4 100644
>> ---
>> a/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
>> +++
>> b/Documentation/devicetree/bindings/display/rockchip/dw_hdmi-rockchip.txt
>> @@ -17,7 +17,8 @@ Required properties:
>>
>>   Optional properties
>>   - ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing
>> -- clocks, clock-names: phandle to the HDMI CEC clock, name should be "cec"
>> +- clocks, clock-names: phandle to the HDMI CEC clock, name should be "cec",
>> +		       phandle to the VPLL clock, name should be "vpll".
>>
>>   Example:
>>   hdmi: hdmi at ff980000 {
>> diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
>> b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index 329099b..701bb73 100644
>> --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
>> +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
>> @@ -7,10 +7,12 @@
>>    * (at your option) any later version.
>>    */
>>
>> +#include <linux/clk.h>
>> +#include <linux/mfd/syscon.h>
>>   #include <linux/module.h>
>>   #include <linux/platform_device.h>
>> -#include <linux/mfd/syscon.h>
>>   #include <linux/regmap.h>
>> +
>>   #include <drm/drm_of.h>
>>   #include <drm/drmP.h>
>>   #include <drm/drm_crtc_helper.h>
>> @@ -33,6 +35,7 @@ struct rockchip_hdmi {
>>   	struct regmap *regmap;
>>   	struct drm_encoder encoder;
>>   	enum dw_hdmi_devtype dev_type;
>> +	struct clk *vpll_clk;
>>   };
>>
>>   #define to_rockchip_hdmi(x)	container_of(x, struct rockchip_hdmi, x)
>> @@ -145,6 +148,7 @@ static const struct dw_hdmi_phy_config
>> rockchip_phy_config[] = { static int rockchip_hdmi_parse_dt(struct
>> rockchip_hdmi *hdmi)
>>   {
>>   	struct device_node *np = hdmi->dev->of_node;
>> +	int ret;
>>
>>   	hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
>>   	if (IS_ERR(hdmi->regmap)) {
>> @@ -152,6 +156,22 @@ static int rockchip_hdmi_parse_dt(struct rockchip_hdmi
>> *hdmi) return PTR_ERR(hdmi->regmap);
>>   	}
>>
>> +	hdmi->vpll_clk = devm_clk_get(hdmi->dev, "vpll");
>> +	if (PTR_ERR(hdmi->vpll_clk) == -ENOENT) {
>> +		hdmi->vpll_clk = NULL;
>> +	} else if (PTR_ERR(hdmi->vpll_clk) == -EPROBE_DEFER) {
>> +		return -EPROBE_DEFER;
>> +	} else if (IS_ERR(hdmi->vpll_clk)) {
>> +		dev_err(hdmi->dev, "failed to get grf clock\n");
>> +		return PTR_ERR(hdmi->vpll_clk);
>> +	}
>> +
>> +	ret = clk_prepare_enable(hdmi->vpll_clk);
>> +	if (ret) {
>> +		dev_err(hdmi->dev, "Failed to enable HDMI vpll: %d\n", ret);
>> +		return ret;
>> +	}
>> +
>>   	return 0;
>>   }
>>
>> @@ -194,6 +214,9 @@ static void dw_hdmi_rockchip_encoder_mode_set(struct
>> drm_encoder *encoder, struct drm_display_mode *mode,
>>   					      struct drm_display_mode *adj_mode)
>>   {
>> +	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
>> +
>> +	clk_set_rate(hdmi->vpll_clk, adj_mode->clock * 1000);
>>   }
>>
>>   static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder)
>
>
>




From k.kozlowski at samsung.com  Wed Jul 13 01:39:27 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Wed, 13 Jul 2016 10:39:27 +0200
Subject: [PATCH v6 00/46] dma-mapping: Use unsigned long for dma_attrs
Message-ID: <1468399167-28083-1-git-send-email-k.kozlowski@samsung.com>

Hi,

The fifth version of this patchset was merged by Andrew Morton
few days ago.  It was rebased on v4.7-rc5 so it missed some ongoing
changes.

This is just rebase on next-20160713.


For easier testing the patchset is available here:
repo:   https://github.com/krzk/linux
branch: for-next/dma-attrs-const-v6


Changes since v5
================
1. New patches:
   1/46:  [media] mtk-vcodec: Remove unused dma_attrs
   44/46: remoteproc: qcom: Use unsigned long for dma_attrs
2. 19/46: rebased on next, some more changes inside
3. Added accumulated acks: Marek Szyprowski, Richard Kuo,
   Konrad Rzeszutek Wilk, Daniel Vetter and Joerg Roedel.


Changes since v4
================
1. Collect some acks. Still need more.
2. Minor fixes pointed by Robin Murphy.
3. Applied changes from Bart Van Assche's comment.
4. More tests and builds (using https://www.kernel.org/pub/tools/crosstool/).


Changes since v3
================
1. Collect some acks.
2. Drop wrong patch 1/45 ("powerpc: dma-mapping: Don't hard-code
   the value of DMA_ATTR_WEAK_ORDERING").
3. Minor fix pointed out by Michael Ellerman.


Changes since v2
================
1. Follow Christoph Hellwig's comments (don't use BIT add
   documentation, remove dma_get_attr).


Rationale
=========
The dma-mapping core and the implementations do not change the
DMA attributes passed by pointer.  Thus the pointer can point to const
data.  However the attributes do not have to be a bitfield. Instead
unsigned long will do fine:

1. This is just simpler.  Both in terms of reading the code and setting
   attributes.  Instead of initializing local attributes on the stack
   and passing pointer to it to dma_set_attr(), just set the bits.

2. It brings safeness and checking for const correctness because the
   attributes are passed by value.


Best regards,
Krzysztof


Krzysztof Kozlowski (46):
  [media] mtk-vcodec: Remove unused dma_attrs
  dma-mapping: Use unsigned long for dma_attrs
  alpha: dma-mapping: Use unsigned long for dma_attrs
  arc: dma-mapping: Use unsigned long for dma_attrs
  ARM: dma-mapping: Use unsigned long for dma_attrs
  arm64: dma-mapping: Use unsigned long for dma_attrs
  avr32: dma-mapping: Use unsigned long for dma_attrs
  blackfin: dma-mapping: Use unsigned long for dma_attrs
  c6x: dma-mapping: Use unsigned long for dma_attrs
  cris: dma-mapping: Use unsigned long for dma_attrs
  frv: dma-mapping: Use unsigned long for dma_attrs
  drm/exynos: dma-mapping: Use unsigned long for dma_attrs
  drm/mediatek: dma-mapping: Use unsigned long for dma_attrs
  drm/msm: dma-mapping: Use unsigned long for dma_attrs
  drm/nouveau: dma-mapping: Use unsigned long for dma_attrs
  drm/rockship: dma-mapping: Use unsigned long for dma_attrs
  infiniband: dma-mapping: Use unsigned long for dma_attrs
  iommu: dma-mapping: Use unsigned long for dma_attrs
  [media] dma-mapping: Use unsigned long for dma_attrs
  xen: dma-mapping: Use unsigned long for dma_attrs
  swiotlb: dma-mapping: Use unsigned long for dma_attrs
  powerpc: dma-mapping: Use unsigned long for dma_attrs
  video: dma-mapping: Use unsigned long for dma_attrs
  x86: dma-mapping: Use unsigned long for dma_attrs
  iommu: intel: dma-mapping: Use unsigned long for dma_attrs
  h8300: dma-mapping: Use unsigned long for dma_attrs
  hexagon: dma-mapping: Use unsigned long for dma_attrs
  ia64: dma-mapping: Use unsigned long for dma_attrs
  m68k: dma-mapping: Use unsigned long for dma_attrs
  metag: dma-mapping: Use unsigned long for dma_attrs
  microblaze: dma-mapping: Use unsigned long for dma_attrs
  mips: dma-mapping: Use unsigned long for dma_attrs
  mn10300: dma-mapping: Use unsigned long for dma_attrs
  nios2: dma-mapping: Use unsigned long for dma_attrs
  openrisc: dma-mapping: Use unsigned long for dma_attrs
  parisc: dma-mapping: Use unsigned long for dma_attrs
  misc: mic: dma-mapping: Use unsigned long for dma_attrs
  s390: dma-mapping: Use unsigned long for dma_attrs
  sh: dma-mapping: Use unsigned long for dma_attrs
  sparc: dma-mapping: Use unsigned long for dma_attrs
  tile: dma-mapping: Use unsigned long for dma_attrs
  unicore32: dma-mapping: Use unsigned long for dma_attrs
  xtensa: dma-mapping: Use unsigned long for dma_attrs
  remoteproc: qcom: Use unsigned long for dma_attrs
  dma-mapping: Remove dma_get_attr
  dma-mapping: Document the DMA attributes next to the declaration

 Documentation/DMA-API.txt                          |  33 +++---
 Documentation/DMA-attributes.txt                   |   2 +-
 arch/alpha/include/asm/dma-mapping.h               |   2 -
 arch/alpha/kernel/pci-noop.c                       |   2 +-
 arch/alpha/kernel/pci_iommu.c                      |  12 +-
 arch/arc/mm/dma.c                                  |  12 +-
 arch/arm/common/dmabounce.c                        |   4 +-
 arch/arm/include/asm/dma-mapping.h                 |  13 +--
 arch/arm/include/asm/xen/page-coherent.h           |  16 +--
 arch/arm/mm/dma-mapping.c                          | 117 +++++++++----------
 arch/arm/xen/mm.c                                  |   8 +-
 arch/arm64/mm/dma-mapping.c                        |  66 +++++------
 arch/avr32/mm/dma-coherent.c                       |  12 +-
 arch/blackfin/kernel/dma-mapping.c                 |   8 +-
 arch/c6x/include/asm/dma-mapping.h                 |   4 +-
 arch/c6x/kernel/dma.c                              |   9 +-
 arch/c6x/mm/dma-coherent.c                         |   4 +-
 arch/cris/arch-v32/drivers/pci/dma.c               |   9 +-
 arch/frv/mb93090-mb00/pci-dma-nommu.c              |   8 +-
 arch/frv/mb93090-mb00/pci-dma.c                    |   9 +-
 arch/h8300/kernel/dma.c                            |   8 +-
 arch/hexagon/include/asm/dma-mapping.h             |   1 -
 arch/hexagon/kernel/dma.c                          |   8 +-
 arch/ia64/hp/common/sba_iommu.c                    |  22 ++--
 arch/ia64/include/asm/machvec.h                    |   1 -
 arch/ia64/kernel/pci-swiotlb.c                     |   4 +-
 arch/ia64/sn/pci/pci_dma.c                         |  22 ++--
 arch/m68k/kernel/dma.c                             |  12 +-
 arch/metag/kernel/dma.c                            |  16 +--
 arch/microblaze/include/asm/dma-mapping.h          |   1 -
 arch/microblaze/kernel/dma.c                       |  12 +-
 arch/mips/cavium-octeon/dma-octeon.c               |   8 +-
 arch/mips/loongson64/common/dma-swiotlb.c          |  10 +-
 arch/mips/mm/dma-default.c                         |  20 ++--
 arch/mips/netlogic/common/nlm-dma.c                |   4 +-
 arch/mn10300/mm/dma-alloc.c                        |   8 +-
 arch/nios2/mm/dma-mapping.c                        |  12 +-
 arch/openrisc/kernel/dma.c                         |  21 ++--
 arch/parisc/kernel/pci-dma.c                       |  18 +--
 arch/powerpc/include/asm/dma-mapping.h             |   7 +-
 arch/powerpc/include/asm/iommu.h                   |  10 +-
 arch/powerpc/kernel/dma-iommu.c                    |  12 +-
 arch/powerpc/kernel/dma.c                          |  18 +--
 arch/powerpc/kernel/ibmebus.c                      |  12 +-
 arch/powerpc/kernel/iommu.c                        |  12 +-
 arch/powerpc/kernel/vio.c                          |  12 +-
 arch/powerpc/platforms/cell/iommu.c                |  28 ++---
 arch/powerpc/platforms/pasemi/iommu.c              |   2 +-
 arch/powerpc/platforms/powernv/npu-dma.c           |   8 +-
 arch/powerpc/platforms/powernv/pci-ioda.c          |   4 +-
 arch/powerpc/platforms/powernv/pci.c               |   2 +-
 arch/powerpc/platforms/powernv/pci.h               |   2 +-
 arch/powerpc/platforms/ps3/system-bus.c            |  18 +--
 arch/powerpc/platforms/pseries/iommu.c             |   6 +-
 arch/powerpc/sysdev/dart_iommu.c                   |   2 +-
 arch/s390/include/asm/dma-mapping.h                |   1 -
 arch/s390/pci/pci_dma.c                            |  23 ++--
 arch/sh/include/asm/dma-mapping.h                  |   4 +-
 arch/sh/kernel/dma-nommu.c                         |   4 +-
 arch/sh/mm/consistent.c                            |   4 +-
 arch/sparc/kernel/iommu.c                          |  12 +-
 arch/sparc/kernel/ioport.c                         |  24 ++--
 arch/sparc/kernel/pci_sun4v.c                      |  12 +-
 arch/tile/kernel/pci-dma.c                         |  28 ++---
 arch/unicore32/mm/dma-swiotlb.c                    |   4 +-
 arch/x86/include/asm/dma-mapping.h                 |   5 +-
 arch/x86/include/asm/swiotlb.h                     |   4 +-
 arch/x86/include/asm/xen/page-coherent.h           |   9 +-
 arch/x86/kernel/amd_gart_64.c                      |  20 ++--
 arch/x86/kernel/pci-calgary_64.c                   |  14 +--
 arch/x86/kernel/pci-dma.c                          |   4 +-
 arch/x86/kernel/pci-nommu.c                        |   4 +-
 arch/x86/kernel/pci-swiotlb.c                      |   4 +-
 arch/x86/pci/sta2x11-fixup.c                       |   2 +-
 arch/x86/pci/vmd.c                                 |  16 +--
 arch/xtensa/kernel/pci-dma.c                       |  12 +-
 drivers/gpu/drm/exynos/exynos_drm_fbdev.c          |   2 +-
 drivers/gpu/drm/exynos/exynos_drm_g2d.c            |  12 +-
 drivers/gpu/drm/exynos/exynos_drm_gem.c            |  20 ++--
 drivers/gpu/drm/exynos/exynos_drm_gem.h            |   2 +-
 drivers/gpu/drm/mediatek/mtk_drm_gem.c             |  13 +--
 drivers/gpu/drm/mediatek/mtk_drm_gem.h             |   2 +-
 drivers/gpu/drm/msm/msm_drv.c                      |  13 +--
 .../gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c    |  13 +--
 drivers/gpu/drm/rockchip/rockchip_drm_gem.c        |  17 ++-
 drivers/gpu/drm/rockchip/rockchip_drm_gem.h        |   2 +-
 drivers/infiniband/core/umem.c                     |   7 +-
 drivers/iommu/amd_iommu.c                          |  12 +-
 drivers/iommu/dma-iommu.c                          |   8 +-
 drivers/iommu/intel-iommu.c                        |  12 +-
 .../media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c |   4 -
 drivers/media/platform/sti/bdisp/bdisp-hw.c        |  26 ++---
 drivers/media/v4l2-core/videobuf2-dma-contig.c     |  28 ++---
 drivers/media/v4l2-core/videobuf2-dma-sg.c         |  21 +---
 drivers/media/v4l2-core/videobuf2-vmalloc.c        |   2 +-
 drivers/misc/mic/host/mic_boot.c                   |  20 ++--
 drivers/parisc/ccio-dma.c                          |  16 +--
 drivers/parisc/sba_iommu.c                         |  16 +--
 drivers/remoteproc/qcom_q6v5_pil.c                 |   7 +-
 drivers/video/fbdev/omap2/omapfb/omapfb-main.c     |  12 +-
 drivers/video/fbdev/omap2/omapfb/omapfb.h          |   3 +-
 drivers/xen/swiotlb-xen.c                          |  14 +--
 include/linux/dma-attrs.h                          |  71 ------------
 include/linux/dma-iommu.h                          |   6 +-
 include/linux/dma-mapping.h                        | 128 ++++++++++++++-------
 include/linux/swiotlb.h                            |  10 +-
 include/media/videobuf2-core.h                     |   6 +-
 include/media/videobuf2-dma-contig.h               |   2 -
 include/rdma/ib_verbs.h                            |   8 +-
 include/xen/swiotlb-xen.h                          |  12 +-
 lib/dma-noop.c                                     |   9 +-
 lib/swiotlb.c                                      |  13 ++-
 112 files changed, 694 insertions(+), 798 deletions(-)
 delete mode 100644 include/linux/dma-attrs.h

-- 
1.9.1



From k.kozlowski at samsung.com  Wed Jul 13 01:41:07 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Wed, 13 Jul 2016 10:41:07 +0200
Subject: [PATCH v6 16/46] drm/rockship: dma-mapping: Use unsigned long for
 dma_attrs
In-Reply-To: <1468399300-5399-15-git-send-email-k.kozlowski@samsung.com>
References: <1468399167-28083-1-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-1-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-2-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-3-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-4-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-5-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-6-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-7-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-8-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-9-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-10-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-11-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-12-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-13-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-14-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-15-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1468399300-5399-16-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
[for drm]
Acked-by: Daniel Vetter <daniel.vetter at ffwll.ch>
---
 drivers/gpu/drm/rockchip/rockchip_drm_gem.c | 17 +++++++----------
 drivers/gpu/drm/rockchip/rockchip_drm_gem.h |  2 +-
 2 files changed, 8 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
index 059e902f872d..28b2a828c650 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
@@ -17,8 +17,6 @@
 #include <drm/drm_gem.h>
 #include <drm/drm_vma_manager.h>
 
-#include <linux/dma-attrs.h>
-
 #include "rockchip_drm_drv.h"
 #include "rockchip_drm_gem.h"
 
@@ -28,15 +26,14 @@ static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj,
 	struct drm_gem_object *obj = &rk_obj->base;
 	struct drm_device *drm = obj->dev;
 
-	init_dma_attrs(&rk_obj->dma_attrs);
-	dma_set_attr(DMA_ATTR_WRITE_COMBINE, &rk_obj->dma_attrs);
+	rk_obj->dma_attrs = DMA_ATTR_WRITE_COMBINE;
 
 	if (!alloc_kmap)
-		dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &rk_obj->dma_attrs);
+		rk_obj->dma_attrs |= DMA_ATTR_NO_KERNEL_MAPPING;
 
 	rk_obj->kvaddr = dma_alloc_attrs(drm->dev, obj->size,
 					 &rk_obj->dma_addr, GFP_KERNEL,
-					 &rk_obj->dma_attrs);
+					 rk_obj->dma_attrs);
 	if (!rk_obj->kvaddr) {
 		DRM_ERROR("failed to allocate %zu byte dma buffer", obj->size);
 		return -ENOMEM;
@@ -51,7 +48,7 @@ static void rockchip_gem_free_buf(struct rockchip_gem_object *rk_obj)
 	struct drm_device *drm = obj->dev;
 
 	dma_free_attrs(drm->dev, obj->size, rk_obj->kvaddr, rk_obj->dma_addr,
-		       &rk_obj->dma_attrs);
+		       rk_obj->dma_attrs);
 }
 
 static int rockchip_drm_gem_object_mmap(struct drm_gem_object *obj,
@@ -70,7 +67,7 @@ static int rockchip_drm_gem_object_mmap(struct drm_gem_object *obj,
 	vma->vm_pgoff = 0;
 
 	ret = dma_mmap_attrs(drm->dev, vma, rk_obj->kvaddr, rk_obj->dma_addr,
-			     obj->size, &rk_obj->dma_attrs);
+			     obj->size, rk_obj->dma_attrs);
 	if (ret)
 		drm_gem_vm_close(vma);
 
@@ -262,7 +259,7 @@ struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj)
 
 	ret = dma_get_sgtable_attrs(drm->dev, sgt, rk_obj->kvaddr,
 				    rk_obj->dma_addr, obj->size,
-				    &rk_obj->dma_attrs);
+				    rk_obj->dma_attrs);
 	if (ret) {
 		DRM_ERROR("failed to allocate sgt, %d\n", ret);
 		kfree(sgt);
@@ -276,7 +273,7 @@ void *rockchip_gem_prime_vmap(struct drm_gem_object *obj)
 {
 	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
 
-	if (dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, &rk_obj->dma_attrs))
+	if (dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, rk_obj->dma_attrs))
 		return NULL;
 
 	return rk_obj->kvaddr;
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
index ad22618473a4..18b3488db4ec 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
@@ -23,7 +23,7 @@ struct rockchip_gem_object {
 
 	void *kvaddr;
 	dma_addr_t dma_addr;
-	struct dma_attrs dma_attrs;
+	unsigned long dma_attrs;
 };
 
 struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj);
-- 
1.9.1



From k.kozlowski at samsung.com  Wed Jul 13 01:41:36 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Wed, 13 Jul 2016 10:41:36 +0200
Subject: [PATCH v6 45/46] dma-mapping: Remove dma_get_attr
In-Reply-To: <1468399300-5399-44-git-send-email-k.kozlowski@samsung.com>
References: <1468399167-28083-1-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-1-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-2-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-3-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-4-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-5-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-6-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-7-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-8-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-9-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-10-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-11-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-12-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-13-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-14-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-15-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-16-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-17-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-18-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-19-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-20-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-21-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-22-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-23-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-24-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-25-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-26-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-27-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-28-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-29-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-30-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-31-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-32-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-33-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-34-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-35-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-36-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-37-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-38-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-39-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-40-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-41-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-42-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-43-git-send-email-k.kozlowski@samsung.com>
 <1468399300-5399-44-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1468399300-5399-45-git-send-email-k.kozlowski@samsung.com>

After switching DMA attributes to unsigned long it is easier to just
compare the bits.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
[for avr32]
Acked-by: Hans-Christian Noren Egtvedt <egtvedt at samfundet.no>
[for arc]
Acked-by: Vineet Gupta <vgupta at synopsys.com>
[for arm64 and dma-iommu]
Acked-by: Robin Murphy <robin.murphy at arm.com>
---
 Documentation/DMA-API.txt                      |  4 +--
 arch/arc/mm/dma.c                              |  4 +--
 arch/arm/mm/dma-mapping.c                      | 36 ++++++++++++--------------
 arch/arm/xen/mm.c                              |  4 +--
 arch/arm64/mm/dma-mapping.c                    | 10 +++----
 arch/avr32/mm/dma-coherent.c                   |  4 +--
 arch/ia64/sn/pci/pci_dma.c                     | 10 ++-----
 arch/metag/kernel/dma.c                        |  2 +-
 arch/mips/mm/dma-default.c                     |  6 ++---
 arch/openrisc/kernel/dma.c                     |  4 +--
 arch/parisc/kernel/pci-dma.c                   |  2 +-
 arch/powerpc/platforms/cell/iommu.c            | 12 ++++-----
 drivers/gpu/drm/rockchip/rockchip_drm_gem.c    |  2 +-
 drivers/iommu/dma-iommu.c                      |  2 +-
 drivers/media/v4l2-core/videobuf2-dma-contig.c |  2 +-
 include/linux/dma-mapping.h                    | 10 -------
 16 files changed, 47 insertions(+), 67 deletions(-)

diff --git a/Documentation/DMA-API.txt b/Documentation/DMA-API.txt
index 24f9688bb98a..1d26eeb6b5f6 100644
--- a/Documentation/DMA-API.txt
+++ b/Documentation/DMA-API.txt
@@ -422,9 +422,7 @@ void whizco_dma_map_sg_attrs(struct device *dev, dma_addr_t dma_addr,
 			     unsigned long attrs)
 {
 	....
-	int foo =  dma_get_attr(DMA_ATTR_FOO, attrs);
-	....
-	if (foo)
+	if (attrs & DMA_ATTR_FOO)
 		/* twizzle the frobnozzle */
 	....
 
diff --git a/arch/arc/mm/dma.c b/arch/arc/mm/dma.c
index 3d1f467d1792..74bbe68dce9d 100644
--- a/arch/arc/mm/dma.c
+++ b/arch/arc/mm/dma.c
@@ -46,7 +46,7 @@ static void *arc_dma_alloc(struct device *dev, size_t size,
 	 *   (vs. always going to memory - thus are faster)
 	 */
 	if ((is_isa_arcv2() && ioc_exists) ||
-	    dma_get_attr(DMA_ATTR_NON_CONSISTENT, attrs))
+	    (attrs & DMA_ATTR_NON_CONSISTENT))
 		need_coh = 0;
 
 	/*
@@ -95,7 +95,7 @@ static void arc_dma_free(struct device *dev, size_t size, void *vaddr,
 	struct page *page = virt_to_page(dma_handle);
 	int is_non_coh = 1;
 
-	is_non_coh = dma_get_attr(DMA_ATTR_NON_CONSISTENT, attrs) ||
+	is_non_coh = (attrs & DMA_ATTR_NON_CONSISTENT) ||
 			(is_isa_arcv2() && ioc_exists);
 
 	if (PageHighMem(page) || !is_non_coh)
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index ebb3fde99043..43e03b5293d0 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -126,7 +126,7 @@ static dma_addr_t arm_dma_map_page(struct device *dev, struct page *page,
 	     unsigned long offset, size_t size, enum dma_data_direction dir,
 	     unsigned long attrs)
 {
-	if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
+	if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
 		__dma_page_cpu_to_dev(page, offset, size, dir);
 	return pfn_to_dma(dev, page_to_pfn(page)) + offset;
 }
@@ -155,7 +155,7 @@ static dma_addr_t arm_coherent_dma_map_page(struct device *dev, struct page *pag
 static void arm_dma_unmap_page(struct device *dev, dma_addr_t handle,
 		size_t size, enum dma_data_direction dir, unsigned long attrs)
 {
-	if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
+	if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
 		__dma_page_dev_to_cpu(pfn_to_page(dma_to_pfn(dev, handle)),
 				      handle & ~PAGE_MASK, size, dir);
 }
@@ -622,9 +622,9 @@ static void __free_from_contiguous(struct device *dev, struct page *page,
 
 static inline pgprot_t __get_dma_pgprot(unsigned long attrs, pgprot_t prot)
 {
-	prot = dma_get_attr(DMA_ATTR_WRITE_COMBINE, attrs) ?
-			    pgprot_writecombine(prot) :
-			    pgprot_dmacoherent(prot);
+	prot = (attrs & DMA_ATTR_WRITE_COMBINE) ?
+			pgprot_writecombine(prot) :
+			pgprot_dmacoherent(prot);
 	return prot;
 }
 
@@ -744,7 +744,7 @@ static void *__dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
 		.gfp = gfp,
 		.prot = prot,
 		.caller = caller,
-		.want_vaddr = !dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, attrs),
+		.want_vaddr = ((attrs & DMA_ATTR_NO_KERNEL_MAPPING) == 0),
 	};
 
 #ifdef CONFIG_DMA_API_DEBUG
@@ -887,7 +887,7 @@ static void __arm_dma_free(struct device *dev, size_t size, void *cpu_addr,
 		.size = PAGE_ALIGN(size),
 		.cpu_addr = cpu_addr,
 		.page = page,
-		.want_vaddr = !dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, attrs),
+		.want_vaddr = ((attrs & DMA_ATTR_NO_KERNEL_MAPPING) == 0),
 	};
 
 	buf = arm_dma_buffer_find(cpu_addr);
@@ -1267,7 +1267,7 @@ static struct page **__iommu_alloc_buffer(struct device *dev, size_t size,
 	if (!pages)
 		return NULL;
 
-	if (dma_get_attr(DMA_ATTR_FORCE_CONTIGUOUS, attrs))
+	if (attrs & DMA_ATTR_FORCE_CONTIGUOUS)
 	{
 		unsigned long order = get_order(size);
 		struct page *page;
@@ -1285,7 +1285,7 @@ static struct page **__iommu_alloc_buffer(struct device *dev, size_t size,
 	}
 
 	/* Go straight to 4K chunks if caller says it's OK. */
-	if (dma_get_attr(DMA_ATTR_ALLOC_SINGLE_PAGES, attrs))
+	if (attrs & DMA_ATTR_ALLOC_SINGLE_PAGES)
 		order_idx = ARRAY_SIZE(iommu_order_array) - 1;
 
 	/*
@@ -1346,7 +1346,7 @@ static int __iommu_free_buffer(struct device *dev, struct page **pages,
 	int count = size >> PAGE_SHIFT;
 	int i;
 
-	if (dma_get_attr(DMA_ATTR_FORCE_CONTIGUOUS, attrs)) {
+	if (attrs & DMA_ATTR_FORCE_CONTIGUOUS) {
 		dma_release_from_contiguous(dev, pages[0], count);
 	} else {
 		for (i = 0; i < count; i++)
@@ -1445,7 +1445,7 @@ static struct page **__iommu_get_pages(void *cpu_addr, unsigned long attrs)
 	if (__in_atomic_pool(cpu_addr, PAGE_SIZE))
 		return __atomic_get_pages(cpu_addr);
 
-	if (dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, attrs))
+	if (attrs & DMA_ATTR_NO_KERNEL_MAPPING)
 		return cpu_addr;
 
 	area = find_vm_area(cpu_addr);
@@ -1512,7 +1512,7 @@ static void *arm_iommu_alloc_attrs(struct device *dev, size_t size,
 	if (*handle == DMA_ERROR_CODE)
 		goto err_buffer;
 
-	if (dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, attrs))
+	if (attrs & DMA_ATTR_NO_KERNEL_MAPPING)
 		return pages;
 
 	addr = __iommu_alloc_remap(pages, size, gfp, prot,
@@ -1583,7 +1583,7 @@ void arm_iommu_free_attrs(struct device *dev, size_t size, void *cpu_addr,
 		return;
 	}
 
-	if (!dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, attrs)) {
+	if ((attrs & DMA_ATTR_NO_KERNEL_MAPPING) == 0) {
 		dma_common_free_remap(cpu_addr, size,
 			VM_ARM_DMA_CONSISTENT | VM_USERMAP);
 	}
@@ -1653,8 +1653,7 @@ static int __map_sg_chunk(struct device *dev, struct scatterlist *sg,
 		phys_addr_t phys = page_to_phys(sg_page(s));
 		unsigned int len = PAGE_ALIGN(s->offset + s->length);
 
-		if (!is_coherent &&
-			!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
+		if (!is_coherent && (attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
 			__dma_page_cpu_to_dev(sg_page(s), s->offset, s->length, dir);
 
 		prot = __dma_direction_to_prot(dir);
@@ -1767,8 +1766,7 @@ static void __iommu_unmap_sg(struct device *dev, struct scatterlist *sg,
 		if (sg_dma_len(s))
 			__iommu_remove_mapping(dev, sg_dma_address(s),
 					       sg_dma_len(s));
-		if (!is_coherent &&
-		    !dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
+		if (!is_coherent && (attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
 			__dma_page_dev_to_cpu(sg_page(s), s->offset,
 					      s->length, dir);
 	}
@@ -1892,7 +1890,7 @@ static dma_addr_t arm_iommu_map_page(struct device *dev, struct page *page,
 	     unsigned long offset, size_t size, enum dma_data_direction dir,
 	     unsigned long attrs)
 {
-	if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
+	if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
 		__dma_page_cpu_to_dev(page, offset, size, dir);
 
 	return arm_coherent_iommu_map_page(dev, page, offset, size, dir, attrs);
@@ -1943,7 +1941,7 @@ static void arm_iommu_unmap_page(struct device *dev, dma_addr_t handle,
 	if (!iova)
 		return;
 
-	if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
+	if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
 		__dma_page_dev_to_cpu(page, offset, size, dir);
 
 	iommu_unmap(mapping->domain, iova, len);
diff --git a/arch/arm/xen/mm.c b/arch/arm/xen/mm.c
index fc67ed236a10..d062f08f5020 100644
--- a/arch/arm/xen/mm.c
+++ b/arch/arm/xen/mm.c
@@ -102,7 +102,7 @@ void __xen_dma_map_page(struct device *hwdev, struct page *page,
 {
 	if (is_device_dma_coherent(hwdev))
 		return;
-	if (dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
+	if (attrs & DMA_ATTR_SKIP_CPU_SYNC)
 		return;
 
 	__xen_dma_page_cpu_to_dev(hwdev, dev_addr, size, dir);
@@ -115,7 +115,7 @@ void __xen_dma_unmap_page(struct device *hwdev, dma_addr_t handle,
 {
 	if (is_device_dma_coherent(hwdev))
 		return;
-	if (dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
+	if (attrs & DMA_ATTR_SKIP_CPU_SYNC)
 		return;
 
 	__xen_dma_page_dev_to_cpu(hwdev, handle, size, dir);
diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
index a284fd0d0b00..c4284c432ae8 100644
--- a/arch/arm64/mm/dma-mapping.c
+++ b/arch/arm64/mm/dma-mapping.c
@@ -35,7 +35,7 @@ static int swiotlb __read_mostly;
 static pgprot_t __get_dma_pgprot(unsigned long attrs, pgprot_t prot,
 				 bool coherent)
 {
-	if (!coherent || dma_get_attr(DMA_ATTR_WRITE_COMBINE, attrs))
+	if (!coherent || (attrs & DMA_ATTR_WRITE_COMBINE))
 		return pgprot_writecombine(prot);
 	return prot;
 }
@@ -714,7 +714,7 @@ static dma_addr_t __iommu_map_page(struct device *dev, struct page *page,
 	dma_addr_t dev_addr = iommu_dma_map_page(dev, page, offset, size, prot);
 
 	if (!iommu_dma_mapping_error(dev, dev_addr) &&
-	    !dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
+	    (attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
 		__iommu_sync_single_for_device(dev, dev_addr, size, dir);
 
 	return dev_addr;
@@ -724,7 +724,7 @@ static void __iommu_unmap_page(struct device *dev, dma_addr_t dev_addr,
 			       size_t size, enum dma_data_direction dir,
 			       unsigned long attrs)
 {
-	if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
+	if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
 		__iommu_sync_single_for_cpu(dev, dev_addr, size, dir);
 
 	iommu_dma_unmap_page(dev, dev_addr, size, dir, attrs);
@@ -764,7 +764,7 @@ static int __iommu_map_sg_attrs(struct device *dev, struct scatterlist *sgl,
 {
 	bool coherent = is_device_dma_coherent(dev);
 
-	if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
+	if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
 		__iommu_sync_sg_for_device(dev, sgl, nelems, dir);
 
 	return iommu_dma_map_sg(dev, sgl, nelems,
@@ -776,7 +776,7 @@ static void __iommu_unmap_sg_attrs(struct device *dev,
 				   enum dma_data_direction dir,
 				   unsigned long attrs)
 {
-	if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
+	if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
 		__iommu_sync_sg_for_cpu(dev, sgl, nelems, dir);
 
 	iommu_dma_unmap_sg(dev, sgl, nelems, dir, attrs);
diff --git a/arch/avr32/mm/dma-coherent.c b/arch/avr32/mm/dma-coherent.c
index fc51f4421933..58610d0df7ed 100644
--- a/arch/avr32/mm/dma-coherent.c
+++ b/arch/avr32/mm/dma-coherent.c
@@ -109,7 +109,7 @@ static void *avr32_dma_alloc(struct device *dev, size_t size,
 		return NULL;
 	phys = page_to_phys(page);
 
-	if (dma_get_attr(DMA_ATTR_WRITE_COMBINE, attrs)) {
+	if (attrs & DMA_ATTR_WRITE_COMBINE) {
 		/* Now, map the page into P3 with write-combining turned on */
 		*handle = phys;
 		return __ioremap(phys, size, _PAGE_BUFFER);
@@ -123,7 +123,7 @@ static void avr32_dma_free(struct device *dev, size_t size,
 {
 	struct page *page;
 
-	if (dma_get_attr(DMA_ATTR_WRITE_COMBINE, attrs)) {
+	if (attrs & DMA_ATTR_WRITE_COMBINE) {
 		iounmap(cpu_addr);
 
 		page = phys_to_page(handle);
diff --git a/arch/ia64/sn/pci/pci_dma.c b/arch/ia64/sn/pci/pci_dma.c
index 6b78fc953c4b..74c934a997bb 100644
--- a/arch/ia64/sn/pci/pci_dma.c
+++ b/arch/ia64/sn/pci/pci_dma.c
@@ -183,14 +183,11 @@ static dma_addr_t sn_dma_map_page(struct device *dev, struct page *page,
 	unsigned long phys_addr;
 	struct pci_dev *pdev = to_pci_dev(dev);
 	struct sn_pcibus_provider *provider = SN_PCIDEV_BUSPROVIDER(pdev);
-	int dmabarr;
-
-	dmabarr = dma_get_attr(DMA_ATTR_WRITE_BARRIER, attrs);
 
 	BUG_ON(!dev_is_pci(dev));
 
 	phys_addr = __pa(cpu_addr);
-	if (dmabarr)
+	if (attrs & DMA_ATTR_WRITE_BARRIER)
 		dma_addr = provider->dma_map_consistent(pdev, phys_addr,
 							size, SN_DMA_ADDR_PHYS);
 	else
@@ -280,9 +277,6 @@ static int sn_dma_map_sg(struct device *dev, struct scatterlist *sgl,
 	struct pci_dev *pdev = to_pci_dev(dev);
 	struct sn_pcibus_provider *provider = SN_PCIDEV_BUSPROVIDER(pdev);
 	int i;
-	int dmabarr;
-
-	dmabarr = dma_get_attr(DMA_ATTR_WRITE_BARRIER, attrs);
 
 	BUG_ON(!dev_is_pci(dev));
 
@@ -292,7 +286,7 @@ static int sn_dma_map_sg(struct device *dev, struct scatterlist *sgl,
 	for_each_sg(sgl, sg, nhwentries, i) {
 		dma_addr_t dma_addr;
 		phys_addr = SG_ENT_PHYS_ADDRESS(sg);
-		if (dmabarr)
+		if (attrs & DMA_ATTR_WRITE_BARRIER)
 			dma_addr = provider->dma_map_consistent(pdev,
 								phys_addr,
 								sg->length,
diff --git a/arch/metag/kernel/dma.c b/arch/metag/kernel/dma.c
index d68f498e82a1..0db31e24c541 100644
--- a/arch/metag/kernel/dma.c
+++ b/arch/metag/kernel/dma.c
@@ -337,7 +337,7 @@ static int metag_dma_mmap(struct device *dev, struct vm_area_struct *vma,
 	struct metag_vm_region *c;
 	int ret = -ENXIO;
 
-	if (dma_get_attr(DMA_ATTR_WRITE_COMBINE, attrs))
+	if (attrs & DMA_ATTR_WRITE_COMBINE)
 		vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
 	else
 		vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
diff --git a/arch/mips/mm/dma-default.c b/arch/mips/mm/dma-default.c
index 0ed9000dc1ff..b2eadd6fa9a1 100644
--- a/arch/mips/mm/dma-default.c
+++ b/arch/mips/mm/dma-default.c
@@ -141,7 +141,7 @@ static void *mips_dma_alloc_coherent(struct device *dev, size_t size,
 	 * XXX: seems like the coherent and non-coherent implementations could
 	 * be consolidated.
 	 */
-	if (dma_get_attr(DMA_ATTR_NON_CONSISTENT, attrs))
+	if (attrs & DMA_ATTR_NON_CONSISTENT)
 		return mips_dma_alloc_noncoherent(dev, size, dma_handle, gfp);
 
 	gfp = massage_gfp_flags(dev, gfp);
@@ -182,7 +182,7 @@ static void mips_dma_free_coherent(struct device *dev, size_t size, void *vaddr,
 	unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT;
 	struct page *page = NULL;
 
-	if (dma_get_attr(DMA_ATTR_NON_CONSISTENT, attrs)) {
+	if (attrs & DMA_ATTR_NON_CONSISTENT) {
 		mips_dma_free_noncoherent(dev, size, vaddr, dma_handle);
 		return;
 	}
@@ -214,7 +214,7 @@ static int mips_dma_mmap(struct device *dev, struct vm_area_struct *vma,
 
 	pfn = page_to_pfn(virt_to_page((void *)addr));
 
-	if (dma_get_attr(DMA_ATTR_WRITE_COMBINE, attrs))
+	if (attrs & DMA_ATTR_WRITE_COMBINE)
 		vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
 	else
 		vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
diff --git a/arch/openrisc/kernel/dma.c b/arch/openrisc/kernel/dma.c
index 50eb1f26c540..140c99140649 100644
--- a/arch/openrisc/kernel/dma.c
+++ b/arch/openrisc/kernel/dma.c
@@ -100,7 +100,7 @@ or1k_dma_alloc(struct device *dev, size_t size,
 
 	va = (unsigned long)page;
 
-	if (!dma_get_attr(DMA_ATTR_NON_CONSISTENT, attrs)) {
+	if ((attrs & DMA_ATTR_NON_CONSISTENT) == 0) {
 		/*
 		 * We need to iterate through the pages, clearing the dcache for
 		 * them and setting the cache-inhibit bit.
@@ -124,7 +124,7 @@ or1k_dma_free(struct device *dev, size_t size, void *vaddr,
 		.mm = &init_mm
 	};
 
-	if (!dma_get_attr(DMA_ATTR_NON_CONSISTENT, attrs)) {
+	if ((attrs & DMA_ATTR_NON_CONSISTENT) == 0) {
 		/* walk_page_range shouldn't be able to fail here */
 		WARN_ON(walk_page_range(va, va + size, &walk));
 	}
diff --git a/arch/parisc/kernel/pci-dma.c b/arch/parisc/kernel/pci-dma.c
index 845fdd52e4c5..02d9ed0f3949 100644
--- a/arch/parisc/kernel/pci-dma.c
+++ b/arch/parisc/kernel/pci-dma.c
@@ -581,7 +581,7 @@ static void *pcx_dma_alloc(struct device *dev, size_t size,
 {
 	void *addr;
 
-	if (!dma_get_attr(DMA_ATTR_NON_CONSISTENT, attrs))
+	if ((attrs & DMA_ATTR_NON_CONSISTENT) == 0)
 		return NULL;
 
 	addr = (void *)__get_free_pages(flag, get_order(size));
diff --git a/arch/powerpc/platforms/cell/iommu.c b/arch/powerpc/platforms/cell/iommu.c
index 9a195f1893af..f7d1a4953ea0 100644
--- a/arch/powerpc/platforms/cell/iommu.c
+++ b/arch/powerpc/platforms/cell/iommu.c
@@ -193,7 +193,7 @@ static int tce_build_cell(struct iommu_table *tbl, long index, long npages,
 	base_pte = CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | CBE_IOPTE_M |
 		CBE_IOPTE_SO_RW | (window->ioid & CBE_IOPTE_IOID_Mask);
 #endif
-	if (unlikely(dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs)))
+	if (unlikely(attrs & DMA_ATTR_WEAK_ORDERING))
 		base_pte &= ~CBE_IOPTE_SO_RW;
 
 	io_pte = (unsigned long *)tbl->it_base + (index - tbl->it_offset);
@@ -600,7 +600,7 @@ static dma_addr_t dma_fixed_map_page(struct device *dev, struct page *page,
 				     enum dma_data_direction direction,
 				     unsigned long attrs)
 {
-	if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs))
+	if (iommu_fixed_is_weak == (attrs & DMA_ATTR_WEAK_ORDERING))
 		return dma_direct_ops.map_page(dev, page, offset, size,
 					       direction, attrs);
 	else
@@ -613,7 +613,7 @@ static void dma_fixed_unmap_page(struct device *dev, dma_addr_t dma_addr,
 				 size_t size, enum dma_data_direction direction,
 				 unsigned long attrs)
 {
-	if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs))
+	if (iommu_fixed_is_weak == (attrs & DMA_ATTR_WEAK_ORDERING))
 		dma_direct_ops.unmap_page(dev, dma_addr, size, direction,
 					  attrs);
 	else
@@ -625,7 +625,7 @@ static int dma_fixed_map_sg(struct device *dev, struct scatterlist *sg,
 			   int nents, enum dma_data_direction direction,
 			   unsigned long attrs)
 {
-	if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs))
+	if (iommu_fixed_is_weak == (attrs & DMA_ATTR_WEAK_ORDERING))
 		return dma_direct_ops.map_sg(dev, sg, nents, direction, attrs);
 	else
 		return ppc_iommu_map_sg(dev, cell_get_iommu_table(dev), sg,
@@ -637,7 +637,7 @@ static void dma_fixed_unmap_sg(struct device *dev, struct scatterlist *sg,
 			       int nents, enum dma_data_direction direction,
 			       unsigned long attrs)
 {
-	if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs))
+	if (iommu_fixed_is_weak == (attrs & DMA_ATTR_WEAK_ORDERING))
 		dma_direct_ops.unmap_sg(dev, sg, nents, direction, attrs);
 	else
 		ppc_iommu_unmap_sg(cell_get_iommu_table(dev), sg, nents,
@@ -1162,7 +1162,7 @@ static int __init setup_iommu_fixed(char *str)
 	pciep = of_find_node_by_type(NULL, "pcie-endpoint");
 
 	if (strcmp(str, "weak") == 0 || (pciep && strcmp(str, "strong") != 0))
-		iommu_fixed_is_weak = 1;
+		iommu_fixed_is_weak = DMA_ATTR_WEAK_ORDERING;
 
 	of_node_put(pciep);
 
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
index 28b2a828c650..b70f9423379c 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
@@ -273,7 +273,7 @@ void *rockchip_gem_prime_vmap(struct drm_gem_object *obj)
 {
 	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
 
-	if (dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, rk_obj->dma_attrs))
+	if (rk_obj->dma_attrs & DMA_ATTR_NO_KERNEL_MAPPING)
 		return NULL;
 
 	return rk_obj->kvaddr;
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 6c1bda504fb1..08a1e2f3690f 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -306,7 +306,7 @@ struct page **iommu_dma_alloc(struct device *dev, size_t size, gfp_t gfp,
 	} else {
 		size = ALIGN(size, min_size);
 	}
-	if (dma_get_attr(DMA_ATTR_ALLOC_SINGLE_PAGES, attrs))
+	if (attrs & DMA_ATTR_ALLOC_SINGLE_PAGES)
 		alloc_sizes = min_size;
 
 	count = PAGE_ALIGN(size) >> PAGE_SHIFT;
diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c
index 1ec4434a86bb..b09b2c9b6b63 100644
--- a/drivers/media/v4l2-core/videobuf2-dma-contig.c
+++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c
@@ -155,7 +155,7 @@ static void *vb2_dc_alloc(struct device *dev, unsigned long attrs,
 		return ERR_PTR(-ENOMEM);
 	}
 
-	if (!dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, buf->attrs))
+	if ((buf->attrs & DMA_ATTR_NO_KERNEL_MAPPING) == 0)
 		buf->vaddr = buf->cookie;
 
 	/* Prevent the device from being released while the buffer is used */
diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h
index 1fd9860487b1..d8fb67783cb7 100644
--- a/include/linux/dma-mapping.h
+++ b/include/linux/dma-mapping.h
@@ -101,16 +101,6 @@ static inline int is_device_dma_capable(struct device *dev)
 	return dev->dma_mask != NULL && *dev->dma_mask != DMA_MASK_NONE;
 }
 
-/**
- * dma_get_attr - check for a specific attribute
- * @attr: attribute to look for
- * @attrs: attributes to check within
- */
-static inline bool dma_get_attr(unsigned long attr, unsigned long attrs)
-{
-	return !!(attr & attrs);
-}
-
 #ifdef CONFIG_HAVE_GENERIC_DMA_COHERENT
 /*
  * These three functions are only for dma allocator.
-- 
1.9.1



From dan.carpenter at oracle.com  Wed Jul 13 03:15:04 2016
From: dan.carpenter at oracle.com (Dan Carpenter)
Date: Wed, 13 Jul 2016 13:15:04 +0300
Subject: [patch] drm/rockchip: fix a couple off by one bugs
Message-ID: <20160713101504.GJ29468@mwanda>

The priv->crtc_funcs[] array has ROCKCHIP_MAX_CRTC elements so > should
be >= here.

Fixes: 2048e3286f34 ('drm: rockchip: Add basic drm driver')
Signed-off-by: Dan Carpenter <dan.carpenter at oracle.com>

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index 7fd20c0..37ca427 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -79,7 +79,7 @@ int rockchip_register_crtc_funcs(struct drm_crtc *crtc,
 	int pipe = drm_crtc_index(crtc);
 	struct rockchip_drm_private *priv = crtc->dev->dev_private;
 
-	if (pipe > ROCKCHIP_MAX_CRTC)
+	if (pipe >= ROCKCHIP_MAX_CRTC)
 		return -EINVAL;
 
 	priv->crtc_funcs[pipe] = crtc_funcs;
@@ -92,7 +92,7 @@ void rockchip_unregister_crtc_funcs(struct drm_crtc *crtc)
 	int pipe = drm_crtc_index(crtc);
 	struct rockchip_drm_private *priv = crtc->dev->dev_private;
 
-	if (pipe > ROCKCHIP_MAX_CRTC)
+	if (pipe >= ROCKCHIP_MAX_CRTC)
 		return;
 
 	priv->crtc_funcs[pipe] = NULL;


From robh at kernel.org  Wed Jul 13 06:49:41 2016
From: robh at kernel.org (Rob Herring)
Date: Wed, 13 Jul 2016 08:49:41 -0500
Subject: [PATCH 1/7] dt-bindings: add rk3399 support for dw-mipi-rockchip
In-Reply-To: <1467968701-15620-2-git-send-email-zyw@rock-chips.com>
References: <1467968701-15620-1-git-send-email-zyw@rock-chips.com>
 <1467968701-15620-2-git-send-email-zyw@rock-chips.com>
Message-ID: <20160713134941.GA10985@rob-hp-laptop>

On Fri, Jul 08, 2016 at 05:04:55PM +0800, Chris Zhong wrote:
> The dw-mipi-dsi of rk3399 is almost the same as rk3288, the rk3399 has
> additional phy config clock.
> 
> Signed-off-by: Chris Zhong <zyw at rock-chips.com>
> ---
> 
>  .../devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt    | 5 +++++
>  1 file changed, 5 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt
> index 1753f0c..4d59df3 100644
> --- a/Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt
> +++ b/Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt
> @@ -5,6 +5,7 @@ Required properties:
>  - #address-cells: Should be <1>.
>  - #size-cells: Should be <0>.
>  - compatible: "rockchip,rk3288-mipi-dsi", "snps,dw-mipi-dsi".
> +	      "rockchip,rk3399-mipi-dsi", "snps,dw-mipi-dsi".
>  - reg: Represent the physical address range of the controller.
>  - interrupts: Represent the controller's interrupt to the CPU(s).
>  - clocks, clock-names: Phandles to the controller's pll reference
> @@ -13,6 +14,10 @@ Required properties:
>  - ports: contain a port node with endpoint definitions as defined in [2].
>    For vopb,set the reg = <0> and set the reg = <1> for vopl.
>  
> +Optional properties:
> +- clocks, clock-names: phandle to the dw-mipi phy clock, name should be
> +		       "phy_cfg".
> +

This is not really optional. It is required for rk3399. Document with 
the rest of the clocks and note this one is rk3399 only.

Rob


From robh at kernel.org  Wed Jul 13 06:50:29 2016
From: robh at kernel.org (Rob Herring)
Date: Wed, 13 Jul 2016 08:50:29 -0500
Subject: [PATCH 3/7] dt-bindings: add power domain node for
 dw-mipi-rockchip
In-Reply-To: <1467968701-15620-4-git-send-email-zyw@rock-chips.com>
References: <1467968701-15620-1-git-send-email-zyw@rock-chips.com>
 <1467968701-15620-4-git-send-email-zyw@rock-chips.com>
Message-ID: <20160713135029.GA12147@rob-hp-laptop>

On Fri, Jul 08, 2016 at 05:04:57PM +0800, Chris Zhong wrote:
> Signed-off-by: Chris Zhong <zyw at rock-chips.com>
> ---
> 
>  .../devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt        | 1 +
>  1 file changed, 1 insertion(+)

Acked-by: Rob Herring <robh at kernel.org>


From seanpaul at chromium.org  Wed Jul 13 06:59:58 2016
From: seanpaul at chromium.org (Sean Paul)
Date: Wed, 13 Jul 2016 06:59:58 -0700
Subject: [v5 PATCH 5/5] drm/rockchip: cdn-dp: add cdn DP support for rk3399
In-Reply-To: <1468336188-565-6-git-send-email-zyw@rock-chips.com>
References: <1468336188-565-1-git-send-email-zyw@rock-chips.com>
 <1468336188-565-6-git-send-email-zyw@rock-chips.com>
Message-ID: <CAOw6vb+=F=X1MeWhUbs42MzP_CFfMkYrFUqKmWjVwwohufFOhw@mail.gmail.com>

On Tue, Jul 12, 2016 at 8:09 AM, Chris Zhong <zyw at rock-chips.com> wrote:
> Add support for cdn DP controller which is embedded in the rk3399
> SoCs. The DP is compliant with DisplayPort Specification,
> Version 1.3, This IP is compatible with the rockchip type-c PHY IP.
> There is a uCPU in DP controller, it need a firmware to work,
> please put the firmware file to /lib/firmware/cdn/dptx.bin. The
> uCPU in charge of aux communication and link training, the host use
> mailbox to communicate with the ucpu.
> The dclk pin_pol of vop must not be invert for DP.
>
> Signed-off-by: Chris Zhong <zyw at rock-chips.com>
>
> ---
>
> Changes in v5:
> - alphabetical order
> - do not use long, use u32 or u64
> - return MODE_CLOCK_HIGH when requested > actual
> - Optimized Coding Style
> - add a formula to get better tu size and symbol value.
>
> Changes in v4:
> - use phy framework to control DP phy
> - support 2 phys
>
> Changes in v3:
> - use EXTCON_DISP_DP and EXTCON_DISP_DP_ALT cable to get dp port state.
> - reset spdif before config it
> - modify the firmware clk to 100Mhz
> - retry load firmware if fw file is requested too early
>
> Changes in v2:
> - Alphabetic order
> - remove excess error message
> - use define clk_rate
> - check all return value
> - remove dev_set_name(dp->dev, "cdn-dp");
> - use schedule_delayed_work
> - remove never-called functions
> - remove some unnecessary ()
>
> Changes in v1:
> - use extcon API
> - use hdmi-codec for the DP Asoc
> - do not initialize the "ret"
> - printk a err log when drm_of_encoder_active_endpoint_id
> - modify the dclk pin_pol to a single line
>
>  drivers/gpu/drm/rockchip/Kconfig            |   9 +
>  drivers/gpu/drm/rockchip/Makefile           |   1 +
>  drivers/gpu/drm/rockchip/cdn-dp-core.c      | 761 ++++++++++++++++++++++++++++
>  drivers/gpu/drm/rockchip/cdn-dp-core.h      | 113 +++++
>  drivers/gpu/drm/rockchip/cdn-dp-reg.c       | 740 +++++++++++++++++++++++++++
>  drivers/gpu/drm/rockchip/cdn-dp-reg.h       | 409 +++++++++++++++

Could you explain the file naming convention in the rk driver? We've
got analogix_dp-rockchip.c, dw_hdmi-rockchip.c, dw-mipi-dsi.c, and now
cdn-dp-(core|reg).[ch]. I'm honestly not sure whether these filenames
are consistent with the rest, but bleh.


>  drivers/gpu/drm/rockchip/rockchip_drm_vop.c |   6 +-
>  drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   2 +
>  drivers/gpu/drm/rockchip/rockchip_vop_reg.c |   2 +
>  9 files changed, 2042 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.c
>  create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.h
>  create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.c
>  create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.h
>
> diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
> index d30bdc3..20da9a8 100644
> --- a/drivers/gpu/drm/rockchip/Kconfig
> +++ b/drivers/gpu/drm/rockchip/Kconfig
> @@ -25,6 +25,15 @@ config ROCKCHIP_ANALOGIX_DP
>           for the Analogix Core DP driver. If you want to enable DP
>           on RK3288 based SoC, you should selet this option.
>
> +config ROCKCHIP_CDN_DP
> +        tristate "Rockchip cdn DP"
> +        depends on DRM_ROCKCHIP
> +        help
> +         This selects support for Rockchip SoC specific extensions
> +         for the cdn Dp driver. If you want to enable Dp on

s/Dp/DP/

> +         RK3399 based SoC, you should selet this

s/selet/select/

> +         option.
> +
>  config ROCKCHIP_DW_HDMI
>          tristate "Rockchip specific extensions for Synopsys DW HDMI"
>          depends on DRM_ROCKCHIP
> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
> index 05d0713..abdecd5 100644
> --- a/drivers/gpu/drm/rockchip/Makefile
> +++ b/drivers/gpu/drm/rockchip/Makefile
> @@ -7,6 +7,7 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
>  rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
>
>  obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
> +obj-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
>  obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
>  obj-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
>  obj-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
> new file mode 100644
> index 0000000..5b8a15e
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
> @@ -0,0 +1,761 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author: Chris Zhong <zyw at rock-chips.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * 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 <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_dp_helper.h>
> +#include <drm/drm_edid.h>
> +#include <drm/drm_of.h>
> +
> +#include <linux/clk.h>
> +#include <linux/component.h>
> +#include <linux/extcon.h>
> +#include <linux/firmware.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/phy/phy.h>
> +
> +#include <sound/hdmi-codec.h>
> +
> +#include <video/of_videomode.h>
> +#include <video/videomode.h>

Doesn't seem like you use these.


> +
> +#include "cdn-dp-core.h"
> +#include "cdn-dp-reg.h"
> +#include "rockchip_drm_vop.h"
> +
> +#define connector_to_dp(c) \
> +               container_of(c, struct cdn_dp_device, connector)
> +
> +#define encoder_to_dp(c) \
> +               container_of(c, struct cdn_dp_device, encoder)
> +
> +/* dp grf register offset */
> +#define DP_VOP_SEL             0x6224
> +#define DP_SEL_VOP_LIT         BIT(12)
> +#define DP_CLK_RATE            100000000

If this clock rate never changes, it should probably live somewhere in
the clock driver.

> +#define WAIT_HPD_STABLE                300

Encode the units in these two define names, ie: DP_CLK_RATE_HZ,
WAIT_HPD_STABLE_MS

> +
> +static int cdn_dp_clk_enable(struct cdn_dp_device *dp)
> +{
> +       int ret;
> +
> +       ret = clk_prepare_enable(dp->pclk);
> +       if (ret < 0) {
> +               dev_err(dp->dev, "cannot enable dp pclk %d\n", ret);
> +               goto err_pclk;
> +       }
> +
> +       ret = clk_prepare_enable(dp->core_clk);
> +       if (ret < 0) {
> +               dev_err(dp->dev, "cannot enable core_clk %d\n", ret);
> +               goto err_core_clk;
> +       }
> +
> +       ret = clk_set_rate(dp->core_clk, DP_CLK_RATE);
> +       if (ret < 0) {
> +               dev_err(dp->dev, "cannot set dp core clk to %d %d\n",
> +                       DP_CLK_RATE, ret);
> +               goto err_set_rate;
> +       }
> +
> +       /* notice fw the clk freq value */

This might be clearer if it was rephrased to "update fw with the
current core clk frequency". I still think the rate should be set in
the clock driver, and perhaps you can query the rate to get the
current value.

> +       cdn_dp_set_fw_clk(dp, DP_CLK_RATE);
> +
> +       return 0;
> +
> +err_set_rate:
> +       clk_disable_unprepare(dp->core_clk);
> +err_core_clk:
> +       clk_disable_unprepare(dp->pclk);
> +err_pclk:
> +       return ret;
> +}
> +
> +static enum drm_connector_status
> +cdn_dp_connector_detect(struct drm_connector *connector, bool force)
> +{
> +       struct cdn_dp_device *dp = connector_to_dp(connector);
> +
> +       return dp->hpd_status ? connector_status_connected :
> +                               connector_status_disconnected;

If you just stored hpd_status as drm_connector_status, you could avoid
having to translate the bool.

> +}
> +
> +static void cdn_dp_connector_destroy(struct drm_connector *connector)
> +{
> +       drm_connector_unregister(connector);
> +       drm_connector_cleanup(connector);
> +}
> +
> +static struct drm_connector_funcs cdn_dp_atomic_connector_funcs = {
> +       .dpms = drm_atomic_helper_connector_dpms,
> +       .detect = cdn_dp_connector_detect,
> +       .destroy = cdn_dp_connector_destroy,
> +       .fill_modes = drm_helper_probe_single_connector_modes,
> +       .reset = drm_atomic_helper_connector_reset,
> +       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> +       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +static int cdn_dp_connector_get_modes(struct drm_connector *connector)
> +{
> +       struct cdn_dp_device *dp = connector_to_dp(connector);
> +       struct edid *edid;
> +
> +       if (!dp->fw_loaded)

Can this actually happen? Seems like we shouldn't be returning
connected from detect if the fw isn't loaded, so this should never be
true. If that is the case, I think BUG_ON or WARN_ON would be more
appropriate here.


> +               return 0;
> +
> +       edid = drm_do_get_edid(connector, cdn_dp_get_edid_block, dp);
> +       if (edid) {
> +               dev_dbg(dp->dev, "got edid: width[%d] x height[%d]\n",
> +                       edid->width_cm, edid->height_cm);
> +
> +               dp->sink_has_audio = drm_detect_monitor_audio(edid);
> +               drm_mode_connector_update_edid_property(connector, edid);
> +               drm_add_edid_modes(connector, edid);
> +               /* Store the ELD */

This comment isn't useful

> +               drm_edid_to_eld(connector, edid);
> +               kfree(edid);
> +       } else {
> +               dev_dbg(dp->dev, "failed to get edid\n");

This seems to warrant a stronger level than dbg, perhaps warn. Also,
should this really return success?

> +       }
> +
> +       return 0;
> +}
> +
> +static struct drm_encoder *
> +       cdn_dp_connector_best_encoder(struct drm_connector *connector)
> +{
> +       struct cdn_dp_device *dp = connector_to_dp(connector);
> +
> +       return &dp->encoder;
> +}
> +
> +static int cdn_dp_connector_mode_valid(struct drm_connector *connector,
> +                                      struct drm_display_mode *mode)
> +{
> +       struct cdn_dp_device *dp = connector_to_dp(connector);
> +       struct drm_display_info *display_info = &dp->connector.display_info;
> +       u32 requested = mode->clock * display_info->bpc * 3 / 1000;
> +       u32 actual, rate;
> +
> +       rate = drm_dp_bw_code_to_link_rate(dp->link.rate);
> +       actual = rate * dp->link.num_lanes / 100;
> +
> +       /* efficiency is about 0.8 */
> +       actual = actual * 8 / 10;
> +
> +       if (requested > actual) {
> +               dev_dbg(dp->dev, "requested=%d, actual=%d, clock=%d\n",
> +                       requested, actual, mode->clock);
> +               return MODE_CLOCK_HIGH;
> +       }

This doesn't seem like something that is likely to happen given the
trained rate should be a function of the display that's attached. Is
that correct?

> +
> +       return MODE_OK;
> +}
> +
> +static struct drm_connector_helper_funcs cdn_dp_connector_helper_funcs = {
> +       .get_modes = cdn_dp_connector_get_modes,
> +       .best_encoder = cdn_dp_connector_best_encoder,
> +       .mode_valid = cdn_dp_connector_mode_valid,
> +};
> +
> +static void cdn_dp_commit(struct drm_encoder *encoder)
> +{
> +       struct cdn_dp_device *dp = encoder_to_dp(encoder);
> +
> +       if (cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE))
> +               return;
> +
> +       if (cdn_dp_config_video(dp)) {
> +               dev_err(dp->dev, "unable to config video\n");
> +               return;
> +       }
> +
> +       if (cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID))
> +               return;
> +
> +       dp->dpms_mode = DRM_MODE_DPMS_ON;
> +}
> +
> +static void cdn_dp_encoder_mode_set(struct drm_encoder *encoder,
> +                                   struct drm_display_mode *mode,
> +                                   struct drm_display_mode *adjusted)
> +{
> +       struct cdn_dp_device *dp = encoder_to_dp(encoder);
> +       struct drm_display_info *display_info = &dp->connector.display_info;
> +       struct rockchip_crtc_state *state;
> +       struct video_info *video = &dp->video_info;
> +       int ret, val;
> +
> +       switch (display_info->bpc) {
> +       case 16:
> +       case 12:
> +       case 10:
> +               video->color_depth = 10;
> +               break;
> +       case 6:
> +               video->color_depth = 6;
> +               break;
> +       default:
> +               video->color_depth = 8;
> +               break;
> +       }
> +
> +       video->color_fmt = PXL_RGB;
> +
> +       video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC);
> +       video->h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC);
> +
> +       ret = drm_of_encoder_active_endpoint_id(dp->dev->of_node, encoder);
> +       if (ret < 0) {
> +               dev_err(dp->dev, "Could not get vop id, %d", ret);
> +               return;
> +       }
> +
> +       state = to_rockchip_crtc_state(encoder->crtc->state);
> +       if (ret) {
> +               val = DP_SEL_VOP_LIT | (DP_SEL_VOP_LIT << 16);
> +               state->output_mode = ROCKCHIP_OUT_MODE_P888;
> +       } else {
> +               val = DP_SEL_VOP_LIT << 16;
> +               state->output_mode = ROCKCHIP_OUT_MODE_AAAA;
> +       }
> +       ret = regmap_write(dp->grf, DP_VOP_SEL, val);
> +       if (ret != 0)
> +               dev_err(dp->dev, "Could not write to GRF: %d\n", ret);
> +
> +       memcpy(&dp->mode, adjusted, sizeof(*mode));
> +}
> +
> +static void cdn_dp_encoder_enable(struct drm_encoder *encoder)
> +{
> +       struct cdn_dp_device *dp = encoder_to_dp(encoder);
> +
> +       if (dp->dpms_mode != DRM_MODE_DPMS_ON)
> +               cdn_dp_commit(encoder);
> +}
> +
> +static void cdn_dp_encoder_disable(struct drm_encoder *encoder)
> +{
> +       struct cdn_dp_device *dp = encoder_to_dp(encoder);
> +
> +       if (dp->dpms_mode != DRM_MODE_DPMS_OFF) {
> +               cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE);
> +               dp->dpms_mode = DRM_MODE_DPMS_OFF;
> +       }
> +}
> +
> +static int
> +cdn_dp_encoder_atomic_check(struct drm_encoder *encoder,
> +                           struct drm_crtc_state *crtc_state,
> +                           struct drm_connector_state *conn_state)
> +{
> +       struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
> +
> +       s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
> +       s->output_type = DRM_MODE_CONNECTOR_DisplayPort;
> +
> +       return 0;
> +}
> +
> +static struct drm_encoder_helper_funcs cdn_dp_encoder_helper_funcs = {
> +       .mode_set = cdn_dp_encoder_mode_set,
> +       .enable = cdn_dp_encoder_enable,
> +       .disable = cdn_dp_encoder_disable,
> +       .atomic_check = cdn_dp_encoder_atomic_check,
> +};
> +
> +static struct drm_encoder_funcs cdn_dp_encoder_funcs = {
> +       .destroy = drm_encoder_cleanup,
> +};
> +
> +static int cdn_dp_firmware_init(struct cdn_dp_device *dp)
> +{
> +       int ret;
> +       const u32 *iram_data, *dram_data;
> +       const struct firmware *fw = dp->fw;
> +       const struct cdn_firmware_header *hdr;
> +
> +       if (dp->fw_loaded)
> +               return 0;
> +
> +       hdr = (struct cdn_firmware_header *)fw->data;
> +       if (fw->size != le32_to_cpu(hdr->size_bytes))
> +               return -EINVAL;
> +
> +       iram_data = (const u32 *)(fw->data + hdr->header_size);
> +       dram_data = (const u32 *)(fw->data + hdr->header_size + hdr->iram_size);
> +
> +       ret = cdn_dp_load_firmware(dp, iram_data, hdr->iram_size,
> +                                  dram_data, hdr->dram_size);
> +       if (ret)
> +               return ret;
> +
> +       release_firmware(fw);

Move this out of here and into the same scope as request_firmware so
there are no leaks.

> +
> +       ret = cdn_dp_active(dp, true);
> +       if (ret) {
> +               dev_err(dp->dev, "active ucpu failed: %d\n", ret);
> +               return ret;
> +       }
> +
> +       return cdn_dp_event_config(dp);
> +}
> +
> +static int cdn_dp_init(struct cdn_dp_device *dp)
> +{
> +       struct device *dev = dp->dev;
> +       struct device_node *np = dev->of_node;
> +       struct platform_device *pdev = to_platform_device(dev);
> +       struct resource *res;
> +       int ret;
> +
> +       dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
> +       if (IS_ERR(dp->grf)) {
> +               dev_err(dev, "cdn-dp needs rockchip,grf property\n");
> +               return PTR_ERR(dp->grf);
> +       }
> +
> +       dp->irq = platform_get_irq(pdev, 0);
> +       if (dp->irq < 0) {
> +               dev_err(dev, "cdn-dp can not get irq\n");
> +               return dp->irq;
> +       }
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       dp->regs = devm_ioremap_resource(dev, res);
> +       if (IS_ERR(dp->regs)) {
> +               dev_err(dev, "ioremap reg failed\n");
> +               return PTR_ERR(dp->regs);
> +       }
> +
> +       dp->core_clk = devm_clk_get(dev, "core-clk");
> +       if (IS_ERR(dp->core_clk)) {
> +               dev_err(dev, "cannot get core_clk_dp\n");
> +               return PTR_ERR(dp->core_clk);
> +       }
> +
> +       dp->pclk = devm_clk_get(dev, "pclk");
> +       if (IS_ERR(dp->pclk)) {
> +               dev_err(dev, "cannot get pclk\n");
> +               return PTR_ERR(dp->pclk);
> +       }
> +
> +       dp->spdif_clk = devm_clk_get(dev, "spdif");
> +       if (IS_ERR(dp->spdif_clk)) {
> +               dev_err(dev, "cannot get spdif_clk\n");
> +               return PTR_ERR(dp->spdif_clk);
> +       }
> +
> +       dp->spdif_rst = devm_reset_control_get(dev, "spdif");
> +       if (IS_ERR(dp->spdif_rst)) {
> +               dev_err(dev, "no spdif reset control found\n");
> +               return PTR_ERR(dp->spdif_rst);
> +       }
> +
> +       dp->dpms_mode = DRM_MODE_DPMS_OFF;
> +
> +       ret = cdn_dp_clk_enable(dp);
> +       if (ret < 0)
> +               return ret;
> +
> +       dp_clock_reset_seq(dp);
> +
> +       return 0;
> +}
> +
> +static int cdn_dp_audio_hw_params(struct device *dev,
> +                                 struct hdmi_codec_daifmt *daifmt,
> +                                 struct hdmi_codec_params *params)
> +{
> +       struct cdn_dp_device *dp = dev_get_drvdata(dev);
> +       struct audio_info audio = {
> +               .sample_width = params->sample_width,
> +               .sample_rate = params->sample_rate,
> +               .channels = params->channels,
> +       };
> +       int ret;
> +
> +       if (!dp->encoder.crtc)
> +               return -ENODEV;
> +
> +       switch (daifmt->fmt) {
> +       case HDMI_I2S:
> +               audio.format = AFMT_I2S;
> +               break;
> +       case HDMI_SPDIF:
> +               audio.format = AFMT_SPDIF;
> +               break;
> +       default:
> +               dev_err(dev, "%s: Invalid format %d\n", __func__, daifmt->fmt);
> +               return -EINVAL;
> +       }
> +
> +       ret = cdn_dp_audio_config_set(dp, &audio);
> +       if (!ret)
> +               dp->audio_info = audio;
> +
> +       return ret;
> +}
> +
> +static void cdn_dp_audio_shutdown(struct device *dev)
> +{
> +       struct cdn_dp_device *dp = dev_get_drvdata(dev);
> +       int ret = cdn_dp_audio_stop(dp, &dp->audio_info);
> +
> +       if (!ret)
> +               dp->audio_info.format = AFMT_UNUSED;
> +}
> +
> +static int cdn_dp_audio_digital_mute(struct device *dev, bool enable)
> +{
> +       struct cdn_dp_device *dp = dev_get_drvdata(dev);
> +
> +       return cdn_dp_audio_mute(dp, enable);
> +}
> +
> +static int cdn_dp_audio_get_eld(struct device *dev, uint8_t *buf, size_t len)
> +{
> +       struct cdn_dp_device *dp = dev_get_drvdata(dev);
> +       struct drm_mode_config *config = &dp->encoder.dev->mode_config;
> +       struct drm_connector *connector;
> +       int ret = -ENODEV;
> +
> +       mutex_lock(&config->mutex);
> +       list_for_each_entry(connector, &config->connector_list, head) {
> +               if (&dp->encoder == connector->encoder) {
> +                       memcpy(buf, connector->eld,
> +                              min(sizeof(connector->eld), len));
> +                       ret = 0;
> +               }
> +       }
> +       mutex_unlock(&config->mutex);
> +
> +       return ret;
> +}
> +
> +static const struct hdmi_codec_ops audio_codec_ops = {
> +       .hw_params = cdn_dp_audio_hw_params,
> +       .audio_shutdown = cdn_dp_audio_shutdown,
> +       .digital_mute = cdn_dp_audio_digital_mute,
> +       .get_eld = cdn_dp_audio_get_eld,
> +};
> +
> +static int cdn_dp_audio_codec_init(struct cdn_dp_device *dp,
> +                                  struct device *dev)
> +{
> +       struct hdmi_codec_pdata codec_data = {
> +               .i2s = 1,
> +               .spdif = 1,
> +               .ops = &audio_codec_ops,
> +               .max_i2s_channels = 8,
> +       };
> +
> +       dp->audio_pdev = platform_device_register_data(
> +                        dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO,
> +                        &codec_data, sizeof(codec_data));
> +
> +       return PTR_ERR_OR_ZERO(dp->audio_pdev);
> +}
> +
> +static void cdn_dp_get_state(struct cdn_dp_device *dp, struct extcon_dev *edev)
> +{
> +       bool dfp, dptx;
> +       u8 cap_lanes;
> +
> +       dfp = extcon_get_cable_state_(edev, EXTCON_USB_HOST);
> +       dptx = extcon_get_cable_state_(edev, EXTCON_DISP_DP);
> +
> +       if (dfp && dptx)
> +               cap_lanes = 2;
> +       else if (dptx)
> +               cap_lanes = 4;
> +       else
> +               cap_lanes = 0;
> +
> +       if (cap_lanes != dp->cap_lanes) {
> +               dp->cap_lanes = cap_lanes;
> +               schedule_delayed_work(&dp->event_wq, 0);
> +       }

A get function shouldn't be scheduling work. Keep the assignment here
and move the schedule into the pd_event function.

> +}
> +
> +static int cdn_dp_pd_event(struct notifier_block *nb,
> +                          unsigned long event, void *priv)
> +{
> +       struct cdn_dp_device *dp;
> +       struct extcon_dev *edev = priv;
> +       u8 i;
> +
> +       dp = container_of(nb, struct cdn_dp_device, event_nb);
> +
> +       for (i = 0; i < MAX_PHY; i++) {
> +               if (edev == dp->extcon[i]) {
> +                       dp->port_id = i;
> +                       break;
> +               }
> +       }
> +
> +       cdn_dp_get_state(dp, edev);

There's a race here. If the cap_lanes change in between scheduling the
work and the work function running (ie, plug/unplug), the work
function will have out-of-date information. You should call this from
the work function instead.


> +
> +       return 0;
> +}
> +
> +static void cdn_dp_pd_event_wq(struct work_struct *work)
> +{
> +       struct cdn_dp_device *dp;
> +       int ret;
> +
> +       dp = container_of(work, struct cdn_dp_device, event_wq.work);
> +
> +       ret = request_firmware(&dp->fw, "cdn/dptx.bin", dp->dev);

This filename needs to be abstracted into a #define.

You're also failing to call release_firmware in a bunch of cases
below. It needs to be sorted out.


> +       if (ret == -ENOENT && !dp->fw_loaded) {

So the case where ret == -ENOENT and dp->fw_loaded is valid? Or is that a bug?

I'm not convinced you need fw_loaded stored in cdn_dp_device. It seems
like something you could either get rid of (hopefully), or convert to
a local.

> +               /*
> +                * If can not find the file, retry to load the firmware in 1
> +                * second, if still failed after 1 minute, give up.
> +                */
> +               if (dp->fw_retry++ < 60) {
> +                       schedule_delayed_work(&dp->event_wq,
> +                                             msecs_to_jiffies(HZ));

I'd rather do this as an exponential back-off so if it doesn't find
the file, it waits progressively longer.

Perhaps something like:

#define MAX_FW_WAIT_SECS 64

[...]

if (dp->fw_wait <= MAX_FW_WAIT_SECS) {
  schedule_delayed_work(&dp_>event_wq, msecs_to_jiffies(dp->fw_wait * HZ));
  dp->fw_wait *= 2;
}

> +               }
> +
> +               dev_err(dp->dev, "failed to request firmware %d\n", ret);

Print the wait/retries.

> +               return;
> +       }
> +
> +       if (!dp->cap_lanes) {
> +               if (dp->phy_status[dp->port_id])
> +                       phy_power_off(dp->phy[dp->port_id]);
> +               dp->phy_status[dp->port_id] = 0;
> +               dp->hpd_status = false;
> +               drm_helper_hpd_irq_event(dp->drm_dev);
> +               return;
> +       }
> +
> +       if (!dp->phy_status[dp->port_id]) {
> +               ret = phy_power_on(dp->phy[dp->port_id]);
> +               if (ret)

Log something in this case

> +                       return;
> +
> +               msleep(WAIT_HPD_STABLE);

300ms is a long time, do we really need to wait that long?

> +       }
> +
> +       dp->phy_status[dp->port_id] = 1;

Do you really need phy_status? It seems like if things are
well-behaved you shouldn't need it. Of course, if you're turning
on/off multiple times, perhaps there's a bug elsewhere.

> +
> +       ret = cdn_dp_firmware_init(dp);
> +       if (ret)
> +               return;
> +
> +       /* read hpd status failed, or the hpd does not exist. */
> +       ret = cdn_dp_get_hpd_status(dp);
> +       if (ret <= 0)

Log something?

> +               return;
> +
> +       /*
> +        * Set the capability and start the training process once
> +        * the hpd is detected.
> +        */
> +       ret = cdn_dp_set_host_cap(dp);
> +       if (ret) {
> +               dev_err(dp->dev, "set host capabilities failed\n");

print ret

> +               return;
> +       }
> +
> +       ret = cdn_dp_training_start(dp);
> +       if (ret) {
> +               dev_err(dp->dev, "hw lt err:%d\n", ret);

s/lt/link training/

> +               return;
> +       }
> +
> +       ret = cdn_dp_get_lt_status(dp);

Change to cdn_dp_get_training_status

> +       if (ret) {
> +               dev_err(dp->dev, "hw lt get status failed\n");

print ret

> +               return;
> +       }
> +
> +       dev_info(dp->dev, "rate:%d, lanes:%d\n",
> +                dp->link.rate, dp->link.num_lanes);
> +

I think everything from cdn_dp_firmare_init to here should be moved
out of the wq and deferred until modeset time. That way you're
training the link at the same time as you're setting the mode, which
should obviate the need to check the clocks in mode_valid.

> +       dp->hpd_status = true;
> +       drm_helper_hpd_irq_event(dp->drm_dev);
> +}
> +
> +static int cdn_dp_bind(struct device *dev, struct device *master,
> +                      void *data)
> +{
> +       struct cdn_dp_device *dp = dev_get_drvdata(dev);
> +       struct drm_encoder *encoder;
> +       struct drm_connector *connector;
> +       struct drm_device *drm_dev = data;
> +       int ret, i;
> +
> +       ret = cdn_dp_init(dp);
> +       if (ret < 0)
> +               return ret;
> +
> +       dp->drm_dev = drm_dev;
> +
> +       encoder = &dp->encoder;
> +
> +       encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
> +                                                            dev->of_node);
> +       DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
> +
> +       ret = drm_encoder_init(drm_dev, encoder, &cdn_dp_encoder_funcs,
> +                              DRM_MODE_ENCODER_TMDS, NULL);
> +       if (ret) {
> +               DRM_ERROR("failed to initialize encoder with drm\n");
> +               return ret;
> +       }
> +
> +       drm_encoder_helper_add(encoder, &cdn_dp_encoder_helper_funcs);
> +
> +       connector = &dp->connector;
> +       connector->polled = DRM_CONNECTOR_POLL_HPD;
> +       connector->dpms = DRM_MODE_DPMS_OFF;
> +
> +       ret = drm_connector_init(drm_dev, connector,
> +                                &cdn_dp_atomic_connector_funcs,
> +                                DRM_MODE_CONNECTOR_DisplayPort);
> +       if (ret) {
> +               DRM_ERROR("failed to initialize connector with drm\n");
> +               goto err_free_encoder;
> +       }
> +
> +       drm_connector_helper_add(connector, &cdn_dp_connector_helper_funcs);
> +
> +       ret = drm_mode_connector_attach_encoder(connector, encoder);
> +       if (ret) {
> +               DRM_ERROR("failed to attach connector and encoder\n");
> +               goto err_free_connector;
> +       }
> +
> +       cdn_dp_audio_codec_init(dp, dev);
> +
> +       dp->event_nb.notifier_call = cdn_dp_pd_event;
> +       INIT_DELAYED_WORK(&dp->event_wq, cdn_dp_pd_event_wq);
> +
> +       for (i = 0; i < MAX_PHY; i++) {
> +               if (IS_ERR(dp->extcon[i]))
> +                       continue;
> +
> +               ret = extcon_register_notifier(dp->extcon[i], EXTCON_DISP_DP,
> +                                              &dp->event_nb);
> +               if (ret) {
> +                       dev_err(dev, "regitster EXTCON_DISP_DP notifier err\n");
> +                       return ret;
> +               }
> +
> +               cdn_dp_get_state(dp, dp->extcon[i]);
> +       }
> +
> +       return 0;
> +
> +err_free_connector:
> +       drm_connector_cleanup(connector);
> +err_free_encoder:
> +       drm_encoder_cleanup(encoder);
> +       return ret;
> +}
> +
> +static void cdn_dp_unbind(struct device *dev, struct device *master, void *data)
> +{
> +       struct cdn_dp_device *dp = dev_get_drvdata(dev);
> +       struct drm_encoder *encoder = &dp->encoder;
> +       int i;
> +
> +       platform_device_unregister(dp->audio_pdev);
> +       cdn_dp_encoder_disable(encoder);
> +       encoder->funcs->destroy(encoder);
> +       drm_connector_unregister(&dp->connector);
> +       drm_connector_cleanup(&dp->connector);

just call connector->destroy() here

> +       drm_encoder_cleanup(encoder);

this is already called by encoder->destroy()

> +
> +       for (i = 0; i < MAX_PHY; i++) {
> +               if (!IS_ERR(dp->extcon[i]))
> +                       extcon_unregister_notifier(dp->extcon[i], EXTCON_USB,
> +                                                  &dp->event_nb);
> +       }
> +}
> +
> +static const struct component_ops cdn_dp_component_ops = {
> +       .bind = cdn_dp_bind,
> +       .unbind = cdn_dp_unbind,
> +};
> +
> +static int cdn_dp_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct cdn_dp_device *dp;
> +       u8 count = 0;
> +       int i;
> +
> +       dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
> +       if (!dp)
> +               return -ENOMEM;
> +       dp->dev = dev;
> +
> +       for (i = 0; i < MAX_PHY; i++) {
> +               dp->extcon[i] = extcon_get_edev_by_phandle(dev, i);
> +               dp->phy[i] = devm_of_phy_get_by_index(dev, dev->of_node, i);
> +
> +               if (!IS_ERR(dp->extcon[i]) && !IS_ERR(dp->phy[i]))
> +                       count++;
> +
> +               if (PTR_ERR(dp->extcon[i]) == -EPROBE_DEFER ||
> +                   PTR_ERR(dp->phy[i]) == -EPROBE_DEFER)
> +                       return -EPROBE_DEFER;
> +       }
> +
> +       if (!count) {
> +               dev_err(dev, "missing extcon or phy\n");
> +               return -EINVAL;
> +       }
> +
> +       dev_set_drvdata(dev, dp);
> +
> +       return component_add(dev, &cdn_dp_component_ops);
> +}
> +
> +static int cdn_dp_remove(struct platform_device *pdev)
> +{
> +       component_del(&pdev->dev, &cdn_dp_component_ops);
> +
> +       return 0;
> +}
> +
> +static const struct of_device_id cdn_dp_dt_ids[] = {
> +       { .compatible = "rockchip,rk3399-cdn-dp" },
> +       {}
> +};
> +
> +MODULE_DEVICE_TABLE(of, cdn_dp_dt_ids);
> +
> +static struct platform_driver cdn_dp_driver = {
> +       .probe = cdn_dp_probe,
> +       .remove = cdn_dp_remove,
> +       .driver = {
> +                  .name = "cdn-dp",
> +                  .owner = THIS_MODULE,
> +                  .of_match_table = of_match_ptr(cdn_dp_dt_ids),
> +       },
> +};
> +
> +module_platform_driver(cdn_dp_driver);
> +
> +MODULE_AUTHOR("Chris Zhong <zyw at rock-chips.com>");
> +MODULE_DESCRIPTION("cdn DP Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
> new file mode 100644
> index 0000000..31ba22d
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
> @@ -0,0 +1,113 @@
> +/*
> + * Copyright (C) 2016 Chris Zhong <zyw at rock-chips.com>
> + * Copyright (C) 2016 ROCKCHIP, Inc.
> + *
> + * 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.
> + */
> +
> +#ifndef _ROCKCHIP_EDP_CORE_H
> +#define _ROCKCHIP_EDP_CORE_H

I think the include guard should match the filename

> +
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_dp_helper.h>
> +#include <drm/drm_panel.h>
> +#include "rockchip_drm_drv.h"
> +#include "cdn-dp-reg.h"
> +
> +#define MAX_PHY                2

This should probably be stored in the .data member of of_device_id.

> +
> +enum AUDIO_FORMAT {

enum audio_format

> +       AFMT_I2S = 0,
> +       AFMT_SPDIF = 1,
> +       AFMT_UNUSED,
> +};
> +
> +struct audio_info {
> +       enum AUDIO_FORMAT format;
> +       int sample_rate;
> +       int channels;
> +       int sample_width;
> +};
> +
> +struct video_info {
> +       bool h_sync_polarity;
> +       bool v_sync_polarity;
> +       bool interlaced;
> +       int color_depth;
> +       enum VIC_PXL_ENCODING_FORMAT color_fmt;
> +};
> +
> +struct cdn_firmware_header {
> +       u32 size_bytes; /* size of the entire header+image(s) in bytes */
> +       u32 header_size; /* size of just the header in bytes */
> +       u32 iram_size; /* size of iram */
> +       u32 dram_size; /* size of dram */
> +};
> +
> +struct cdn_dp_device {
> +       struct device *dev;
> +       struct drm_device *drm_dev;
> +       struct drm_connector connector;
> +       struct drm_encoder encoder;
> +       struct drm_display_mode mode;
> +       struct platform_device *audio_pdev;
> +
> +       const struct firmware *fw;      /* cdn dp firmware */
> +       unsigned int fw_version;        /* cdn fw version */
> +       u32 fw_retry;
> +       bool fw_loaded;
> +       void __iomem *regs;
> +       struct regmap *grf;
> +       unsigned int irq;
> +       struct clk *core_clk;
> +       struct clk *pclk;
> +       struct clk *spdif_clk;
> +       struct reset_control *spdif_rst;
> +       struct audio_info audio_info;
> +       struct video_info video_info;
> +       struct extcon_dev *extcon[MAX_PHY];
> +       struct phy *phy[MAX_PHY];
> +       struct notifier_block event_nb;
> +       struct delayed_work event_wq;
> +
> +       u8 port_id;
> +       u8 cap_lanes;
> +       bool hpd_status;
> +       bool phy_status[MAX_PHY];
> +
> +       int dpms_mode;
> +       struct drm_dp_link link;
> +       bool sink_has_audio;
> +};
> +
> +void dp_clock_reset_seq(struct cdn_dp_device *dp);

cdn_ prefix here?

> +
> +void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, int clk);
> +int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
> +                        u32 i_size, const u32 *d_mem, u32 d_size);
> +int cdn_dp_active(struct cdn_dp_device *dp, bool enable);
> +int cdn_dp_set_host_cap(struct cdn_dp_device *dp);
> +int cdn_dp_event_config(struct cdn_dp_device *dp);
> +int cdn_dp_get_event(struct cdn_dp_device *dp);
> +int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
> +int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value);
> +int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr);
> +int cdn_dp_get_edid_block(void *dp, u8 *edid,
> +                         unsigned int block, size_t length);
> +int cdn_dp_training_start(struct cdn_dp_device *dp);
> +int cdn_dp_get_lt_status(struct cdn_dp_device *dp);
> +int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active);
> +int cdn_dp_config_video(struct cdn_dp_device *dp);
> +int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio);
> +int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable);
> +int cdn_dp_audio_config_set(struct cdn_dp_device *dp, struct audio_info *audio);
> +

I really don't like that you've declared these in core.h, yet defined
them in reg.c.

> +#endif  /* _ROCKCHIP_EDP_CORE_H */

Make sure you update here too


> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
> new file mode 100644
> index 0000000..8d5becd
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
> @@ -0,0 +1,740 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author: Chris Zhong <zyw at rock-chips.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * 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/clk.h>
> +#include <linux/device.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/reset.h>
> +
> +#include "cdn-dp-core.h"
> +#include "cdn-dp-reg.h"
> +
> +#define CDN_DP_SPDIF_CLK               200000000
> +#define FW_ALIVE_TIMEOUT_US            1000000
> +#define MAILBOX_TIMEOUT_US             5000000
> +
> +void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, int clk)
> +{
> +       writel(clk / 1000000, dp->regs + SW_CLK_H);
> +}
> +
> +void dp_clock_reset_seq(struct cdn_dp_device *dp)
> +{
> +       writel(0xfff, dp->regs + SOURCE_DPTX_CAR);
> +       writel(0x7, dp->regs + SOURCE_PHY_CAR);
> +       writel(0xf, dp->regs + SOURCE_PKT_CAR);
> +       writel(0xff, dp->regs + SOURCE_AIF_CAR);
> +       writel(0xf, dp->regs + SOURCE_CIPHER_CAR);
> +       writel(0x3, dp->regs + SOURCE_CRYPTO_CAR);
> +       writel(0, dp->regs + APB_INT_MASK);

Pull these hardcoded values out into #defines

> +}
> +
> +static int cdn_dp_mailbox_read(struct cdn_dp_device *dp)
> +{
> +       int val, ret;
> +
> +       if (!dp->fw_loaded)
> +               return 0;

This seems like an error. Return -errno and log a message

> +
> +       ret = readx_poll_timeout(readl, dp->regs + MAILBOX_EMPTY_ADDR,
> +                                val, !val, 1000, MAILBOX_TIMEOUT_US);

Pull the 1000 out into a #define (here and below)

> +       if (ret < 0) {
> +               dev_err(dp->dev, "failed to read mailbox, keep alive = %x\n",
> +                       readl(dp->regs + KEEP_ALIVE));
> +               return ret;
> +       }
> +
> +       return readl(dp->regs + MAILBOX0_RD_DATA) & 0xff;
> +}
> +
> +static int cdp_dp_mailbox_write(struct cdn_dp_device *dp, u8 val)
> +{
> +       int ret, full;
> +
> +       if (!dp->fw_loaded)
> +               return 0;

Same here

> +
> +       ret = readx_poll_timeout(readl, dp->regs + MAILBOX_FULL_ADDR,
> +                                full, !full, 1000, MAILBOX_TIMEOUT_US);
> +       if (ret < 0) {
> +               dev_err(dp->dev, "mailbox is full, keep alive = %x\n",
> +                       readl(dp->regs + KEEP_ALIVE));
> +               return ret;
> +       }
> +
> +       writel(val, dp->regs + MAILBOX0_WR_DATA);
> +
> +       return 0;
> +}
> +
> +static int cdn_dp_mailbox_response(struct cdn_dp_device *dp, u8 module_id,

I think _receive would be a better name than _response

> +                                  u8 opcode, u8 *buff, u8 buff_size)
> +{
> +       int ret, i = 0;
> +       u8 header[4];
> +
> +       /* read the header of the message */
> +       while (i < 4) {

for (i = 0; i < 4; i++) {

> +               ret = cdn_dp_mailbox_read(dp);
> +               if (ret < 0)
> +                       return ret;
> +
> +               header[i++] = ret;

header[i] = ret;

> +       }
> +
> +       if (opcode != header[0] || module_id != header[1] ||
> +           buff_size != ((header[2] << 8) | header[3])) {

pull header[2] << 8 | header[3] out into a local, mbox_size.

> +               dev_err(dp->dev, "mailbox response failed");
> +
> +               /*
> +                * If the message in mailbox is not what we want, we need to
> +                * clear the mailbox by read.
> +                */
> +               i = (header[2] << 8) | header[3];
> +               while (i--)

for (i = 0; i < mbox_size; i++)

> +                       if (cdn_dp_mailbox_read(dp) < 0)
> +                               break;
> +
> +               return -EINVAL;
> +       }
> +
> +       i = 0;
> +       while (i < buff_size) {

for (i = 0; i < buff_size; i++) {

> +               ret = cdn_dp_mailbox_read(dp);
> +               if (ret < 0)
> +                       return ret;
> +
> +               buff[i++] = ret;

 buff[i] = ret;

> +       }
> +
> +       return 0;


Are you always guaranteed to have the entire message in the mailbox?
If not, you might consider supporting partial messages and returning
the length read.

> +}
> +
> +static int cdn_dp_mailbox_send(struct cdn_dp_device *dp, u8 module_id,
> +                              u8 opcode, u16 size, u8 *message)
> +{
> +       u8 header[4];
> +       int ret, i;
> +
> +       header[0] = opcode;
> +       header[1] = module_id;
> +       header[2] = size >> 8;

(size >> 8) & 0xff;

> +       header[3] = size & 0xff;
> +
> +       for (i = 0; i < 4; i++) {
> +               ret = cdp_dp_mailbox_write(dp, header[i]);
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       while (size--) {

for (i = 0; i < size; i++) {

> +               ret = cdp_dp_mailbox_write(dp, *message++);

message[i]

> +               if (ret)

Log something here?

> +                       return ret;
> +       }
> +
> +       return 0;

Perhaps consider returning the number of bytes that were sent.

> +}
> +
> +static int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
> +{
> +       u8 msg[6];
> +
> +       msg[0] = (addr >> 8) & 0xff;
> +       msg[1] = addr & 0xff;
> +       msg[2] = (val >> 24) & 0xff;
> +       msg[3] = (val >> 16) & 0xff;
> +       msg[4] = (val >> 8) & 0xff;
> +       msg[5] = val & 0xff;
> +       return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_REGISTER,
> +                                  ARRAY_SIZE(msg), msg);
> +}
> +
> +int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
> +                        u32 i_size, const u32 *d_mem, u32 d_size)
> +{
> +       int i, reg, ret;

reg should be u32

> +
> +       /* reset ucpu before load firmware*/
> +       writel(APB_IRAM_PATH | APB_DRAM_PATH | APB_XT_RESET,
> +              dp->regs + APB_CTRL);
> +
> +       for (i = 0; i < i_size; i += 4)
> +               writel(*i_mem++, dp->regs + ADDR_IMEM + i);
> +
> +       for (i = 0; i < d_size; i += 4)
> +               writel(*d_mem++, dp->regs + ADDR_DMEM + i);
> +
> +       /* un-reset ucpu */
> +       writel(0, dp->regs + APB_CTRL);
> +
> +       /* check the keep alive register to make sure fw working */
> +       ret = readx_poll_timeout(readl, dp->regs + KEEP_ALIVE,
> +                                reg, reg, 2000, FW_ALIVE_TIMEOUT_US);
> +       if (ret < 0) {
> +               dev_err(dp->dev, "failed to loaded the FW reg = %x\n", reg);
> +               return -EINVAL;
> +       }
> +
> +       reg = readl(dp->regs + VER_L) & 0xff;
> +       dp->fw_version = reg;
> +       reg = readl(dp->regs + VER_H) & 0xff;
> +       dp->fw_version |= reg << 8;
> +       reg = readl(dp->regs + VER_LIB_L_ADDR) & 0xff;
> +       dp->fw_version |= reg << 16;
> +       reg = readl(dp->regs + VER_LIB_H_ADDR) & 0xff;
> +       dp->fw_version |= reg << 24;
> +

Are there any sanity checks you can do on the fw_version before returning?

> +       dp->fw_loaded = 1;
> +
> +       return 0;
> +}
> +
> +int cdn_dp_reg_write_bit(struct cdn_dp_device *dp, u16 addr, u8 start_bit,
> +                        u8 bits_no, u32 val)
> +{
> +       u8 field[8];
> +
> +       field[0] = (addr >> 8) & 0xff;
> +       field[1] = addr & 0xff;
> +       field[2] = start_bit;
> +       field[3] = bits_no;
> +       field[4] = (val >> 24) & 0xff;
> +       field[5] = (val >> 16) & 0xff;
> +       field[6] = (val >> 8) & 0xff;
> +       field[7] = val & 0xff;
> +
> +       return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_FIELD,
> +                           sizeof(field), field);
> +}
> +
> +int cdn_dp_active(struct cdn_dp_device *dp, bool enable)
> +{
> +       u8 active = enable ? 1 : 0;
> +       u8 resp;
> +       int ret;
> +
> +       /* set firmware status, 1: avtive; 0: standby */

s/avtive/active/

> +       ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_GENERAL,
> +                                 GENERAL_MAIN_CONTROL, 1, &active);
> +       if (ret)
> +               return ret;
> +
> +       ret = cdn_dp_mailbox_response(dp, MB_MODULE_ID_GENERAL,
> +                                     GENERAL_MAIN_CONTROL, &resp, 1);
> +       if (ret)
> +               return ret;
> +
> +       return resp ? 0 : -EINVAL;
> +}
> +
> +int cdn_dp_set_host_cap(struct cdn_dp_device *dp)
> +{
> +       u8 msg[8];
> +       int ret;
> +
> +       msg[0] = DP_LINK_BW_5_4;

Why is this always 5.4GHz? Does the hw support any other rates?

> +       msg[1] = dp->cap_lanes;
> +       msg[2] = VOLTAGE_LEVEL_2;
> +       msg[3] = PRE_EMPHASIS_LEVEL_3;
> +       msg[4] = PRBS7 | D10_2 | TRAINING_PTN1 | TRAINING_PTN2;

You don't need to use training pattern 3 for hbr modes?

> +       msg[5] = FAST_LT_NOT_SUPPORT;
> +       msg[6] = LANE_MAPPING_NORMAL;
> +       msg[7] = ENHANCED;
> +
> +       ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
> +                                 DPTX_SET_HOST_CAPABILITIES,
> +                                 ARRAY_SIZE(msg), msg);

ARRAY_SIZE counts the number of elements, not the size, so I think
you're better off using sizeof instead. This applies to the rest of
the dp_mailbox_send instances as well.

> +       if (ret)
> +               return ret;
> +
> +       ret = cdn_dp_reg_write(dp, DP_AUX_SWAP_INVERSION_CONTROL,
> +                              AUX_HOST_INVERT);
> +       if (ret)
> +               return ret;
> +
> +       return 0;
> +}
> +
> +int cdn_dp_event_config(struct cdn_dp_device *dp)
> +{
> +       u8 msg[5] = {0, 0, 0, 0, 0};

memset(msg, 0, sizeof(msg));

> +
> +       msg[0] = DPTX_EVENT_ENABLE_HPD | DPTX_EVENT_ENABLE_TRAINING;
> +
> +       return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_ENABLE_EVENT,
> +                                   ARRAY_SIZE(msg), msg);
> +}
> +
> +int cdn_dp_get_event(struct cdn_dp_device *dp)

Should return u32

> +{
> +       return readl(dp->regs + SW_EVENTS0);
> +}
> +
> +int cdn_dp_get_hpd_status(struct cdn_dp_device *dp)
> +{
> +       u8 status;
> +       int ret;
> +
> +       ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_HPD_STATE,
> +                                 0, NULL);
> +       if (ret)
> +               return ret;
> +
> +       ret = cdn_dp_mailbox_response(dp, MB_MODULE_ID_DP_TX,
> +                                     DPTX_HPD_STATE, &status, 1);
> +       if (ret)
> +               return ret;
> +
> +       return status;
> +}
> +
> +int cdn_dp_get_edid_block(void *data, u8 *edid,
> +                         unsigned int block, size_t length)
> +{
> +       struct cdn_dp_device *dp = data;
> +       u8 msg[2], reg[EDID_DATA + EDID_BLOCK_SIZE];
> +       int ret;
> +
> +       if (length != EDID_BLOCK_SIZE)
> +               return -EINVAL;
> +
> +       msg[0] = block / 2;
> +       msg[1] = block % 2;
> +
> +       ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_GET_EDID,
> +                                 2, msg);

sizeof(msg), msg);

> +       if (ret)
> +               return ret;
> +
> +       ret = cdn_dp_mailbox_response(dp, MB_MODULE_ID_DP_TX,
> +                                     DPTX_GET_EDID, reg,
> +                                     EDID_DATA + EDID_BLOCK_SIZE);
> +       if (ret)
> +               return ret;
> +
> +       if (reg[EDID_LENGTH_BYTE] != EDID_BLOCK_SIZE ||
> +           reg[EDID_SEGMENT_BUMBER] != block / 2) {
> +               dev_err(dp->dev, "edid block size err\n");
> +               return -EINVAL;
> +       }
> +
> +       memcpy(edid, &reg[EDID_DATA], EDID_BLOCK_SIZE);

You can avoid the intermediate reg[] buffer if you restructure the
_response/_receive mailbox function. I'd suggest splitting it into a
_validate_response() function that reads the header and makes sure the
lengths match. The second part would be a _read_response() which
simply reads the specified number of bytes from the mailbox. For
convenience, you could make cdn_dp_mailbox_receive() a helper which
calls both validate and read for times when you're reading the same
number as you're validating.

So in this function, you'd validate the response has EDID_DATA +
EDID_BLOCK_SIZE bytes, then read the EDID_DATA prefix into a local,
then read EDID_BLOCK_DATA directly into edid.

I think you'll also need a _drain function to clear out the mailbox in
case of error (otherwise the next read will fail).

> +
> +       return 0;
> +}
> +
> +int cdn_dp_training_start(struct cdn_dp_device *dp)
> +{
> +       u8 msg, event[2];
> +       unsigned long timeout;
> +       int ret;
> +
> +       msg = LINK_TRAINING_RUN;
> +
> +       /* start training */
> +       ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_TRAINING_CONTROL,
> +                                 1, &msg);
> +       if (ret)
> +               return ret;
> +
> +       /* the whole training should finish in 500ms */
> +       timeout = jiffies + msecs_to_jiffies(500);

Pull 500 out into a #define

> +       while (1) {

while(time_before(jiffies, timeout))

> +               msleep(20);

Pull 20 out into a #define

> +               ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
> +                                         DPTX_READ_EVENT, 0, NULL);
> +               if (ret)
> +                       return ret;
> +
> +               ret = cdn_dp_mailbox_response(dp, MB_MODULE_ID_DP_TX,
> +                                             DPTX_READ_EVENT, event, 2);
> +               if (ret)
> +                       return ret;
> +
> +               if (event[1] & EQ_PHASE_FINISHED)
> +                       break;

return 0

> +
> +               if (time_after(jiffies, timeout))
> +                       return -ETIMEDOUT;
> +       }
> +
> +       return 0;

return -ETIMEDOUT

> +}
> +
> +int cdn_dp_get_lt_status(struct cdn_dp_device *dp)

s/lt/training/

> +{
> +       u8 status[10];
> +       int ret;
> +
> +       ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_READ_LINK_STAT,
> +                                 0, NULL);
> +       if (ret)
> +               return ret;
> +
> +       ret = cdn_dp_mailbox_response(dp, MB_MODULE_ID_DP_TX,
> +                                     DPTX_READ_LINK_STAT, status, 10);

s/10/sizeof(status)/

> +       if (ret)
> +               return ret;
> +
> +       dp->link.rate = status[0];
> +       dp->link.num_lanes = status[1];
> +
> +       return 0;
> +}
> +
> +int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active)
> +{
> +       u8 msg;
> +
> +       msg = !!active;
> +
> +       return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_SET_VIDEO,
> +                                  1, &msg);

s/1/sizeof(msg)/

> +}
> +
> +static int cdn_dp_get_msa_misc(struct video_info *video,
> +                              struct drm_display_mode *mode)
> +{
> +       u8 val0, val1;

u8 val[2];

> +       u32 msa_misc;
> +
> +       switch (video->color_fmt) {
> +       case PXL_RGB:
> +       case Y_ONLY:
> +               val0 = 0;
> +               break;

Perhaps this would be better as listed under the default case.

> +       case YCBCR_4_4_4:
> +               /* set default color space conversion to BT601 */
> +               val0 = 6 + BT_601 * 8;
> +               break;
> +       case YCBCR_4_2_2:
> +               /* set default color space conversion to BT601 */
> +               val0 = 5 + BT_601 * 8;
> +               break;
> +       case YCBCR_4_2_0:
> +               val0 = 5;
> +               break;
> +       };
> +
> +       switch (video->color_depth) {
> +       case 6:
> +               val1 = 0;
> +               break;
> +       case 8:
> +               val1 = 1;
> +               break;
> +       case 10:
> +               val1 = 2;
> +               break;
> +       case 12:
> +               val1 = 3;
> +               break;
> +       case 16:
> +               val1 = 4;
> +               break;
> +       };
> +
> +       msa_misc = 2 * val0 + 32 * val1 +
> +                  ((video->color_fmt == Y_ONLY) ? (1 << 14) : 0);

Perhaps a stupid question, but is this documented anywhere? If not,
could you add a comment describing what you're doing?

> +
> +       return msa_misc;
> +}
> +
> +int cdn_dp_config_video(struct cdn_dp_device *dp)
> +{
> +       struct video_info *video = &dp->video_info;
> +       struct drm_display_mode *mode = &dp->mode;
> +       u64 symbol;
> +       u32 val, link_rate;
> +       u8 bit_per_pix;
> +       u8 tu_size_reg = TU_SIZE;
> +       int ret;
> +
> +       bit_per_pix = (video->color_fmt == YCBCR_4_2_2) ?
> +                     (video->color_depth * 2) : (video->color_depth * 3);
> +
> +       link_rate = drm_dp_bw_code_to_link_rate(dp->link.rate);
> +
> +       val = VIF_BYPASS_INTERLACE;
> +

remove extra line

> +       ret = cdn_dp_reg_write(dp, BND_HSYNC2VSYNC, val);
> +       if (ret)
> +               return ret;
> +
> +       ret = cdn_dp_reg_write(dp, HSYNC2VSYNC_POL_CTRL, 0);
> +       if (ret)
> +               return ret;
> +
> +       /* get a best tu_size and symbol */
> +       do {
> +               tu_size_reg += 2;
> +               symbol = tu_size_reg * mode->clock * bit_per_pix;
> +               symbol /= dp->link.num_lanes * link_rate * 8;
> +       } while ((symbol == 1) || (tu_size_reg - symbol < 4));
> +
> +       val = symbol + (tu_size_reg << 8);
> +

remove extra line

> +       ret = cdn_dp_reg_write(dp, DP_FRAMER_TU, val);
> +       if (ret)
> +               return ret;
> +
> +       switch (video->color_depth) {
> +       case 6:
> +               val = BCS_6;
> +               break;
> +       case 8:
> +               val = BCS_8;
> +               break;
> +       case 10:
> +               val = BCS_10;
> +               break;
> +       case 12:
> +               val = BCS_12;
> +               break;
> +       case 16:
> +               val = BCS_16;
> +               break;
> +       };
> +
> +       val += video->color_fmt << 8;
> +       ret = cdn_dp_reg_write(dp, DP_FRAMER_PXL_REPR, val);
> +       if (ret)
> +               return ret;
> +
> +       val = video->h_sync_polarity ? DP_FRAMER_SP_HSP : 0;
> +       val |= video->v_sync_polarity ? DP_FRAMER_SP_VSP : 0;
> +       ret = cdn_dp_reg_write(dp, DP_FRAMER_SP, val);
> +       if (ret)
> +               return ret;
> +
> +       val = (mode->hsync_start - mode->hdisplay) << 16;
> +       val |= mode->htotal - mode->hsync_end;
> +       ret = cdn_dp_reg_write(dp, DP_FRONT_BACK_PORCH, val);
> +       if (ret)
> +               return ret;
> +
> +       val = mode->hdisplay * bit_per_pix / 8;
> +       ret = cdn_dp_reg_write(dp, DP_BYTE_COUNT, val);
> +       if (ret)
> +               return ret;
> +
> +       val = mode->htotal | ((mode->htotal - mode->hsync_start) << 16);
> +       ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_0, val);
> +       if (ret)
> +               return ret;
> +
> +       val = mode->hsync_end - mode->hsync_start;
> +       val |= (mode->hdisplay << 16) | (video->h_sync_polarity << 15);
> +       ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_1, val);
> +       if (ret)
> +               return ret;
> +
> +       val = mode->vtotal;
> +       val |= ((mode->vtotal - mode->vsync_start) << 16);
> +

remove extra line

> +       ret = cdn_dp_reg_write(dp, MSA_VERTICAL_0, val);
> +       if (ret)
> +               return ret;
> +
> +       val = mode->vsync_end - mode->vsync_start;
> +       val |= mode->vdisplay << 16;
> +       val |= (video->v_sync_polarity << 15);

Make this consistent with how you assigned val for MSA_HORIZONTAL_1
(ie: split both or combine both)

> +       ret = cdn_dp_reg_write(dp, MSA_VERTICAL_1, val);
> +       if (ret)
> +               return ret;
> +
> +       val = cdn_dp_get_msa_misc(video, mode);
> +       ret = cdn_dp_reg_write(dp, MSA_MISC, val);
> +       if (ret)
> +               return ret;
> +
> +       ret = cdn_dp_reg_write(dp, STREAM_CONFIG, 1);
> +       if (ret)
> +               return ret;
> +
> +       val = mode->hsync_end - mode->hsync_start;
> +       val |= (mode->hdisplay << 16);
> +       ret = cdn_dp_reg_write(dp, DP_HORIZONTAL, val);
> +       if (ret)
> +               return ret;
> +
> +       val = mode->vtotal;
> +       val -= (mode->vtotal - mode->vdisplay);
> +       val |= (mode->vtotal - mode->vsync_start) << 16;
> +

extra line

> +       ret = cdn_dp_reg_write(dp, DP_VERTICAL_0, val);
> +       if (ret)
> +               return ret;
> +
> +       val = mode->vtotal;
> +       ret = cdn_dp_reg_write(dp, DP_VERTICAL_1, val);
> +       if (ret)
> +               return ret;
> +
> +       val =  0;
> +       return cdn_dp_reg_write_bit(dp, DP_VB_ID, 2, 1, val);
> +}
> +
> +int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio)
> +{
> +       int ret = cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, AUDIO_PACK_EN(0));
> +
remove this empty line

> +       if (ret)
> +               return ret;

insert empy line

> +       writel(0x1F0707, dp->regs + SPDIF_CTRL_ADDR);

pull this magic value into a #define

> +       writel(0, dp->regs + AUDIO_SRC_CNTL);
> +       writel(0, dp->regs + AUDIO_SRC_CNFG);
> +       writel(1, dp->regs + AUDIO_SRC_CNTL);
> +       writel(0, dp->regs + AUDIO_SRC_CNTL);
> +
> +       writel(0, dp->regs + SMPL2PKT_CNTL);
> +       writel(1, dp->regs + SMPL2PKT_CNTL);
> +       writel(0, dp->regs + SMPL2PKT_CNTL);
> +
> +       writel(1, dp->regs + FIFO_CNTL);
> +       writel(0, dp->regs + FIFO_CNTL);

Can you document why you need to toggle these bits as opposed to just
setting them to 0?

> +
> +       if (audio->format == AFMT_SPDIF)
> +               clk_disable_unprepare(dp->spdif_clk);
> +
> +       return 0;
> +}
> +
> +int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable)
> +{
> +       return cdn_dp_reg_write_bit(dp, DP_VB_ID, 4, 1, enable);
> +}
> +
> +int cdn_dp_audio_config_set(struct cdn_dp_device *dp, struct audio_info *audio)
> +{
> +       int lanes_param, i2s_port_en_val, val, i;

int lanes_param = 3, i2s_port_en_val = 0xf, i;

> +       int ret;
> +
> +       if (audio->channels == 2 && dp->link.num_lanes == 1)
> +               lanes_param = 1;
> +       else if (audio->channels == 2)
> +               lanes_param = 3;
> +       else
> +               lanes_param = 0;
> +
> +       if (audio->channels == 2)
> +               i2s_port_en_val = 1;
> +       else if (audio->channels == 4)
> +               i2s_port_en_val = 3;
> +       else
> +               i2s_port_en_val = 0xf;


I think with the above initialization values, you can replace these 2
conditionals with:

if (audio->channels == 2) {
  if (dp->link.num_lanes == 1)
    lanes_param = 3;
  i2s_port_en_val = 1;
} else if (audio->channels == 4) {
  i2s_port_en_val = 3;
}


> +
> +       if (audio->format == AFMT_SPDIF) {
> +               reset_control_assert(dp->spdif_rst);
> +               reset_control_deassert(dp->spdif_rst);
> +       }
> +
> +       ret = cdn_dp_reg_write(dp, CM_LANE_CTRL, 0x8000);

pull value out into #define (and use BIT(15) if appropriate)

> +       if (ret)
> +               return ret;
> +
> +       ret = cdn_dp_reg_write(dp, CM_CTRL, 0);
> +       if (ret)
> +               return ret;
> +
> +       if (audio->format == AFMT_I2S) {
> +               writel(0x0, dp->regs + SPDIF_CTRL_ADDR);
> +
> +               writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL);
> +
> +               val = audio->channels - 1;
> +               val |= (audio->channels / 2 - 1) << 5;
> +               val |= BIT(8);
> +               val |= lanes_param << 11;
> +               writel(val, dp->regs + SMPL2PKT_CNFG);
> +
> +               if (audio->sample_width == 16)
> +                       val = 0;
> +               else if (audio->sample_width == 24)
> +                       val = 1 << 9;
> +               else
> +                       val = 2 << 9;
> +
> +               val |= (audio->channels - 1) << 2;
> +               val |= i2s_port_en_val << 17;
> +               val |= 2 << 11;
> +               writel(val, dp->regs + AUDIO_SRC_CNFG);
> +
> +               for (i = 0; i < (audio->channels + 1) / 2; i++) {
> +                       if (audio->sample_width == 16)
> +                               val = (0x08 << 8) | (0x08 << 20);
> +                       else if (audio->sample_width == 24)
> +                               val = (0x0b << 8) | (0x0b << 20);
> +
> +                       val |= ((2 * i) << 4) | ((2 * i + 1) << 16);
> +                       writel(val, dp->regs + STTS_BIT_CH(i));
> +               }
> +
> +               switch (audio->sample_rate) {
> +               case 32000:
> +                       val = SAMPLING_FREQ(3) |
> +                             ORIGINAL_SAMP_FREQ(0xc);
> +                       break;
> +               case 44100:
> +                       val = SAMPLING_FREQ(0) |
> +                             ORIGINAL_SAMP_FREQ(0xf);
> +                       break;
> +               case 48000:
> +                       val = SAMPLING_FREQ(2) |
> +                             ORIGINAL_SAMP_FREQ(0xd);
> +                       break;
> +               case 88200:
> +                       val = SAMPLING_FREQ(8) |
> +                             ORIGINAL_SAMP_FREQ(0x7);
> +                       break;
> +               case 96000:
> +                       val = SAMPLING_FREQ(0xa) |
> +                             ORIGINAL_SAMP_FREQ(5);
> +                       break;
> +               case 176400:
> +                       val = SAMPLING_FREQ(0xc) |
> +                             ORIGINAL_SAMP_FREQ(3);
> +                       break;
> +               case 192000:
> +                       val = SAMPLING_FREQ(0xe) |
> +                             ORIGINAL_SAMP_FREQ(1);
> +                       break;
> +               }
> +               val |= 4;
> +               writel(val, dp->regs + COM_CH_STTS_BITS);
> +
> +               writel(2, dp->regs + SMPL2PKT_CNTL);
> +               writel(2, dp->regs + AUDIO_SRC_CNTL);

Pull all of this code into a new cdn_dp_audio_config_i2s() function
(with the lane_param and i2s_port_en_val assignments) and call it from
here

> +       } else {
> +               val = 0x1F0707;

Magic values need to be in #defines

> +               writel(val, dp->regs + SPDIF_CTRL_ADDR);
> +
> +               writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL);
> +
> +               val = 0x101 | (3 << 11);

Same here, these mean nothing.

> +               writel(val, dp->regs + SMPL2PKT_CNFG);
> +               writel(2, dp->regs + SMPL2PKT_CNTL);
> +
> +               val = 0x3F0707;

And here. And anywhere else I missed.

> +               writel(val, dp->regs + SPDIF_CTRL_ADDR);
> +
> +               clk_prepare_enable(dp->spdif_clk);
> +               clk_set_rate(dp->spdif_clk, CDN_DP_SPDIF_CLK);
> +       }
> +
> +       return cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, AUDIO_PACK_EN(1));
> +}
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
> new file mode 100644
> index 0000000..b33793e
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
> @@ -0,0 +1,409 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author: Chris Zhong <zyw at rock-chips.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * 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.
> + */
> +
> +#ifndef _CDN_DP_REG_H
> +#define _CDN_DP_REG_H
> +
> +#include <linux/bitops.h>
> +
> +#define ADDR_IMEM              0x10000
> +#define ADDR_DMEM              0x20000
> +
> +/* APB CFG addr */
> +#define APB_CTRL                       0
> +#define XT_INT_CTRL                    0x04
> +#define MAILBOX_FULL_ADDR              0x08
> +#define MAILBOX_EMPTY_ADDR             0x0c
> +#define MAILBOX0_WR_DATA               0x10
> +#define MAILBOX0_RD_DATA               0x14
> +#define KEEP_ALIVE                     0x18
> +#define VER_L                          0x1c
> +#define VER_H                          0x20
> +#define VER_LIB_L_ADDR                 0x24
> +#define VER_LIB_H_ADDR                 0x28
> +#define SW_DEBUG_L                     0x2c
> +#define SW_DEBUG_H                     0x30
> +#define MAILBOX_INT_MASK               0x34
> +#define MAILBOX_INT_STATUS             0x38
> +#define SW_CLK_L                       0x3c
> +#define SW_CLK_H                       0x40
> +#define SW_EVENTS0                     0x44
> +#define SW_EVENTS1                     0x48
> +#define SW_EVENTS2                     0x4c
> +#define SW_EVENTS3                     0x50
> +#define XT_OCD_CTRL                    0x60
> +#define APB_INT_MASK                   0x6c
> +#define APB_STATUS_MASK                        0x70
> +
> +/* audio decoder addr */
> +#define AUDIO_SRC_CNTL                 0x30000
> +#define AUDIO_SRC_CNFG                 0x30004
> +#define COM_CH_STTS_BITS               0x30008

Can you split these up into individual bits?

> +#define STTS_BIT_CH(x)                 (0x3000c + ((x) << 2))
> +#define SPDIF_CTRL_ADDR                        0x3004c
> +#define SPDIF_CH1_CS_3100_ADDR         0x30050
> +#define SPDIF_CH1_CS_6332_ADDR         0x30054
> +#define SPDIF_CH1_CS_9564_ADDR         0x30058
> +#define SPDIF_CH1_CS_12796_ADDR                0x3005c
> +#define SPDIF_CH1_CS_159128_ADDR       0x30060
> +#define SPDIF_CH1_CS_191160_ADDR       0x30064
> +#define SPDIF_CH2_CS_3100_ADDR         0x30068
> +#define SPDIF_CH2_CS_6332_ADDR         0x3006c
> +#define SPDIF_CH2_CS_9564_ADDR         0x30070
> +#define SPDIF_CH2_CS_12796_ADDR                0x30074
> +#define SPDIF_CH2_CS_159128_ADDR       0x30078
> +#define SPDIF_CH2_CS_191160_ADDR       0x3007c
> +#define SMPL2PKT_CNTL                  0x30080
> +#define SMPL2PKT_CNFG                  0x30084
> +#define FIFO_CNTL                      0x30088
> +#define FIFO_STTS                      0x3008c

These cntl and cnfig values should be split up so we know what the
individual bits do

> +
> +/* source pif addr */
> +#define SOURCE_PIF_WR_ADDR             0x30800
> +#define SOURCE_PIF_WR_REQ              0x30804
> +#define SOURCE_PIF_RD_ADDR             0x30808
> +#define SOURCE_PIF_RD_REQ              0x3080c
> +#define SOURCE_PIF_DATA_WR             0x30810
> +#define SOURCE_PIF_DATA_RD             0x30814
> +#define SOURCE_PIF_FIFO1_FLUSH         0x30818
> +#define SOURCE_PIF_FIFO2_FLUSH         0x3081c
> +#define SOURCE_PIF_STATUS              0x30820
> +#define SOURCE_PIF_INTERRUPT_SOURCE    0x30824
> +#define SOURCE_PIF_INTERRUPT_MASK      0x30828

Same here, split these up! Everywhere else applicable too.

> +#define SOURCE_PIF_PKT_ALLOC_REG       0x3082c
> +#define SOURCE_PIF_PKT_ALLOC_WR_EN     0x30830
> +#define SOURCE_PIF_SW_RESET            0x30834
> +
> +/* bellow registers need access by mailbox */
> +/* source car addr */
> +#define SOURCE_HDTX_CAR                        0x0900
> +#define SOURCE_DPTX_CAR                        0x0904
> +#define SOURCE_PHY_CAR                 0x0908
> +#define SOURCE_CEC_CAR                 0x090c
> +#define SOURCE_CBUS_CAR                        0x0910
> +#define SOURCE_PKT_CAR                 0x0918
> +#define SOURCE_AIF_CAR                 0x091c
> +#define SOURCE_CIPHER_CAR              0x0920
> +#define SOURCE_CRYPTO_CAR              0x0924
> +
> +/* clock meters addr */
> +#define CM_CTRL                                0x0a00
> +#define CM_I2S_CTRL                    0x0a04
> +#define CM_SPDIF_CTRL                  0x0a08
> +#define CM_VID_CTRL                    0x0a0c
> +#define CM_LANE_CTRL                   0x0a10
> +#define I2S_NM_STABLE                  0x0a14
> +#define I2S_NCTS_STABLE                        0x0a18
> +#define SPDIF_NM_STABLE                        0x0a1c
> +#define SPDIF_NCTS_STABLE              0x0a20
> +#define NMVID_MEAS_STABLE              0x0a24
> +#define I2S_MEAS                       0x0a40
> +#define SPDIF_MEAS                     0x0a80
> +#define NMVID_MEAS                     0x0ac0
> +
> +/* source vif addr */
> +#define BND_HSYNC2VSYNC                        0x0b00
> +#define HSYNC2VSYNC_F1_L1              0x0b04
> +#define HSYNC2VSYNC_F2_L1              0x0b08
> +#define HSYNC2VSYNC_STATUS             0x0b0c
> +#define HSYNC2VSYNC_POL_CTRL           0x0b10
> +
> +/* dptx phy addr */
> +#define DP_TX_PHY_CONFIG_REG           0x2000
> +#define DP_TX_PHY_STATUS_REG           0x2004
> +#define DP_TX_PHY_SW_RESET             0x2008
> +#define DP_TX_PHY_SCRAMBLER_SEED       0x200c
> +#define DP_TX_PHY_TRAINING_01_04       0x2010
> +#define DP_TX_PHY_TRAINING_05_08       0x2014
> +#define DP_TX_PHY_TRAINING_09_10       0x2018
> +#define TEST_COR                       0x23fc
> +
> +/* dptx hpd addr */
> +#define HPD_IRQ_DET_MIN_TIMER          0x2100
> +#define HPD_IRQ_DET_MAX_TIMER          0x2104
> +#define HPD_UNPLGED_DET_MIN_TIMER      0x2108
> +#define HPD_STABLE_TIMER               0x210c
> +#define HPD_FILTER_TIMER               0x2110
> +#define HPD_EVENT_MASK                 0x211c
> +#define HPD_EVENT_DET                  0x2120
> +
> +/* dpyx framer addr */
> +#define DP_FRAMER_GLOBAL_CONFIG                0x2200
> +#define DP_SW_RESET                    0x2204
> +#define DP_FRAMER_TU                   0x2208
> +#define DP_FRAMER_PXL_REPR             0x220c
> +#define DP_FRAMER_SP                   0x2210
> +#define AUDIO_PACK_CONTROL             0x2214
> +#define DP_VC_TABLE(x)                 (0x2218 + ((x) << 2))
> +#define DP_VB_ID                       0x2258
> +#define DP_MTPH_LVP_CONTROL            0x225c
> +#define DP_MTPH_SYMBOL_VALUES          0x2260
> +#define DP_MTPH_ECF_CONTROL            0x2264
> +#define DP_MTPH_ACT_CONTROL            0x2268
> +#define DP_MTPH_STATUS                 0x226c
> +#define DP_INTERRUPT_SOURCE            0x2270
> +#define DP_INTERRUPT_MASK              0x2274
> +#define DP_FRONT_BACK_PORCH            0x2278
> +#define DP_BYTE_COUNT                  0x227c
> +
> +/* dptx stream addr */
> +#define MSA_HORIZONTAL_0               0x2280
> +#define MSA_HORIZONTAL_1               0x2284
> +#define MSA_VERTICAL_0                 0x2288
> +#define MSA_VERTICAL_1                 0x228c
> +#define MSA_MISC                       0x2290
> +#define STREAM_CONFIG                  0x2294
> +#define AUDIO_PACK_STATUS              0x2298
> +#define VIF_STATUS                     0x229c
> +#define PCK_STUFF_STATUS_0             0x22a0
> +#define PCK_STUFF_STATUS_1             0x22a4
> +#define INFO_PACK_STATUS               0x22a8
> +#define RATE_GOVERNOR_STATUS           0x22ac
> +#define DP_HORIZONTAL                  0x22b0
> +#define DP_VERTICAL_0                  0x22b4
> +#define DP_VERTICAL_1                  0x22b8
> +#define DP_BLOCK_SDP                   0x22bc
> +
> +/* dptx glbl addr */
> +#define DPTX_LANE_EN                   0x2300
> +#define DPTX_ENHNCD                    0x2304
> +#define DPTX_INT_MASK                  0x2308
> +#define DPTX_INT_STATUS                        0x230c
> +
> +/* dp aux addr */
> +#define DP_AUX_HOST_CONTROL            0x2800
> +#define DP_AUX_INTERRUPT_SOURCE                0x2804
> +#define DP_AUX_INTERRUPT_MASK          0x2808
> +#define DP_AUX_SWAP_INVERSION_CONTROL  0x280c
> +#define DP_AUX_SEND_NACK_TRANSACTION   0x2810
> +#define DP_AUX_CLEAR_RX                        0x2814
> +#define DP_AUX_CLEAR_TX                        0x2818
> +#define DP_AUX_TIMER_STOP              0x281c
> +#define DP_AUX_TIMER_CLEAR             0x2820
> +#define DP_AUX_RESET_SW                        0x2824
> +#define DP_AUX_DIVIDE_2M               0x2828
> +#define DP_AUX_TX_PREACHARGE_LENGTH    0x282c
> +#define DP_AUX_FREQUENCY_1M_MAX                0x2830
> +#define DP_AUX_FREQUENCY_1M_MIN                0x2834
> +#define DP_AUX_RX_PRE_MIN              0x2838
> +#define DP_AUX_RX_PRE_MAX              0x283c
> +#define DP_AUX_TIMER_PRESET            0x2840
> +#define DP_AUX_NACK_FORMAT             0x2844
> +#define DP_AUX_TX_DATA                 0x2848
> +#define DP_AUX_RX_DATA                 0x284c
> +#define DP_AUX_TX_STATUS               0x2850
> +#define DP_AUX_RX_STATUS               0x2854
> +#define DP_AUX_RX_CYCLE_COUNTER                0x2858
> +#define DP_AUX_MAIN_STATES             0x285c
> +#define DP_AUX_MAIN_TIMER              0x2860
> +#define DP_AUX_AFE_OUT                 0x2864
> +
> +/* crypto addr */
> +#define CRYPTO_HDCP_REVISION           0x5800
> +#define HDCP_CRYPTO_CONFIG             0x5804
> +#define CRYPTO_INTERRUPT_SOURCE                0x5808
> +#define CRYPTO_INTERRUPT_MASK          0x580c
> +#define CRYPTO22_CONFIG                        0x5818
> +#define CRYPTO22_STATUS                        0x581c
> +#define SHA_256_DATA_IN                        0x583c
> +#define SHA_256_DATA_OUT_(x)           (0x5850 + ((x) << 2))
> +#define AES_32_KEY_(x)                 (0x5870 + ((x) << 2))
> +#define AES_32_DATA_IN                 0x5880
> +#define AES_32_DATA_OUT_(x)            (0x5884 + ((x) << 2))
> +#define CRYPTO14_CONFIG                        0x58a0
> +#define CRYPTO14_STATUS                        0x58a4
> +#define CRYPTO14_PRNM_OUT              0x58a8
> +#define CRYPTO14_KM_0                  0x58ac
> +#define CRYPTO14_KM_1                  0x58b0
> +#define CRYPTO14_AN_0                  0x58b4
> +#define CRYPTO14_AN_1                  0x58b8
> +#define CRYPTO14_YOUR_KSV_0            0x58bc
> +#define CRYPTO14_YOUR_KSV_1            0x58c0
> +#define CRYPTO14_MI_0                  0x58c4
> +#define CRYPTO14_MI_1                  0x58c8
> +#define CRYPTO14_TI_0                  0x58cc
> +#define CRYPTO14_KI_0                  0x58d0
> +#define CRYPTO14_KI_1                  0x58d4
> +#define CRYPTO14_BLOCKS_NUM            0x58d8
> +#define CRYPTO14_KEY_MEM_DATA_0                0x58dc
> +#define CRYPTO14_KEY_MEM_DATA_1                0x58e0
> +#define CRYPTO14_SHA1_MSG_DATA         0x58e4
> +#define CRYPTO14_SHA1_V_VALUE_(x)      (0x58e8 + ((x) << 2))
> +#define TRNG_CTRL                      0x58fc
> +#define TRNG_DATA_RDY                  0x5900
> +#define TRNG_DATA                      0x5904
> +
> +/* cipher addr */
> +#define HDCP_REVISION                  0x60000
> +#define INTERRUPT_SOURCE               0x60004
> +#define INTERRUPT_MASK                 0x60008
> +#define HDCP_CIPHER_CONFIG             0x6000c
> +#define AES_128_KEY_0                  0x60010
> +#define AES_128_KEY_1                  0x60014
> +#define AES_128_KEY_2                  0x60018
> +#define AES_128_KEY_3                  0x6001c
> +#define AES_128_RANDOM_0               0x60020
> +#define AES_128_RANDOM_1               0x60024
> +#define CIPHER14_KM_0                  0x60028
> +#define CIPHER14_KM_1                  0x6002c
> +#define CIPHER14_STATUS                        0x60030
> +#define CIPHER14_RI_PJ_STATUS          0x60034
> +#define CIPHER_MODE                    0x60038
> +#define CIPHER14_AN_0                  0x6003c
> +#define CIPHER14_AN_1                  0x60040
> +#define CIPHER22_AUTH                  0x60044
> +#define CIPHER14_R0_DP_STATUS          0x60048
> +#define CIPHER14_BOOTSTRAP             0x6004c
> +
> +#define APB_IRAM_PATH                  BIT(2)
> +#define APB_DRAM_PATH                  BIT(1)
> +#define APB_XT_RESET                   BIT(0)
> +
> +/* mailbox */
> +#define MB_OPCODE_ID                   0
> +#define MB_MODULE_ID                   1
> +#define MB_SIZE_MSB_ID                 2
> +#define MB_SIZE_LSB_ID                 3
> +#define MB_DATA_ID                     4
> +
> +#define MB_MODULE_ID_DP_TX             0x01
> +#define MB_MODULE_ID_HDCP_TX           0x07
> +#define MB_MODULE_ID_HDCP_RX           0x08
> +#define MB_MODULE_ID_HDCP_GENERAL      0x09
> +#define MB_MODULE_ID_GENERAL           0x0a
> +
> +/* general opcode */
> +#define GENERAL_MAIN_CONTROL            0x01
> +#define GENERAL_TEST_ECHO               0x02
> +#define GENERAL_BUS_SETTINGS            0x03
> +#define GENERAL_TEST_ACCESS             0x04
> +
> +#define DPTX_SET_POWER_MNG                     0x00
> +#define DPTX_SET_HOST_CAPABILITIES             0x01
> +#define DPTX_GET_EDID                          0x02
> +#define DPTX_READ_DPCD                         0x03
> +#define DPTX_WRITE_DPCD                                0x04
> +#define DPTX_ENABLE_EVENT                      0x05
> +#define DPTX_WRITE_REGISTER                    0x06
> +#define DPTX_READ_REGISTER                     0x07
> +#define DPTX_WRITE_FIELD                       0x08
> +#define DPTX_TRAINING_CONTROL                  0x09
> +#define DPTX_READ_EVENT                                0x0a
> +#define DPTX_READ_LINK_STAT                    0x0b
> +#define DPTX_SET_VIDEO                         0x0c
> +#define DPTX_SET_AUDIO                         0x0d
> +#define DPTX_GET_LAST_AUX_STAUS                        0x0e
> +#define DPTX_SET_LINK_BREAK_POINT              0x0f
> +#define DPTX_FORCE_LANES                       0x10
> +#define DPTX_HPD_STATE                         0x11
> +
> +#define DPTX_EVENT_ENABLE_HPD                  BIT(0)
> +#define DPTX_EVENT_ENABLE_TRAINING             BIT(1)
> +
> +#define LINK_TRAINING_NOT_ACTIVE               0
> +#define LINK_TRAINING_RUN                      1
> +#define LINK_TRAINING_RESTART                  2
> +
> +#define CONTROL_VIDEO_IDLE                     0
> +#define CONTROL_VIDEO_VALID                    1
> +
> +#define VIF_BYPASS_INTERLACE                   BIT(13)
> +#define INTERLACE_FMT_DET                      BIT(12)
> +#define INTERLACE_DTCT_WIN                     0x20
> +
> +#define DP_FRAMER_SP_INTERLACE_EN              BIT(2)
> +#define DP_FRAMER_SP_HSP                       BIT(1)
> +#define DP_FRAMER_SP_VSP                       BIT(0)
> +
> +/* capability */
> +#define AUX_HOST_INVERT                                3
> +#define        FAST_LT_SUPPORT                         1
> +#define FAST_LT_NOT_SUPPORT                    0
> +#define LANE_MAPPING_NORMAL                    0xe4
> +#define LANE_MAPPING_FLIPPED                   0x1b
> +#define ENHANCED                               1
> +
> +#define        FULL_LT_STARTED                         BIT(0)
> +#define FASE_LT_STARTED                                BIT(1)
> +#define CLK_RECOVERY_FINISHED                  BIT(2)
> +#define EQ_PHASE_FINISHED                      BIT(3)
> +#define FASE_LT_START_FINISHED                 BIT(4)
> +#define CLK_RECOVERY_FAILED                    BIT(5)
> +#define EQ_PHASE_FAILED                                BIT(6)
> +#define FASE_LT_FAILED                         BIT(7)
> +
> +#define DPTX_HPD_EVENT                         BIT(0)
> +#define DPTX_TRAINING_EVENT                    BIT(1)
> +#define HDCP_TX_STATUS_EVENT                   BIT(4)
> +#define HDCP2_TX_IS_KM_STORED_EVENT            BIT(5)
> +#define HDCP2_TX_STORE_KM_EVENT                        BIT(6)
> +#define HDCP_TX_IS_RECEIVER_ID_VALID_EVENT     BIT(7)
> +
> +#define EDID_LENGTH_BYTE                       0
> +#define EDID_SEGMENT_BUMBER                    1
> +#define EDID_DATA                              2
> +#define EDID_BLOCK_SIZE                                128
> +
> +#define TU_SIZE                                        30
> +
> +/* audio */
> +#define AUDIO_PACK_EN(x)                       ((x) << 8)
> +#define SAMPLING_FREQ(x)                       ((x) << 16)
> +#define ORIGINAL_SAMP_FREQ(x)                  ((x) << 24)
> +#define SYNC_WR_TO_CH_ZERO                     BIT(1)
> +
> +enum voltage_swing_level {
> +       VOLTAGE_LEVEL_0,
> +       VOLTAGE_LEVEL_1,
> +       VOLTAGE_LEVEL_2,
> +       VOLTAGE_LEVEL_3,
> +};
> +
> +enum pre_emphasis_level {
> +       PRE_EMPHASIS_LEVEL_0,
> +       PRE_EMPHASIS_LEVEL_1,
> +       PRE_EMPHASIS_LEVEL_2,
> +       PRE_EMPHASIS_LEVEL_3,
> +};
> +
> +enum pattern_set {
> +       PRBS7           = BIT(0),
> +       D10_2           = BIT(1),
> +       TRAINING_PTN1   = BIT(2),
> +       TRAINING_PTN2   = BIT(3),
> +       DP_NONE         = BIT(4)
> +};
> +
> +enum VIC_PXL_ENCODING_FORMAT {

lowercase name

> +       PXL_RGB = 0x1,
> +       YCBCR_4_4_4 = 0x2,
> +       YCBCR_4_2_2 = 0x4,
> +       YCBCR_4_2_0 = 0x8,
> +       Y_ONLY = 0x10,
> +};
> +
> +enum VIC_COLOR_DEPTH {

and here

> +       BCS_6 = 0x1,
> +       BCS_8 = 0x2,
> +       BCS_10 = 0x4,
> +       BCS_12 = 0x8,
> +       BCS_16 = 0x10,
> +};
> +
> +enum VIC_BT_TYPE {

and here

> +       BT_601 = 0x0,
> +       BT_709 = 0x1,
> +};
> +
> +#endif /* _CDN_DP_REG_H */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> index edd7ec2..98302b3 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> @@ -969,7 +969,7 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
>                 vop_dsp_hold_valid_irq_disable(vop);
>         }
>
> -       pin_pol = 0x8;
> +       pin_pol = (s->output_type == DRM_MODE_CONNECTOR_DisplayPort) ? 0 : 0x8;

There's no sense checking output_type here and then again in the
switch statement. Instead, pull 0x8 into a #define and only assign it
to pin_pol in the individual case statements below.

>         pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1;
>         pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : (1 << 1);
>         VOP_CTRL_SET(vop, pin_pol, pin_pol);
> @@ -991,6 +991,10 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
>                 VOP_CTRL_SET(vop, mipi_pin_pol, pin_pol);
>                 VOP_CTRL_SET(vop, mipi_en, 1);
>                 break;
> +       case DRM_MODE_CONNECTOR_DisplayPort:
> +               VOP_CTRL_SET(vop, dp_pin_pol, pin_pol);
> +               VOP_CTRL_SET(vop, dp_en, 1);
> +               break;
>         default:
>                 DRM_ERROR("unsupport connector_type[%d]\n", s->output_type);
>         }
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> index ff4f52e..50a045c 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> @@ -45,6 +45,7 @@ struct vop_ctrl {
>         struct vop_reg edp_en;
>         struct vop_reg hdmi_en;
>         struct vop_reg mipi_en;
> +       struct vop_reg dp_en;
>         struct vop_reg out_mode;
>         struct vop_reg dither_down;
>         struct vop_reg dither_up;
> @@ -53,6 +54,7 @@ struct vop_ctrl {
>         struct vop_reg hdmi_pin_pol;
>         struct vop_reg edp_pin_pol;
>         struct vop_reg mipi_pin_pol;
> +       struct vop_reg dp_pin_pol;
>
>         struct vop_reg htotal_pw;
>         struct vop_reg hact_st_end;
> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> index 5b1ae1f..dcf172e 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> @@ -281,6 +281,7 @@ static const struct vop_data rk3288_vop = {
>  static const struct vop_ctrl rk3399_ctrl_data = {
>         .standby = VOP_REG(RK3399_SYS_CTRL, 0x1, 22),
>         .gate_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 23),
> +       .dp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 11),
>         .rgb_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 12),
>         .hdmi_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 13),
>         .edp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 14),
> @@ -290,6 +291,7 @@ static const struct vop_ctrl rk3399_ctrl_data = {
>         .data_blank = VOP_REG(RK3399_DSP_CTRL0, 0x1, 19),
>         .out_mode = VOP_REG(RK3399_DSP_CTRL0, 0xf, 0),
>         .rgb_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16),
> +       .dp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16),
>         .hdmi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 20),
>         .edp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 24),
>         .mipi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 28),
> --
> 2.6.3
>
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel


From seanpaul at chromium.org  Wed Jul 13 08:38:03 2016
From: seanpaul at chromium.org (Sean Paul)
Date: Wed, 13 Jul 2016 08:38:03 -0700
Subject: [patch] drm/rockchip: fix a couple off by one bugs
In-Reply-To: <20160713101504.GJ29468@mwanda>
References: <20160713101504.GJ29468@mwanda>
Message-ID: <CAOw6vbK+hWcSaV14=Ri3Vsci7Hqi+krpf5d_8SJ-e3FG-9FB0g@mail.gmail.com>

On Wed, Jul 13, 2016 at 3:15 AM, Dan Carpenter <dan.carpenter at oracle.com> wrote:
> The priv->crtc_funcs[] array has ROCKCHIP_MAX_CRTC elements so > should
> be >= here.
>
> Fixes: 2048e3286f34 ('drm: rockchip: Add basic drm driver')
> Signed-off-by: Dan Carpenter <dan.carpenter at oracle.com>
>

Reviewed-by: Sean Paul <seanpaul at chromium.org>

> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> index 7fd20c0..37ca427 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> @@ -79,7 +79,7 @@ int rockchip_register_crtc_funcs(struct drm_crtc *crtc,
>         int pipe = drm_crtc_index(crtc);
>         struct rockchip_drm_private *priv = crtc->dev->dev_private;
>
> -       if (pipe > ROCKCHIP_MAX_CRTC)
> +       if (pipe >= ROCKCHIP_MAX_CRTC)
>                 return -EINVAL;
>
>         priv->crtc_funcs[pipe] = crtc_funcs;
> @@ -92,7 +92,7 @@ void rockchip_unregister_crtc_funcs(struct drm_crtc *crtc)
>         int pipe = drm_crtc_index(crtc);
>         struct rockchip_drm_private *priv = crtc->dev->dev_private;
>
> -       if (pipe > ROCKCHIP_MAX_CRTC)
> +       if (pipe >= ROCKCHIP_MAX_CRTC)
>                 return;
>
>         priv->crtc_funcs[pipe] = NULL;
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel


From cw00.choi at samsung.com  Wed Jul 13 17:49:58 2016
From: cw00.choi at samsung.com (Chanwoo Choi)
Date: Thu, 14 Jul 2016 09:49:58 +0900
Subject: [v5 PATCH 1/5] extcon: Add Type-C and DP support
In-Reply-To: <5785AD61.3070307@rock-chips.com>
References: <1468336188-565-1-git-send-email-zyw@rock-chips.com>
 <CGME20160712151003epcas1p3d60b6754866b984f943b7bea4be6d33f@epcas1p3.samsung.com>
 <1468336188-565-2-git-send-email-zyw@rock-chips.com>
 <57859532.1010002@samsung.com> <57859BDA.7080309@rock-chips.com>
 <5785A1F3.1020703@samsung.com> <5785AD61.3070307@rock-chips.com>
Message-ID: <5786E1B6.2000805@samsung.com>

Hi Chris,

On 2016? 07? 13? 11:54, Chris Zhong wrote:
> Hi Chanwoo Choi
> 
> On 07/13/2016 10:05 AM, Chanwoo Choi wrote:
>> Hi Chris,
>>
>> On 2016? 07? 13? 10:39, Chris Zhong wrote:
>>> Hi Chanwoo Choi
>>>
>>>
>>> On 07/13/2016 09:11 AM, Chanwoo Choi wrote:
>>>> Hi Chris,
>>>>
>>>> I'm now developing the extcon property on extcon-test branch.
>>>> But, it has not been completed.
>>>>
>>>> On next version, I'll remove the notification about extcon property
>>>> and only support the following two functions.
>>>> - extcon_set_cable_property()
>>>> - extcon_get_cable_property()
>>>>
>>>> Because the number of properties would be risen and the all properties
>>>> depend on the specific external connector(e.g., EXTCON_PROP_USB_VBUS
>>>> depend on the EXTCON_TYPE_USB type). When the specific external connector
>>>> is detached, extcon framework should make the property state as default state.
>>> Yes, I think getting the notification from cable state is enough, actually I am using it like you said.
>> OK.
>>
>>>> It may send the too many notification for extcon property.
>>>> For example, Assume that EXTCON_TYPE_USB has the over 20 properties,
>>>> when EXTCON_USB or EXTCON_USB_HOST is detached, extcon should send
>>>> the notification for the over 20 properties and one more notificaiton
>>>> for state of external connector.
>>>>
>>>> So, I'll send the RFC patchset without the notification of proerty.
>>>>
>>>> Lastly,
>>>> I have a comment on below.
>>>>
>>>> Thanks,
>>>> Chanwoo Choi
>>>>
>>>> On 2016? 07? 13? 00:09, Chris Zhong wrote:
>>>>> Add EXTCON_DISP_DP for the Display external connector. For Type-C
>>>>> connector the DisplayPort can work as an Alternate Mode(VESA DisplayPort
>>>>> Alt Mode on USB Type-C Standard). The Type-C support both normal and
>>>>> flipped orientation, so add a property to extcon.
>>>>>
>>>>> Signe-off-by: Chris Zhong <zyw at rock-chips.com>
>>>>>
>>>>> Signed-off-by: Chris Zhong <zyw at rock-chips.com>
>>>>> ---
>>>>>
>>>>> Changes in v5:
>>>>> - support get property
>>>>>
>>>>> Changes in v4: None
>>>>> Changes in v3: None
>>>>> Changes in v2: None
>>>>> Changes in v1: None
>>>>>
>>>>>    drivers/extcon/extcon.c | 28 ++++++++++++++++++++++++++++
>>>>>    include/linux/extcon.h  | 13 +++++++++++++
>>>>>    2 files changed, 41 insertions(+)
>>>>>
>>>>> diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c
>>>>> index a1117db..2591b28 100644
>>>>> --- a/drivers/extcon/extcon.c
>>>>> +++ b/drivers/extcon/extcon.c
>>>>> @@ -157,6 +157,11 @@ struct __extcon_info {
>>>>>            .id = EXTCON_DISP_VGA,
>>>>>            .name = "VGA",
>>>>>        },
>>>>> +    [EXTCON_DISP_DP] = {
>>>>> +        .type = EXTCON_TYPE_DISP,
>>>>> +        .id = EXTCON_DISP_DP,
>>>>> +        .name = "DP",
>>>>> +    },
>>>>>          /* Miscellaneous external connector */
>>>>>        [EXTCON_DOCK] = {
>>>>> @@ -270,6 +275,7 @@ static bool is_extcon_property_supported(unsigned int id,
>>>>>            switch (prop) {
>>>>>            case EXTCON_PROP_USB_ID:
>>>>>            case EXTCON_PROP_USB_VBUS:
>>>>> +        case EXTCON_PROP_TYPEC_POLARITY:
>>>>>                return true;
>>>>>            default:
>>>>>                break;
>>>>> @@ -286,6 +292,8 @@ static bool is_extcon_property_supported(unsigned int id,
>>>>>            }
>>>>>        case EXTCON_TYPE_DISP:
>>>>>            switch (prop) {
>>>>> +        case EXTCON_PROP_TYPEC_POLARITY:
>>>> Should EXTCON_PROP_TYPEC_POLARITY property add to both EXTCON_TYPE_USB and EXTCON_TYP_DISP?
>>>> EXTCON_PROP_TYPEC_POLARITY is the property of USB C-type?
>>> it is for USB Type-C, But at Display Port alt mode, both EXTCON_USB and EXTCON_USB_HOST may be detached. Does it support set the property to a detached cable, if so, I think move this case to EXTCON_USB is fine.
>> One external connector can set the state of one more external connector
>> if the one connector support the various functions.
>> For example, EXTCON_USB and EXTCON_CHG_USB_SDP
>> The existing extcon driver[1](e.g., max14577/max77693 etc.) set the state of both EXTCON_USB and EXTCON_CHG_USB_SDP connector at the same time
>> when usb cable is attached. Because in this case, the usb connector uses as both power supply(EXTCON_CHG_USB_SDP) and data transfer(EXTCON_USB).
>> [1] https://git.kernel.org/cgit/linux/kernel/git/chanwoo/extcon.git/commit/?h=extcon-next&id=8b45b6a0741678902810d7be95e635c210fbb198
>>
>> So, DP Alt mode uses the USB Type-C. So, When USB C-type connector is attached for DP Alt mode,
>> Maybe, you can set the following two state of connector and one property:
>> - extcon_set_cable_state(edev, [EXTCON_USB or EXTCON_USB_HOST], 1);
>> - extcon_set_cable_state(edev, EXTCON_DISP_DP, 1);
>> - extcon_set_cable_state(edev, [EXTCON_USB or EXTCON_USB_HOST], EXTCON_PROP_TYPEC_POLARITY, 0 or 1);
>>
>> Thanks,
>> Chanwoo Choi
> 
> There are 4 modes for Type-C DP alt mode:
> 1) USB host only  :
> 
> extcon_set_cable_state(edev, EXTCON_USB_HOST, 1);
> extcon_set_cable_state(edev, EXTCON_USB, 0);
> extcon_set_cable_state(edev, EXTCON_DISP_DP, 0);
> 
> 2) USB device only
> 
> extcon_set_cable_state(edev, EXTCON_USB_HOST, 0);
> extcon_set_cable_state(edev, EXTCON_USB, 1);
> extcon_set_cable_state(edev, EXTCON_DISP_DP, 0);
> 
> 3) DP only
> 
> extcon_set_cable_state(edev, EXTCON_USB_HOST, 0);
> extcon_set_cable_state(edev, EXTCON_USB, 0);
> extcon_set_cable_state(edev, EXTCON_DISP_DP, 1);
> 
> 4) USB + DP
> 
> extcon_set_cable_state(edev, EXTCON_USB_HOST, 1);
> extcon_set_cable_state(edev, EXTCON_USB, 0);
> extcon_set_cable_state(edev, EXTCON_DISP_DP, 1);
> 
> 
> for 3rd mode: DP only, there is only EXTCON_DISP_DP is attached, the EXTCON_USB_HOST
> and EXTCON_USB are detached, Can I set the property into these 2 detached cable?
> or just call extcon_set_cable_state(edev, EXTCON_DISP_DP, EXTCON_PROP_TYPEC_POLARITY, 0 or 1);

I'm thinking to solve this issue. In result, we can add one more type to specific connector.
For EXTCON_DISP_DP, we can add the two type as following. And EXTCON_PROP_TYPEC_POLARITY property
should be added to EXTCON_TYPE_USB.

+	[EXTCON_DISP_DP] = {
+		.type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB,
+		.id = EXTCON_DISP_DP,
+		.name = "DP",
+	},

So, as you mentioned, EXTCON_DISP_DP can set the EXTCON_PROP_TYPEC_POLARITY property as following:
- extcon_set_cable_state(edev, EXTCON_DISP_DP, EXTCON_PROP_TYPEC_POLARITY, 0 or 1);

I'll again developing the extcon property.

Thanks,
Chanwoo Choi



From zyw at rock-chips.com  Wed Jul 13 18:03:35 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Thu, 14 Jul 2016 09:03:35 +0800
Subject: [v5 PATCH 1/5] extcon: Add Type-C and DP support
In-Reply-To: <5786E1B6.2000805@samsung.com>
References: <1468336188-565-1-git-send-email-zyw@rock-chips.com>
 <CGME20160712151003epcas1p3d60b6754866b984f943b7bea4be6d33f@epcas1p3.samsung.com>
 <1468336188-565-2-git-send-email-zyw@rock-chips.com>
 <57859532.1010002@samsung.com> <57859BDA.7080309@rock-chips.com>
 <5785A1F3.1020703@samsung.com> <5785AD61.3070307@rock-chips.com>
 <5786E1B6.2000805@samsung.com>
Message-ID: <5786E4E7.30505@rock-chips.com>

Hi Chanwoo Choi

On 07/14/2016 08:49 AM, Chanwoo Choi wrote:
> Hi Chris,
>
> On 2016? 07? 13? 11:54, Chris Zhong wrote:
>> Hi Chanwoo Choi
>>
>> On 07/13/2016 10:05 AM, Chanwoo Choi wrote:
>>> Hi Chris,
>>>
>>> On 2016? 07? 13? 10:39, Chris Zhong wrote:
>>>> Hi Chanwoo Choi
>>>>
>>>>
>>>> On 07/13/2016 09:11 AM, Chanwoo Choi wrote:
>>>>> Hi Chris,
>>>>>
>>>>> I'm now developing the extcon property on extcon-test branch.
>>>>> But, it has not been completed.
>>>>>
>>>>> On next version, I'll remove the notification about extcon property
>>>>> and only support the following two functions.
>>>>> - extcon_set_cable_property()
>>>>> - extcon_get_cable_property()
>>>>>
>>>>> Because the number of properties would be risen and the all properties
>>>>> depend on the specific external connector(e.g., EXTCON_PROP_USB_VBUS
>>>>> depend on the EXTCON_TYPE_USB type). When the specific external connector
>>>>> is detached, extcon framework should make the property state as default state.
>>>> Yes, I think getting the notification from cable state is enough, actually I am using it like you said.
>>> OK.
>>>
>>>>> It may send the too many notification for extcon property.
>>>>> For example, Assume that EXTCON_TYPE_USB has the over 20 properties,
>>>>> when EXTCON_USB or EXTCON_USB_HOST is detached, extcon should send
>>>>> the notification for the over 20 properties and one more notificaiton
>>>>> for state of external connector.
>>>>>
>>>>> So, I'll send the RFC patchset without the notification of proerty.
>>>>>
>>>>> Lastly,
>>>>> I have a comment on below.
>>>>>
>>>>> Thanks,
>>>>> Chanwoo Choi
>>>>>
>>>>> On 2016? 07? 13? 00:09, Chris Zhong wrote:
>>>>>> Add EXTCON_DISP_DP for the Display external connector. For Type-C
>>>>>> connector the DisplayPort can work as an Alternate Mode(VESA DisplayPort
>>>>>> Alt Mode on USB Type-C Standard). The Type-C support both normal and
>>>>>> flipped orientation, so add a property to extcon.
>>>>>>
>>>>>> Signe-off-by: Chris Zhong <zyw at rock-chips.com>
>>>>>>
>>>>>> Signed-off-by: Chris Zhong <zyw at rock-chips.com>
>>>>>> ---
>>>>>>
>>>>>> Changes in v5:
>>>>>> - support get property
>>>>>>
>>>>>> Changes in v4: None
>>>>>> Changes in v3: None
>>>>>> Changes in v2: None
>>>>>> Changes in v1: None
>>>>>>
>>>>>>     drivers/extcon/extcon.c | 28 ++++++++++++++++++++++++++++
>>>>>>     include/linux/extcon.h  | 13 +++++++++++++
>>>>>>     2 files changed, 41 insertions(+)
>>>>>>
>>>>>> diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c
>>>>>> index a1117db..2591b28 100644
>>>>>> --- a/drivers/extcon/extcon.c
>>>>>> +++ b/drivers/extcon/extcon.c
>>>>>> @@ -157,6 +157,11 @@ struct __extcon_info {
>>>>>>             .id = EXTCON_DISP_VGA,
>>>>>>             .name = "VGA",
>>>>>>         },
>>>>>> +    [EXTCON_DISP_DP] = {
>>>>>> +        .type = EXTCON_TYPE_DISP,
>>>>>> +        .id = EXTCON_DISP_DP,
>>>>>> +        .name = "DP",
>>>>>> +    },
>>>>>>           /* Miscellaneous external connector */
>>>>>>         [EXTCON_DOCK] = {
>>>>>> @@ -270,6 +275,7 @@ static bool is_extcon_property_supported(unsigned int id,
>>>>>>             switch (prop) {
>>>>>>             case EXTCON_PROP_USB_ID:
>>>>>>             case EXTCON_PROP_USB_VBUS:
>>>>>> +        case EXTCON_PROP_TYPEC_POLARITY:
>>>>>>                 return true;
>>>>>>             default:
>>>>>>                 break;
>>>>>> @@ -286,6 +292,8 @@ static bool is_extcon_property_supported(unsigned int id,
>>>>>>             }
>>>>>>         case EXTCON_TYPE_DISP:
>>>>>>             switch (prop) {
>>>>>> +        case EXTCON_PROP_TYPEC_POLARITY:
>>>>> Should EXTCON_PROP_TYPEC_POLARITY property add to both EXTCON_TYPE_USB and EXTCON_TYP_DISP?
>>>>> EXTCON_PROP_TYPEC_POLARITY is the property of USB C-type?
>>>> it is for USB Type-C, But at Display Port alt mode, both EXTCON_USB and EXTCON_USB_HOST may be detached. Does it support set the property to a detached cable, if so, I think move this case to EXTCON_USB is fine.
>>> One external connector can set the state of one more external connector
>>> if the one connector support the various functions.
>>> For example, EXTCON_USB and EXTCON_CHG_USB_SDP
>>> The existing extcon driver[1](e.g., max14577/max77693 etc.) set the state of both EXTCON_USB and EXTCON_CHG_USB_SDP connector at the same time
>>> when usb cable is attached. Because in this case, the usb connector uses as both power supply(EXTCON_CHG_USB_SDP) and data transfer(EXTCON_USB).
>>> [1] https://git.kernel.org/cgit/linux/kernel/git/chanwoo/extcon.git/commit/?h=extcon-next&id=8b45b6a0741678902810d7be95e635c210fbb198
>>>
>>> So, DP Alt mode uses the USB Type-C. So, When USB C-type connector is attached for DP Alt mode,
>>> Maybe, you can set the following two state of connector and one property:
>>> - extcon_set_cable_state(edev, [EXTCON_USB or EXTCON_USB_HOST], 1);
>>> - extcon_set_cable_state(edev, EXTCON_DISP_DP, 1);
>>> - extcon_set_cable_state(edev, [EXTCON_USB or EXTCON_USB_HOST], EXTCON_PROP_TYPEC_POLARITY, 0 or 1);
>>>
>>> Thanks,
>>> Chanwoo Choi
>> There are 4 modes for Type-C DP alt mode:
>> 1) USB host only  :
>>
>> extcon_set_cable_state(edev, EXTCON_USB_HOST, 1);
>> extcon_set_cable_state(edev, EXTCON_USB, 0);
>> extcon_set_cable_state(edev, EXTCON_DISP_DP, 0);
>>
>> 2) USB device only
>>
>> extcon_set_cable_state(edev, EXTCON_USB_HOST, 0);
>> extcon_set_cable_state(edev, EXTCON_USB, 1);
>> extcon_set_cable_state(edev, EXTCON_DISP_DP, 0);
>>
>> 3) DP only
>>
>> extcon_set_cable_state(edev, EXTCON_USB_HOST, 0);
>> extcon_set_cable_state(edev, EXTCON_USB, 0);
>> extcon_set_cable_state(edev, EXTCON_DISP_DP, 1);
>>
>> 4) USB + DP
>>
>> extcon_set_cable_state(edev, EXTCON_USB_HOST, 1);
>> extcon_set_cable_state(edev, EXTCON_USB, 0);
>> extcon_set_cable_state(edev, EXTCON_DISP_DP, 1);
>>
>>
>> for 3rd mode: DP only, there is only EXTCON_DISP_DP is attached, the EXTCON_USB_HOST
>> and EXTCON_USB are detached, Can I set the property into these 2 detached cable?
>> or just call extcon_set_cable_state(edev, EXTCON_DISP_DP, EXTCON_PROP_TYPEC_POLARITY, 0 or 1);
> I'm thinking to solve this issue. In result, we can add one more type to specific connector.
> For EXTCON_DISP_DP, we can add the two type as following. And EXTCON_PROP_TYPEC_POLARITY property
> should be added to EXTCON_TYPE_USB.
>
> +	[EXTCON_DISP_DP] = {
> +		.type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB,
> +		.id = EXTCON_DISP_DP,
> +		.name = "DP",
> +	},
>
> So, as you mentioned, EXTCON_DISP_DP can set the EXTCON_PROP_TYPEC_POLARITY property as following:
> - extcon_set_cable_state(edev, EXTCON_DISP_DP, EXTCON_PROP_TYPEC_POLARITY, 0 or 1);
>
> I'll again developing the extcon property.
>
> Thanks,
> Chanwoo Choi
>
Nice idea, But I am thinking about is it compatible with real 
DisplayPort without Type-C.
Maybe we should add a new cable: EXTCON_DISP_DP_ALT

+	[EXTCON_DISP_DP] = {
+		.type = EXTCON_TYPE_DISP,
+		.id = EXTCON_DISP_DP,
+		.name = "DP",
+	},

+	[EXTCON_DISP_DP_ALT] = {
+		.type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB,
+		.id = EXTCON_DISP_DP,
+		.name = "DP ALT",
+	},



>
>




From cw00.choi at samsung.com  Wed Jul 13 18:15:01 2016
From: cw00.choi at samsung.com (Chanwoo Choi)
Date: Thu, 14 Jul 2016 10:15:01 +0900
Subject: [v5 PATCH 1/5] extcon: Add Type-C and DP support
In-Reply-To: <5786E4E7.30505@rock-chips.com>
References: <1468336188-565-1-git-send-email-zyw@rock-chips.com>
 <CGME20160712151003epcas1p3d60b6754866b984f943b7bea4be6d33f@epcas1p3.samsung.com>
 <1468336188-565-2-git-send-email-zyw@rock-chips.com>
 <57859532.1010002@samsung.com> <57859BDA.7080309@rock-chips.com>
 <5785A1F3.1020703@samsung.com> <5785AD61.3070307@rock-chips.com>
 <5786E1B6.2000805@samsung.com> <5786E4E7.30505@rock-chips.com>
Message-ID: <5786E795.6040504@samsung.com>

Hi Chris,

[snip]

>>>> Thanks,
>>>> Chanwoo Choi
>>> There are 4 modes for Type-C DP alt mode:
>>> 1) USB host only  :
>>>
>>> extcon_set_cable_state(edev, EXTCON_USB_HOST, 1);
>>> extcon_set_cable_state(edev, EXTCON_USB, 0);
>>> extcon_set_cable_state(edev, EXTCON_DISP_DP, 0);
>>>
>>> 2) USB device only
>>>
>>> extcon_set_cable_state(edev, EXTCON_USB_HOST, 0);
>>> extcon_set_cable_state(edev, EXTCON_USB, 1);
>>> extcon_set_cable_state(edev, EXTCON_DISP_DP, 0);
>>>
>>> 3) DP only
>>>
>>> extcon_set_cable_state(edev, EXTCON_USB_HOST, 0);
>>> extcon_set_cable_state(edev, EXTCON_USB, 0);
>>> extcon_set_cable_state(edev, EXTCON_DISP_DP, 1);
>>>
>>> 4) USB + DP
>>>
>>> extcon_set_cable_state(edev, EXTCON_USB_HOST, 1);
>>> extcon_set_cable_state(edev, EXTCON_USB, 0);
>>> extcon_set_cable_state(edev, EXTCON_DISP_DP, 1);
>>>
>>>
>>> for 3rd mode: DP only, there is only EXTCON_DISP_DP is attached, the EXTCON_USB_HOST
>>> and EXTCON_USB are detached, Can I set the property into these 2 detached cable?
>>> or just call extcon_set_cable_state(edev, EXTCON_DISP_DP, EXTCON_PROP_TYPEC_POLARITY, 0 or 1);
>> I'm thinking to solve this issue. In result, we can add one more type to specific connector.
>> For EXTCON_DISP_DP, we can add the two type as following. And EXTCON_PROP_TYPEC_POLARITY property
>> should be added to EXTCON_TYPE_USB.
>>
>> +    [EXTCON_DISP_DP] = {
>> +        .type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB,
>> +        .id = EXTCON_DISP_DP,
>> +        .name = "DP",
>> +    },
>>
>> So, as you mentioned, EXTCON_DISP_DP can set the EXTCON_PROP_TYPEC_POLARITY property as following:
>> - extcon_set_cable_state(edev, EXTCON_DISP_DP, EXTCON_PROP_TYPEC_POLARITY, 0 or 1);
>>
>> I'll again developing the extcon property.
>>
>> Thanks,
>> Chanwoo Choi
>>
> Nice idea, But I am thinking about is it compatible with real DisplayPort without Type-C.
> Maybe we should add a new cable: EXTCON_DISP_DP_ALT
> 
> +    [EXTCON_DISP_DP] = {
> +        .type = EXTCON_TYPE_DISP,
> +        .id = EXTCON_DISP_DP,
> +        .name = "DP",
> +    },
> 
> +    [EXTCON_DISP_DP_ALT] = {
> +        .type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB,
> +        .id = EXTCON_DISP_DP,
> +        .name = "DP ALT",
> +    },

EXTCON_DISP_DP_ALT means EXTCON_DISP_DP connector with EXTCON_PROP_TYPEC_POLARITY property.
So, we can explain the DP alternative mode without EXTCON_DISP_DP_ALT.

I'm wondering to add the new EXTCON_DISP_DP_ALT connector
because alternative mode was defined as mode instead of new h/w connector type.

Thanks,
Chanwoo Choi






From caesar.upstream at gmail.com  Wed Jul 13 18:14:57 2016
From: caesar.upstream at gmail.com (Caesar Wang)
Date: Thu, 14 Jul 2016 09:14:57 +0800
Subject: [PATCH v2 0/2] arm64: dts: rockchip: Support PMU for rk3399 SoCs
In-Reply-To: <20160706112444.GB14657@leverpostej>
References: <1467792357-9749-1-git-send-email-wxt@rock-chips.com>
 <20160706112444.GB14657@leverpostej>
Message-ID: <5786E791.1010906@gmail.com>

Hi,

On 2016?07?06? 19:24, Mark Rutland wrote:
> On Wed, Jul 06, 2016 at 04:05:55PM +0800, Caesar Wang wrote:
>> Hello Heiko, Marc & ARM guys
> Hi,
>
> These patches themselves look fine, so FWIW:
>
> Acked-by: Mark Rutland <mark.rutland at arm.com>

Thanks your ack.

>
> The prerequisite patch from Marc [1] also largely looks fine, but there
> are some minor details to sort out [2], so at this point in time I'm
> guesssing this is all v4.9 material.
>
> Thanks,
> Mark.
>
> [1] http://lists.infradead.org/pipermail/linux-arm-kernel/2016-July/440647.html
> [2] http://lists.infradead.org/pipermail/linux-arm-kernel/2016-July/441495.html

Okay, I saw the Marc's patch had been merged on next kernel.:-)


-Caesar
>
>> When Jay first submitted the rk3399.dtsi upstream
>> <https://patchwork.kernel.org/patch/8885821/> he had the PMU node in there,
>> but then took it out because the upstream binding wasn't done yet.
>> It looks as if the upstream stuff has landed, since in linux/master I see:
>> 287e9357abcc DT/arm,gic-v3: Documment PPI partition support
>> e3825ba1af3a irqchip/gic-v3: Add support for partitioned PPIs
>> 9e2c986cb460 irqchip: Add per-cpu interrupt partitioning library
>> 222df54fd8b7 genirq: Allow the affinity of a percpu interrupt to be set/retrieved
>> 651e8b54abde irqdomain: Allow domain matching on irq_fwspec
>>
>> This series patches add to support the rk3399 SoCs PMU.
>> I pick up the https://patchwork.kernel.org/patch/9209369/.
>>
>> As do some tests with ChromeOs for my rk3399 board.
>> Tested with linus master 4.7-rc6 kernel on rk3399 board.
>> https://github.com/Caesar-github/rockchip/tree/rk3399/pmu-upstream
>>
>> localhost / # perf list
>>
>> List of pre-defined events (to be used in -e):
>> cpu-cycles OR cycles [Hardware event]
>> instructions [Hardware event]
>> cache-references [Hardware event]
>> cache-misses [Hardware event]
>> branch-instructions OR branches [Hardware event]
>> branch-misses [Hardware event]
>> bus-cycles [Hardware event]
>> ...
>>
>> perf stat --cpu 0/1/2/3..... to minitor
>> e.g. cpu0;
>>
>> localhost / # perf stat --cpu 0
>>
>> Performance counter stats for 'CPU(s) 0':
>>
>> 3374.917571 task-clock (msec) # 1.001 CPUs utilized [100.00%]
>> 20 context-switches # 0.006 K/sec [100.00%]
>> 2 cpu-migrations # 0.001 K/sec [100.00%]
>> 55 page-faults # 0.016 K/sec
>> 7151843 cycles # 0.002 GHz [100.00%]
>> <not supported> stalled-cycles-frontend
>> <not supported> stalled-cycles-backend
>> 4272536 instructions # 0.60 insns per cycle [100.00%]
>> 568406 branches # 0.168 M/sec [100.00%]
>> 65652 branch-misses # 11.55% of all branches
>>
>> Also, 'perf top' to monitor the PMU interrupts from cpus
>>
>> -Caesar
>>
>>
>> Changes in v2:
>> - AS Mark comments on https://patchwork.kernel.org/patch/9209369/
>>    remove the interrupt-affinity property, we need depend on Marc' perf
>>    code on https://patchwork.kernel.org/patch/9209369/.
>>
>> Caesar Wang (2):
>>    arm64: dts: rockchip: change all interrupts cells for 4 on rk3399 SoCs
>>    arm64: dts: rockchip: support the pmu node for rk3399
>>
>>   arch/arm64/boot/dts/rockchip/rk3399.dtsi | 118 ++++++++++++++++++-------------
>>   1 file changed, 69 insertions(+), 49 deletions(-)
>>
>> -- 
>> 1.9.1
>>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip



From zyw at rock-chips.com  Wed Jul 13 19:07:55 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Thu, 14 Jul 2016 10:07:55 +0800
Subject: [v5 PATCH 1/5] extcon: Add Type-C and DP support
In-Reply-To: <5786E795.6040504@samsung.com>
References: <1468336188-565-1-git-send-email-zyw@rock-chips.com>
 <CGME20160712151003epcas1p3d60b6754866b984f943b7bea4be6d33f@epcas1p3.samsung.com>
 <1468336188-565-2-git-send-email-zyw@rock-chips.com>
 <57859532.1010002@samsung.com> <57859BDA.7080309@rock-chips.com>
 <5785A1F3.1020703@samsung.com> <5785AD61.3070307@rock-chips.com>
 <5786E1B6.2000805@samsung.com> <5786E4E7.30505@rock-chips.com>
 <5786E795.6040504@samsung.com>
Message-ID: <5786F3FB.5010608@rock-chips.com>

Hi Chanwoo Choi

On 07/14/2016 09:15 AM, Chanwoo Choi wrote:
> Hi Chris,
>
> [snip]
>
>>>>> Thanks,
>>>>> Chanwoo Choi
>>>> There are 4 modes for Type-C DP alt mode:
>>>> 1) USB host only  :
>>>>
>>>> extcon_set_cable_state(edev, EXTCON_USB_HOST, 1);
>>>> extcon_set_cable_state(edev, EXTCON_USB, 0);
>>>> extcon_set_cable_state(edev, EXTCON_DISP_DP, 0);
>>>>
>>>> 2) USB device only
>>>>
>>>> extcon_set_cable_state(edev, EXTCON_USB_HOST, 0);
>>>> extcon_set_cable_state(edev, EXTCON_USB, 1);
>>>> extcon_set_cable_state(edev, EXTCON_DISP_DP, 0);
>>>>
>>>> 3) DP only
>>>>
>>>> extcon_set_cable_state(edev, EXTCON_USB_HOST, 0);
>>>> extcon_set_cable_state(edev, EXTCON_USB, 0);
>>>> extcon_set_cable_state(edev, EXTCON_DISP_DP, 1);
>>>>
>>>> 4) USB + DP
>>>>
>>>> extcon_set_cable_state(edev, EXTCON_USB_HOST, 1);
>>>> extcon_set_cable_state(edev, EXTCON_USB, 0);
>>>> extcon_set_cable_state(edev, EXTCON_DISP_DP, 1);
>>>>
>>>>
>>>> for 3rd mode: DP only, there is only EXTCON_DISP_DP is attached, the EXTCON_USB_HOST
>>>> and EXTCON_USB are detached, Can I set the property into these 2 detached cable?
>>>> or just call extcon_set_cable_state(edev, EXTCON_DISP_DP, EXTCON_PROP_TYPEC_POLARITY, 0 or 1);
>>> I'm thinking to solve this issue. In result, we can add one more type to specific connector.
>>> For EXTCON_DISP_DP, we can add the two type as following. And EXTCON_PROP_TYPEC_POLARITY property
>>> should be added to EXTCON_TYPE_USB.
>>>
>>> +    [EXTCON_DISP_DP] = {
>>> +        .type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB,
>>> +        .id = EXTCON_DISP_DP,
>>> +        .name = "DP",
>>> +    },
>>>
>>> So, as you mentioned, EXTCON_DISP_DP can set the EXTCON_PROP_TYPEC_POLARITY property as following:
>>> - extcon_set_cable_state(edev, EXTCON_DISP_DP, EXTCON_PROP_TYPEC_POLARITY, 0 or 1);
>>>
>>> I'll again developing the extcon property.
>>>
>>> Thanks,
>>> Chanwoo Choi
>>>
>> Nice idea, But I am thinking about is it compatible with real DisplayPort without Type-C.
>> Maybe we should add a new cable: EXTCON_DISP_DP_ALT
>>
>> +    [EXTCON_DISP_DP] = {
>> +        .type = EXTCON_TYPE_DISP,
>> +        .id = EXTCON_DISP_DP,
>> +        .name = "DP",
>> +    },
>>
>> +    [EXTCON_DISP_DP_ALT] = {
>> +        .type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB,
>> +        .id = EXTCON_DISP_DP,
>> +        .name = "DP ALT",
>> +    },
> EXTCON_DISP_DP_ALT means EXTCON_DISP_DP connector with EXTCON_PROP_TYPEC_POLARITY property.
> So, we can explain the DP alternative mode without EXTCON_DISP_DP_ALT.
>
> I'm wondering to add the new EXTCON_DISP_DP_ALT connector
> because alternative mode was defined as mode instead of new h/w connector type.
>
> Thanks,
> Chanwoo Choi
>
>
Okay, so I think some comment for EXTCON_DISP_DP is necessary and enough.
>
>




From cw00.choi at samsung.com  Wed Jul 13 19:10:35 2016
From: cw00.choi at samsung.com (Chanwoo Choi)
Date: Thu, 14 Jul 2016 11:10:35 +0900
Subject: [v5 PATCH 1/5] extcon: Add Type-C and DP support
In-Reply-To: <5786F3FB.5010608@rock-chips.com>
References: <1468336188-565-1-git-send-email-zyw@rock-chips.com>
 <CGME20160712151003epcas1p3d60b6754866b984f943b7bea4be6d33f@epcas1p3.samsung.com>
 <1468336188-565-2-git-send-email-zyw@rock-chips.com>
 <57859532.1010002@samsung.com> <57859BDA.7080309@rock-chips.com>
 <5785A1F3.1020703@samsung.com> <5785AD61.3070307@rock-chips.com>
 <5786E1B6.2000805@samsung.com> <5786E4E7.30505@rock-chips.com>
 <5786E795.6040504@samsung.com> <5786F3FB.5010608@rock-chips.com>
Message-ID: <5786F49B.20107@samsung.com>

Hi Chris,

On 2016? 07? 14? 11:07, Chris Zhong wrote:
> Hi Chanwoo Choi
> 
> On 07/14/2016 09:15 AM, Chanwoo Choi wrote:
>> Hi Chris,
>>
>> [snip]
>>
>>>>>> Thanks,
>>>>>> Chanwoo Choi
>>>>> There are 4 modes for Type-C DP alt mode:
>>>>> 1) USB host only  :
>>>>>
>>>>> extcon_set_cable_state(edev, EXTCON_USB_HOST, 1);
>>>>> extcon_set_cable_state(edev, EXTCON_USB, 0);
>>>>> extcon_set_cable_state(edev, EXTCON_DISP_DP, 0);
>>>>>
>>>>> 2) USB device only
>>>>>
>>>>> extcon_set_cable_state(edev, EXTCON_USB_HOST, 0);
>>>>> extcon_set_cable_state(edev, EXTCON_USB, 1);
>>>>> extcon_set_cable_state(edev, EXTCON_DISP_DP, 0);
>>>>>
>>>>> 3) DP only
>>>>>
>>>>> extcon_set_cable_state(edev, EXTCON_USB_HOST, 0);
>>>>> extcon_set_cable_state(edev, EXTCON_USB, 0);
>>>>> extcon_set_cable_state(edev, EXTCON_DISP_DP, 1);
>>>>>
>>>>> 4) USB + DP
>>>>>
>>>>> extcon_set_cable_state(edev, EXTCON_USB_HOST, 1);
>>>>> extcon_set_cable_state(edev, EXTCON_USB, 0);
>>>>> extcon_set_cable_state(edev, EXTCON_DISP_DP, 1);
>>>>>
>>>>>
>>>>> for 3rd mode: DP only, there is only EXTCON_DISP_DP is attached, the EXTCON_USB_HOST
>>>>> and EXTCON_USB are detached, Can I set the property into these 2 detached cable?
>>>>> or just call extcon_set_cable_state(edev, EXTCON_DISP_DP, EXTCON_PROP_TYPEC_POLARITY, 0 or 1);
>>>> I'm thinking to solve this issue. In result, we can add one more type to specific connector.
>>>> For EXTCON_DISP_DP, we can add the two type as following. And EXTCON_PROP_TYPEC_POLARITY property
>>>> should be added to EXTCON_TYPE_USB.
>>>>
>>>> +    [EXTCON_DISP_DP] = {
>>>> +        .type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB,
>>>> +        .id = EXTCON_DISP_DP,
>>>> +        .name = "DP",
>>>> +    },
>>>>
>>>> So, as you mentioned, EXTCON_DISP_DP can set the EXTCON_PROP_TYPEC_POLARITY property as following:
>>>> - extcon_set_cable_state(edev, EXTCON_DISP_DP, EXTCON_PROP_TYPEC_POLARITY, 0 or 1);
>>>>
>>>> I'll again developing the extcon property.
>>>>
>>>> Thanks,
>>>> Chanwoo Choi
>>>>
>>> Nice idea, But I am thinking about is it compatible with real DisplayPort without Type-C.
>>> Maybe we should add a new cable: EXTCON_DISP_DP_ALT
>>>
>>> +    [EXTCON_DISP_DP] = {
>>> +        .type = EXTCON_TYPE_DISP,
>>> +        .id = EXTCON_DISP_DP,
>>> +        .name = "DP",
>>> +    },
>>>
>>> +    [EXTCON_DISP_DP_ALT] = {
>>> +        .type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB,
>>> +        .id = EXTCON_DISP_DP,
>>> +        .name = "DP ALT",
>>> +    },
>> EXTCON_DISP_DP_ALT means EXTCON_DISP_DP connector with EXTCON_PROP_TYPEC_POLARITY property.
>> So, we can explain the DP alternative mode without EXTCON_DISP_DP_ALT.
>>
>> I'm wondering to add the new EXTCON_DISP_DP_ALT connector
>> because alternative mode was defined as mode instead of new h/w connector type.
>>
>> Thanks,
>> Chanwoo Choi
>>
>>
> Okay, so I think some comment for EXTCON_DISP_DP is necessary and enough.

Okay. I'll do that

Thanks,
Chanwoo Choi



From mark.yao at rock-chips.com  Wed Jul 13 19:22:23 2016
From: mark.yao at rock-chips.com (Mark yao)
Date: Thu, 14 Jul 2016 10:22:23 +0800
Subject: [patch] drm/rockchip: fix a couple off by one bugs
In-Reply-To: <20160713101504.GJ29468@mwanda>
References: <20160713101504.GJ29468@mwanda>
Message-ID: <5786F75F.30703@rock-chips.com>

On 2016?07?13? 18:15, Dan Carpenter wrote:
> The priv->crtc_funcs[] array has ROCKCHIP_MAX_CRTC elements so > should
> be >= here.
>
> Fixes: 2048e3286f34 ('drm: rockchip: Add basic drm driver')
> Signed-off-by: Dan Carpenter <dan.carpenter at oracle.com>

Thanks for the fix, applied to my drm-fixes.

> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> index 7fd20c0..37ca427 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> @@ -79,7 +79,7 @@ int rockchip_register_crtc_funcs(struct drm_crtc *crtc,
>   	int pipe = drm_crtc_index(crtc);
>   	struct rockchip_drm_private *priv = crtc->dev->dev_private;
>   
> -	if (pipe > ROCKCHIP_MAX_CRTC)
> +	if (pipe >= ROCKCHIP_MAX_CRTC)
>   		return -EINVAL;
>   
>   	priv->crtc_funcs[pipe] = crtc_funcs;
> @@ -92,7 +92,7 @@ void rockchip_unregister_crtc_funcs(struct drm_crtc *crtc)
>   	int pipe = drm_crtc_index(crtc);
>   	struct rockchip_drm_private *priv = crtc->dev->dev_private;
>   
> -	if (pipe > ROCKCHIP_MAX_CRTC)
> +	if (pipe >= ROCKCHIP_MAX_CRTC)
>   		return;
>   
>   	priv->crtc_funcs[pipe] = NULL;
>
>
>


-- 
?ark Yao




From zyw at rock-chips.com  Wed Jul 13 20:08:00 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Thu, 14 Jul 2016 11:08:00 +0800
Subject: [v5 PATCH 5/5] drm/rockchip: cdn-dp: add cdn DP support for rk3399
In-Reply-To: <CAOw6vb+=F=X1MeWhUbs42MzP_CFfMkYrFUqKmWjVwwohufFOhw@mail.gmail.com>
References: <1468336188-565-1-git-send-email-zyw@rock-chips.com>
 <1468336188-565-6-git-send-email-zyw@rock-chips.com>
 <CAOw6vb+=F=X1MeWhUbs42MzP_CFfMkYrFUqKmWjVwwohufFOhw@mail.gmail.com>
Message-ID: <57870210.4020307@rock-chips.com>

Hi Sean

Thanks for your detailed review. I'm working to modify most of code 
according to comment.
And there is reply for some comment

On 07/13/2016 09:59 PM, Sean Paul wrote:
> On Tue, Jul 12, 2016 at 8:09 AM, Chris Zhong <zyw at rock-chips.com> wrote:
>> Add support for cdn DP controller which is embedded in the rk3399
>> SoCs. The DP is compliant with DisplayPort Specification,
>> Version 1.3, This IP is compatible with the rockchip type-c PHY IP.
>> There is a uCPU in DP controller, it need a firmware to work,
>> please put the firmware file to /lib/firmware/cdn/dptx.bin. The
>> uCPU in charge of aux communication and link training, the host use
>> mailbox to communicate with the ucpu.
>> The dclk pin_pol of vop must not be invert for DP.
>>
>> Signed-off-by: Chris Zhong <zyw at rock-chips.com>
>>
>> ---
>>
>> Changes in v5:
>> - alphabetical order
>> - do not use long, use u32 or u64
>> - return MODE_CLOCK_HIGH when requested > actual
>> - Optimized Coding Style
>> - add a formula to get better tu size and symbol value.
>>
>> Changes in v4:
>> - use phy framework to control DP phy
>> - support 2 phys
>>
>> Changes in v3:
>> - use EXTCON_DISP_DP and EXTCON_DISP_DP_ALT cable to get dp port state.
>> - reset spdif before config it
>> - modify the firmware clk to 100Mhz
>> - retry load firmware if fw file is requested too early
>>
>> Changes in v2:
>> - Alphabetic order
>> - remove excess error message
>> - use define clk_rate
>> - check all return value
>> - remove dev_set_name(dp->dev, "cdn-dp");
>> - use schedule_delayed_work
>> - remove never-called functions
>> - remove some unnecessary ()
>>
>> Changes in v1:
>> - use extcon API
>> - use hdmi-codec for the DP Asoc
>> - do not initialize the "ret"
>> - printk a err log when drm_of_encoder_active_endpoint_id
>> - modify the dclk pin_pol to a single line
>>
>>   drivers/gpu/drm/rockchip/Kconfig            |   9 +
>>   drivers/gpu/drm/rockchip/Makefile           |   1 +
>>   drivers/gpu/drm/rockchip/cdn-dp-core.c      | 761 ++++++++++++++++++++++++++++
>>   drivers/gpu/drm/rockchip/cdn-dp-core.h      | 113 +++++
>>   drivers/gpu/drm/rockchip/cdn-dp-reg.c       | 740 +++++++++++++++++++++++++++
>>   drivers/gpu/drm/rockchip/cdn-dp-reg.h       | 409 +++++++++++++++
> Could you explain the file naming convention in the rk driver? We've
> got analogix_dp-rockchip.c, dw_hdmi-rockchip.c, dw-mipi-dsi.c, and now
> cdn-dp-(core|reg).[ch]. I'm honestly not sure whether these filenames
> are consistent with the rest, but bleh.
>
cdn is the IP vendor's name
dp is the controller's name.

>>   drivers/gpu/drm/rockchip/rockchip_drm_vop.c |   6 +-
>>   drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   2 +
>>   drivers/gpu/drm/rockchip/rockchip_vop_reg.c |   2 +
>>   9 files changed, 2042 insertions(+), 1 deletion(-)
>>   create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.c
>>   create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.h
>>   create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.c
>>   create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.h
>>
>> diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
>> index d30bdc3..20da9a8 100644
>> --- a/drivers/gpu/drm/rockchip/Kconfig
>> +++ b/drivers/gpu/drm/rockchip/Kconfig
>> @@ -25,6 +25,15 @@ config ROCKCHIP_ANALOGIX_DP
>>            for the Analogix Core DP driver. If you want to enable DP
>>            on RK3288 based SoC, you should selet this option.
>>
>> +config ROCKCHIP_CDN_DP
>> +        tristate "Rockchip cdn DP"
>> +        depends on DRM_ROCKCHIP
>> +        help
>> +         This selects support for Rockchip SoC specific extensions
>> +         for the cdn Dp driver. If you want to enable Dp on
> s/Dp/DP/
>
>> +         RK3399 based SoC, you should selet this
> s/selet/select/
>
>> +         option.
>> +
>>   config ROCKCHIP_DW_HDMI
>>           tristate "Rockchip specific extensions for Synopsys DW HDMI"
>>           depends on DRM_ROCKCHIP
>> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
>> index 05d0713..abdecd5 100644
>> --- a/drivers/gpu/drm/rockchip/Makefile
>> +++ b/drivers/gpu/drm/rockchip/Makefile
>> @@ -7,6 +7,7 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
>>   rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
>>
>>   obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
>> +obj-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
>>   obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
>>   obj-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
>>   obj-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
>> new file mode 100644
>> index 0000000..5b8a15e
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
>> @@ -0,0 +1,761 @@
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + * Author: Chris Zhong <zyw at rock-chips.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * 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 <drm/drmP.h>
>> +#include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_crtc_helper.h>
>> +#include <drm/drm_dp_helper.h>
>> +#include <drm/drm_edid.h>
>> +#include <drm/drm_of.h>
>> +
>> +#include <linux/clk.h>
>> +#include <linux/component.h>
>> +#include <linux/extcon.h>
>> +#include <linux/firmware.h>
>> +#include <linux/regmap.h>
>> +#include <linux/reset.h>
>> +#include <linux/mfd/syscon.h>
>> +#include <linux/phy/phy.h>
>> +
>> +#include <sound/hdmi-codec.h>
>> +
>> +#include <video/of_videomode.h>
>> +#include <video/videomode.h>
> Doesn't seem like you use these.
>
>
>> +
>> +#include "cdn-dp-core.h"
>> +#include "cdn-dp-reg.h"
>> +#include "rockchip_drm_vop.h"
>> +
>> +#define connector_to_dp(c) \
>> +               container_of(c, struct cdn_dp_device, connector)
>> +
>> +#define encoder_to_dp(c) \
>> +               container_of(c, struct cdn_dp_device, encoder)
>> +
>> +/* dp grf register offset */
>> +#define DP_VOP_SEL             0x6224
>> +#define DP_SEL_VOP_LIT         BIT(12)
>> +#define DP_CLK_RATE            100000000
> If this clock rate never changes, it should probably live somewhere in
> the clock driver.


>
>> +#define WAIT_HPD_STABLE                300
> Encode the units in these two define names, ie: DP_CLK_RATE_HZ,
> WAIT_HPD_STABLE_MS
>
>> +
>> +static int cdn_dp_clk_enable(struct cdn_dp_device *dp)
>> +{
>> +       int ret;
>> +
>> +       ret = clk_prepare_enable(dp->pclk);
>> +       if (ret < 0) {
>> +               dev_err(dp->dev, "cannot enable dp pclk %d\n", ret);
>> +               goto err_pclk;
>> +       }
>> +
>> +       ret = clk_prepare_enable(dp->core_clk);
>> +       if (ret < 0) {
>> +               dev_err(dp->dev, "cannot enable core_clk %d\n", ret);
>> +               goto err_core_clk;
>> +       }
>> +
>> +       ret = clk_set_rate(dp->core_clk, DP_CLK_RATE);
>> +       if (ret < 0) {
>> +               dev_err(dp->dev, "cannot set dp core clk to %d %d\n",
>> +                       DP_CLK_RATE, ret);
>> +               goto err_set_rate;
>> +       }
>> +
>> +       /* notice fw the clk freq value */
> This might be clearer if it was rephrased to "update fw with the
> current core clk frequency". I still think the rate should be set in
> the clock driver, and perhaps you can query the rate to get the
> current value.
This function is setting the controller register, it let the uCPU know 
what freq it is, then the uCPU use this value to do something.

>
>> +       cdn_dp_set_fw_clk(dp, DP_CLK_RATE);
>> +
>> +       return 0;
>> +
>> +err_set_rate:
>> +       clk_disable_unprepare(dp->core_clk);
>> +err_core_clk:
>> +       clk_disable_unprepare(dp->pclk);
>> +err_pclk:
>> +       return ret;
>> +}
>> +
>> +static enum drm_connector_status
>> +cdn_dp_connector_detect(struct drm_connector *connector, bool force)
>> +{
>> +       struct cdn_dp_device *dp = connector_to_dp(connector);
>> +
>> +       return dp->hpd_status ? connector_status_connected :
>> +                               connector_status_disconnected;
> If you just stored hpd_status as drm_connector_status, you could avoid
> having to translate the bool.
>
>> +}
>> +
>> +static void cdn_dp_connector_destroy(struct drm_connector *connector)
>> +{
>> +       drm_connector_unregister(connector);
>> +       drm_connector_cleanup(connector);
>> +}
>> +
>> +static struct drm_connector_funcs cdn_dp_atomic_connector_funcs = {
>> +       .dpms = drm_atomic_helper_connector_dpms,
>> +       .detect = cdn_dp_connector_detect,
>> +       .destroy = cdn_dp_connector_destroy,
>> +       .fill_modes = drm_helper_probe_single_connector_modes,
>> +       .reset = drm_atomic_helper_connector_reset,
>> +       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
>> +       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
>> +};
>> +
>> +static int cdn_dp_connector_get_modes(struct drm_connector *connector)
>> +{
>> +       struct cdn_dp_device *dp = connector_to_dp(connector);
>> +       struct edid *edid;
>> +
>> +       if (!dp->fw_loaded)
> Can this actually happen? Seems like we shouldn't be returning
> connected from detect if the fw isn't loaded, so this should never be
> true. If that is the case, I think BUG_ON or WARN_ON would be more
> appropriate here.
>
>
>> +               return 0;
>> +
>> +       edid = drm_do_get_edid(connector, cdn_dp_get_edid_block, dp);
>> +       if (edid) {
>> +               dev_dbg(dp->dev, "got edid: width[%d] x height[%d]\n",
>> +                       edid->width_cm, edid->height_cm);
>> +
>> +               dp->sink_has_audio = drm_detect_monitor_audio(edid);
>> +               drm_mode_connector_update_edid_property(connector, edid);
>> +               drm_add_edid_modes(connector, edid);
>> +               /* Store the ELD */
> This comment isn't useful
>
>> +               drm_edid_to_eld(connector, edid);
>> +               kfree(edid);
>> +       } else {
>> +               dev_dbg(dp->dev, "failed to get edid\n");
> This seems to warrant a stronger level than dbg, perhaps warn. Also,
> should this really return success?
>
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static struct drm_encoder *
>> +       cdn_dp_connector_best_encoder(struct drm_connector *connector)
>> +{
>> +       struct cdn_dp_device *dp = connector_to_dp(connector);
>> +
>> +       return &dp->encoder;
>> +}
>> +
>> +static int cdn_dp_connector_mode_valid(struct drm_connector *connector,
>> +                                      struct drm_display_mode *mode)
>> +{
>> +       struct cdn_dp_device *dp = connector_to_dp(connector);
>> +       struct drm_display_info *display_info = &dp->connector.display_info;
>> +       u32 requested = mode->clock * display_info->bpc * 3 / 1000;
>> +       u32 actual, rate;
>> +
>> +       rate = drm_dp_bw_code_to_link_rate(dp->link.rate);
>> +       actual = rate * dp->link.num_lanes / 100;
>> +
>> +       /* efficiency is about 0.8 */
>> +       actual = actual * 8 / 10;
>> +
>> +       if (requested > actual) {
>> +               dev_dbg(dp->dev, "requested=%d, actual=%d, clock=%d\n",
>> +                       requested, actual, mode->clock);
>> +               return MODE_CLOCK_HIGH;
>> +       }
> This doesn't seem like something that is likely to happen given the
> trained rate should be a function of the display that's attached. Is
> that correct?
Yes, the link training should be a function of the display. But 
sometimes the edid give
us a excess size for default resolution, it need somewhere to do this 
mode valid checking.
Maybe we can do it at mode_set.

...
>
>> +       hdr = (struct cdn_firmware_header *)fw->data;
>> +       if (fw->size != le32_to_cpu(hdr->size_bytes))
>> +               return -EINVAL;
>> +
>> +       iram_data = (const u32 *)(fw->data + hdr->header_size);
>> +       dram_data = (const u32 *)(fw->data + hdr->header_size + hdr->iram_size);
>> +
>> +       ret = cdn_dp_load_firmware(dp, iram_data, hdr->iram_size,
>> +                                  dram_data, hdr->dram_size);
>> +       if (ret)
>> +               return ret;
>> +
>> +       release_firmware(fw);
> Move this out of here and into the same scope as request_firmware so
> there are no leaks.
>
>> +
>> +       ret = cdn_dp_active(dp, true);
>> +       if (ret) {
>> +               dev_err(dp->dev, "active ucpu failed: %d\n", ret);
>> +               return ret;
>> +       }
>> +
>> +       return cdn_dp_event_config(dp);
>> +}
>> +
>> +static int cdn_dp_init(struct cdn_dp_device *dp)
>> +{
>> +       struct device *dev = dp->dev;
>> +       struct device_node *np = dev->of_node;
>> +       struct platform_device *pdev = to_platform_device(dev);
>> +       struct resource *res;
>> +       int ret;
>> +
>> +       dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
>> +       if (IS_ERR(dp->grf)) {
>> +               dev_err(dev, "cdn-dp needs rockchip,grf property\n");
>> +               return PTR_ERR(dp->grf);
>> +       }
>> +
>> +       dp->irq = platform_get_irq(pdev, 0);
>> +       if (dp->irq < 0) {
>> +               dev_err(dev, "cdn-dp can not get irq\n");
>> +               return dp->irq;
>> +       }
>> +
>> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +       dp->regs = devm_ioremap_resource(dev, res);
>> +       if (IS_ERR(dp->regs)) {
>> +               dev_err(dev, "ioremap reg failed\n");
>> +               return PTR_ERR(dp->regs);
>> +       }
>> +
>> +       dp->core_clk = devm_clk_get(dev, "core-clk");
>> +       if (IS_ERR(dp->core_clk)) {
>> +               dev_err(dev, "cannot get core_clk_dp\n");
>> +               return PTR_ERR(dp->core_clk);
>> +       }
>> +
>> +       dp->pclk = devm_clk_get(dev, "pclk");
>> +       if (IS_ERR(dp->pclk)) {
>> +               dev_err(dev, "cannot get pclk\n");
>> +               return PTR_ERR(dp->pclk);
>> +       }
>> +
>> +       dp->spdif_clk = devm_clk_get(dev, "spdif");
>> +       if (IS_ERR(dp->spdif_clk)) {
>> +               dev_err(dev, "cannot get spdif_clk\n");
>> +               return PTR_ERR(dp->spdif_clk);
>> +       }
>> +
>> +       dp->spdif_rst = devm_reset_control_get(dev, "spdif");
>> +       if (IS_ERR(dp->spdif_rst)) {
>> +               dev_err(dev, "no spdif reset control found\n");
>> +               return PTR_ERR(dp->spdif_rst);
>> +       }
>> +
>> +       dp->dpms_mode = DRM_MODE_DPMS_OFF;
>> +
>> +       ret = cdn_dp_clk_enable(dp);
>> +       if (ret < 0)
>> +               return ret;
>> +
>> +       dp_clock_reset_seq(dp);
>> +
>> +       return 0;
>> +}
>> +
>> +static int cdn_dp_audio_hw_params(struct device *dev,
>> +                                 struct hdmi_codec_daifmt *daifmt,
>> +                                 struct hdmi_codec_params *params)
>> +{
>> +       struct cdn_dp_device *dp = dev_get_drvdata(dev);
>> +       struct audio_info audio = {
>> +               .sample_width = params->sample_width,
>> +               .sample_rate = params->sample_rate,
>> +               .channels = params->channels,
>> +       };
>> +       int ret;
>> +
>> +       if (!dp->encoder.crtc)
>> +               return -ENODEV;
>> +
>> +       switch (daifmt->fmt) {
>> +       case HDMI_I2S:
>> +               audio.format = AFMT_I2S;
>> +               break;
>> +       case HDMI_SPDIF:
>> +               audio.format = AFMT_SPDIF;
>> +               break;
>> +       default:
>> +               dev_err(dev, "%s: Invalid format %d\n", __func__, daifmt->fmt);
>> +               return -EINVAL;
>> +       }
>> +
>> +       ret = cdn_dp_audio_config_set(dp, &audio);
>> +       if (!ret)
>> +               dp->audio_info = audio;
>> +
>> +       return ret;
>> +}
>> +
>> +static void cdn_dp_audio_shutdown(struct device *dev)
>> +{
>> +       struct cdn_dp_device *dp = dev_get_drvdata(dev);
>> +       int ret = cdn_dp_audio_stop(dp, &dp->audio_info);
>> +
>> +       if (!ret)
>> +               dp->audio_info.format = AFMT_UNUSED;
>> +}
>> +
>> +static int cdn_dp_audio_digital_mute(struct device *dev, bool enable)
>> +{
>> +       struct cdn_dp_device *dp = dev_get_drvdata(dev);
>> +
>> +       return cdn_dp_audio_mute(dp, enable);
>> +}
>> +
>> +static int cdn_dp_audio_get_eld(struct device *dev, uint8_t *buf, size_t len)
>> +{
>> +       struct cdn_dp_device *dp = dev_get_drvdata(dev);
>> +       struct drm_mode_config *config = &dp->encoder.dev->mode_config;
>> +       struct drm_connector *connector;
>> +       int ret = -ENODEV;
>> +
>> +       mutex_lock(&config->mutex);
>> +       list_for_each_entry(connector, &config->connector_list, head) {
>> +               if (&dp->encoder == connector->encoder) {
>> +                       memcpy(buf, connector->eld,
>> +                              min(sizeof(connector->eld), len));
>> +                       ret = 0;
>> +               }
>> +       }
>> +       mutex_unlock(&config->mutex);
>> +
>> +       return ret;
>> +}
>> +
>> +static const struct hdmi_codec_ops audio_codec_ops = {
>> +       .hw_params = cdn_dp_audio_hw_params,
>> +       .audio_shutdown = cdn_dp_audio_shutdown,
>> +       .digital_mute = cdn_dp_audio_digital_mute,
>> +       .get_eld = cdn_dp_audio_get_eld,
>> +};
>> +
>> +static int cdn_dp_audio_codec_init(struct cdn_dp_device *dp,
>> +                                  struct device *dev)
>> +{
>> +       struct hdmi_codec_pdata codec_data = {
>> +               .i2s = 1,
>> +               .spdif = 1,
>> +               .ops = &audio_codec_ops,
>> +               .max_i2s_channels = 8,
>> +       };
>> +
>> +       dp->audio_pdev = platform_device_register_data(
>> +                        dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO,
>> +                        &codec_data, sizeof(codec_data));
>> +
>> +       return PTR_ERR_OR_ZERO(dp->audio_pdev);
>> +}
>> +
>> +static void cdn_dp_get_state(struct cdn_dp_device *dp, struct extcon_dev *edev)
>> +{
>> +       bool dfp, dptx;
>> +       u8 cap_lanes;
>> +
>> +       dfp = extcon_get_cable_state_(edev, EXTCON_USB_HOST);
>> +       dptx = extcon_get_cable_state_(edev, EXTCON_DISP_DP);
>> +
>> +       if (dfp && dptx)
>> +               cap_lanes = 2;
>> +       else if (dptx)
>> +               cap_lanes = 4;
>> +       else
>> +               cap_lanes = 0;
>> +
>> +       if (cap_lanes != dp->cap_lanes) {
>> +               dp->cap_lanes = cap_lanes;
>> +               schedule_delayed_work(&dp->event_wq, 0);
>> +       }
> A get function shouldn't be scheduling work. Keep the assignment here
> and move the schedule into the pd_event function.
>
>> +}
>> +
>> +static int cdn_dp_pd_event(struct notifier_block *nb,
>> +                          unsigned long event, void *priv)
>> +{
>> +       struct cdn_dp_device *dp;
>> +       struct extcon_dev *edev = priv;
>> +       u8 i;
>> +
>> +       dp = container_of(nb, struct cdn_dp_device, event_nb);
>> +
>> +       for (i = 0; i < MAX_PHY; i++) {
>> +               if (edev == dp->extcon[i]) {
>> +                       dp->port_id = i;
>> +                       break;
>> +               }
>> +       }
>> +
>> +       cdn_dp_get_state(dp, edev);
> There's a race here. If the cap_lanes change in between scheduling the
> work and the work function running (ie, plug/unplug), the work
> function will have out-of-date information. You should call this from
> the work function instead.
>
>
>> +
>> +       return 0;
>> +}
>> +
>> +static void cdn_dp_pd_event_wq(struct work_struct *work)
>> +{
>> +       struct cdn_dp_device *dp;
>> +       int ret;
>> +
>> +       dp = container_of(work, struct cdn_dp_device, event_wq.work);
>> +
>> +       ret = request_firmware(&dp->fw, "cdn/dptx.bin", dp->dev);
> This filename needs to be abstracted into a #define.
>
> You're also failing to call release_firmware in a bunch of cases
> below. It needs to be sorted out.
>
>
>> +       if (ret == -ENOENT && !dp->fw_loaded) {
> So the case where ret == -ENOENT and dp->fw_loaded is valid? Or is that a bug?
>
> I'm not convinced you need fw_loaded stored in cdn_dp_device. It seems
> like something you could either get rid of (hopefully), or convert to
> a local.
it is not bug, "ret == -ENOENT" is for file missing, maybe user space is 
not ready.

if dp->fw_loaded it means the firmware has been loaded, do not load again.


>
>> +               /*
>> +                * If can not find the file, retry to load the firmware in 1
>> +                * second, if still failed after 1 minute, give up.
>> +                */
>> +               if (dp->fw_retry++ < 60) {
>> +                       schedule_delayed_work(&dp->event_wq,
>> +                                             msecs_to_jiffies(HZ));
> I'd rather do this as an exponential back-off so if it doesn't find
> the file, it waits progressively longer.
>
> Perhaps something like:
>
> #define MAX_FW_WAIT_SECS 64
>
> [...]
>
> if (dp->fw_wait <= MAX_FW_WAIT_SECS) {
>    schedule_delayed_work(&dp_>event_wq, msecs_to_jiffies(dp->fw_wait * HZ));
>    dp->fw_wait *= 2;
> }
If so, sometimes boot with DP will later than boot and then plug DP cable.


>> +               }
>> +
>> +               dev_err(dp->dev, "failed to request firmware %d\n", ret);
> Print the wait/retries.
>
>> +               return;
>> +       }
>> +
>> +       if (!dp->cap_lanes) {
>> +               if (dp->phy_status[dp->port_id])
>> +                       phy_power_off(dp->phy[dp->port_id]);
>> +               dp->phy_status[dp->port_id] = 0;
>> +               dp->hpd_status = false;
>> +               drm_helper_hpd_irq_event(dp->drm_dev);
>> +               return;
>> +       }
>> +
>> +       if (!dp->phy_status[dp->port_id]) {
>> +               ret = phy_power_on(dp->phy[dp->port_id]);
>> +               if (ret)
> Log something in this case
>
>> +                       return;
>> +
>> +               msleep(WAIT_HPD_STABLE);
> 300ms is a long time, do we really need to wait that long?
The 300ms is a safe value, maybe this time can be reduced

>
>> +       }
>> +
>> +       dp->phy_status[dp->port_id] = 1;
> Do you really need phy_status? It seems like if things are
> well-behaved you shouldn't need it. Of course, if you're turning
> on/off multiple times, perhaps there's a bug elsewhere.
This status is needed, since the phy_power_on would increase the 
power_count, this status can avoid power on multiple times

>
>> +
>> +       ret = cdn_dp_firmware_init(dp);
>> +       if (ret)
>> +               return;
>> +
>> +       /* read hpd status failed, or the hpd does not exist. */
>> +       ret = cdn_dp_get_hpd_status(dp);
>> +       if (ret <= 0)
> Log something?
>
>> +               return;
>> +
>> +       /*
>> +        * Set the capability and start the training process once
>> +        * the hpd is detected.
>> +        */
>> +       ret = cdn_dp_set_host_cap(dp);
>> +       if (ret) {
>> +               dev_err(dp->dev, "set host capabilities failed\n");
> print ret
>
>> +               return;
>> +       }
>> +
>> +       ret = cdn_dp_training_start(dp);
>> +       if (ret) {
>> +               dev_err(dp->dev, "hw lt err:%d\n", ret);
> s/lt/link training/
>
>> +               return;
>> +       }
>> +
>> +       ret = cdn_dp_get_lt_status(dp);
> Change to cdn_dp_get_training_status
>
>> +       if (ret) {
>> +               dev_err(dp->dev, "hw lt get status failed\n");
> print ret
>
>> +               return;
>> +       }
>> +
>> +       dev_info(dp->dev, "rate:%d, lanes:%d\n",
>> +                dp->link.rate, dp->link.num_lanes);
>> +
> I think everything from cdn_dp_firmare_init to here should be moved
> out of the wq and deferred until modeset time. That way you're
> training the link at the same time as you're setting the mode, which
> should obviate the need to check the clocks in mode_valid.

cdn_dp_training_start and cdn_dp_get_lt_status can be move the mode_set,
but the others are need by get_modes, so stay here is better.

>
>> +       dp->hpd_status = true;
>> +       drm_helper_hpd_irq_event(dp->drm_dev);
>> +}
>> +
>> +static int cdn_dp_bind(struct device *dev, struct device *master,
>> +                      void *data)
>> +{
>> +       struct cdn_dp_device *dp = dev_get_drvdata(dev);
>> +       struct drm_encoder *encoder;
>> +       struct drm_connector *connector;
>> +       struct drm_device *drm_dev = data;
>> +       int ret, i;
>> +
>> +       ret = cdn_dp_init(dp);
>> +       if (ret < 0)
>> +               return ret;
>> +
>> +       dp->drm_dev = drm_dev;
>> +
>> +       encoder = &dp->encoder;
>> +
>> +       encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
>> +                                                            dev->of_node);
>> +       DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
>> +
>> +       ret = drm_encoder_init(drm_dev, encoder, &cdn_dp_encoder_funcs,
>> +                              DRM_MODE_ENCODER_TMDS, NULL);
>> +       if (ret) {
>> +               DRM_ERROR("failed to initialize encoder with drm\n");
>> +               return ret;
>> +       }
>> +
>> +       drm_encoder_helper_add(encoder, &cdn_dp_encoder_helper_funcs);
>> +
>> +       connector = &dp->connector;
>> +       connector->polled = DRM_CONNECTOR_POLL_HPD;
>> +       connector->dpms = DRM_MODE_DPMS_OFF;
>> +
>> +       ret = drm_connector_init(drm_dev, connector,
>> +                                &cdn_dp_atomic_connector_funcs,
>> +                                DRM_MODE_CONNECTOR_DisplayPort);
>> +       if (ret) {
>> +               DRM_ERROR("failed to initialize connector with drm\n");
>> +               goto err_free_encoder;
>> +       }
>> +
>> +       drm_connector_helper_add(connector, &cdn_dp_connector_helper_funcs);
>> +
>> +       ret = drm_mode_connector_attach_encoder(connector, encoder);
>> +       if (ret) {
>> +               DRM_ERROR("failed to attach connector and encoder\n");
>> +               goto err_free_connector;
>> +       }
>> +
>> +       cdn_dp_audio_codec_init(dp, dev);
>> +
>> +       dp->event_nb.notifier_call = cdn_dp_pd_event;
>> +       INIT_DELAYED_WORK(&dp->event_wq, cdn_dp_pd_event_wq);
>> +
>> +       for (i = 0; i < MAX_PHY; i++) {
>> +               if (IS_ERR(dp->extcon[i]))
>> +                       continue;
>> +
>> +               ret = extcon_register_notifier(dp->extcon[i], EXTCON_DISP_DP,
>> +                                              &dp->event_nb);
>> +               if (ret) {
>> +                       dev_err(dev, "regitster EXTCON_DISP_DP notifier err\n");
>> +                       return ret;
>> +               }
>> +
>> +               cdn_dp_get_state(dp, dp->extcon[i]);
>> +       }
>> +
>> +       return 0;
>> +
>> +err_free_connector:
>> +       drm_connector_cleanup(connector);
>> +err_free_encoder:
>> +       drm_encoder_cleanup(encoder);
>> +       return ret;
>> +}
>> +
>> +static void cdn_dp_unbind(struct device *dev, struct device *master, void *data)
>> +{
>> +       struct cdn_dp_device *dp = dev_get_drvdata(dev);
>> +       struct drm_encoder *encoder = &dp->encoder;
>> +       int i;
>> +
>> +       platform_device_unregister(dp->audio_pdev);
>> +       cdn_dp_encoder_disable(encoder);
>> +       encoder->funcs->destroy(encoder);
>> +       drm_connector_unregister(&dp->connector);
>> +       drm_connector_cleanup(&dp->connector);
> just call connector->destroy() here
>
>> +       drm_encoder_cleanup(encoder);
> this is already called by encoder->destroy()
>
>> +
>> +       for (i = 0; i < MAX_PHY; i++) {
>> +               if (!IS_ERR(dp->extcon[i]))
>> +                       extcon_unregister_notifier(dp->extcon[i], EXTCON_USB,
>> +                                                  &dp->event_nb);
>> +       }
>> +}
>> +
>> +static const struct component_ops cdn_dp_component_ops = {
>> +       .bind = cdn_dp_bind,
>> +       .unbind = cdn_dp_unbind,
>> +};
>> +
>> +static int cdn_dp_probe(struct platform_device *pdev)
>> +{
>> +       struct device *dev = &pdev->dev;
>> +       struct cdn_dp_device *dp;
>> +       u8 count = 0;
>> +       int i;
>> +
>> +       dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
>> +       if (!dp)
>> +               return -ENOMEM;
>> +       dp->dev = dev;
>> +
>> +       for (i = 0; i < MAX_PHY; i++) {
>> +               dp->extcon[i] = extcon_get_edev_by_phandle(dev, i);
>> +               dp->phy[i] = devm_of_phy_get_by_index(dev, dev->of_node, i);
>> +
>> +               if (!IS_ERR(dp->extcon[i]) && !IS_ERR(dp->phy[i]))
>> +                       count++;
>> +
>> +               if (PTR_ERR(dp->extcon[i]) == -EPROBE_DEFER ||
>> +                   PTR_ERR(dp->phy[i]) == -EPROBE_DEFER)
>> +                       return -EPROBE_DEFER;
>> +       }
>> +
>> +       if (!count) {
>> +               dev_err(dev, "missing extcon or phy\n");
>> +               return -EINVAL;
>> +       }
>> +
>> +       dev_set_drvdata(dev, dp);
>> +
>> +       return component_add(dev, &cdn_dp_component_ops);
>> +}
>> +
>> +static int cdn_dp_remove(struct platform_device *pdev)
>> +{
>> +       component_del(&pdev->dev, &cdn_dp_component_ops);
>> +
>> +       return 0;
>> +}
>> +
>> +static const struct of_device_id cdn_dp_dt_ids[] = {
>> +       { .compatible = "rockchip,rk3399-cdn-dp" },
>> +       {}
>> +};
>> +
>> +MODULE_DEVICE_TABLE(of, cdn_dp_dt_ids);
>> +
>> +static struct platform_driver cdn_dp_driver = {
>> +       .probe = cdn_dp_probe,
>> +       .remove = cdn_dp_remove,
>> +       .driver = {
>> +                  .name = "cdn-dp",
>> +                  .owner = THIS_MODULE,
>> +                  .of_match_table = of_match_ptr(cdn_dp_dt_ids),
>> +       },
>> +};
>> +
>> +module_platform_driver(cdn_dp_driver);
>> +
>> +MODULE_AUTHOR("Chris Zhong <zyw at rock-chips.com>");
>> +MODULE_DESCRIPTION("cdn DP Driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
>> new file mode 100644
>> index 0000000..31ba22d
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
>> @@ -0,0 +1,113 @@
>> +/*
>> + * Copyright (C) 2016 Chris Zhong <zyw at rock-chips.com>
>> + * Copyright (C) 2016 ROCKCHIP, Inc.
>> + *
>> + * 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.
>> + */
>> +
>> +#ifndef _ROCKCHIP_EDP_CORE_H
>> +#define _ROCKCHIP_EDP_CORE_H
> I think the include guard should match the filename
>
>> +
>> +#include <drm/drmP.h>
>> +#include <drm/drm_crtc_helper.h>
>> +#include <drm/drm_dp_helper.h>
>> +#include <drm/drm_panel.h>
>> +#include "rockchip_drm_drv.h"
>> +#include "cdn-dp-reg.h"
>> +
>> +#define MAX_PHY                2
> This should probably be stored in the .data member of of_device_id.
>
>> +
>> +enum AUDIO_FORMAT {
> enum audio_format
>
>> +       AFMT_I2S = 0,
>> +       AFMT_SPDIF = 1,
>> +       AFMT_UNUSED,
>> +};
>> +
>> +struct audio_info {
>> +       enum AUDIO_FORMAT format;
>> +       int sample_rate;
>> +       int channels;
>> +       int sample_width;
>> +};
>> +
>> +struct video_info {
>> +       bool h_sync_polarity;
>> +       bool v_sync_polarity;
>> +       bool interlaced;
>> +       int color_depth;
>> +       enum VIC_PXL_ENCODING_FORMAT color_fmt;
>> +};
>> +
>> +struct cdn_firmware_header {
>> +       u32 size_bytes; /* size of the entire header+image(s) in bytes */
>> +       u32 header_size; /* size of just the header in bytes */
>> +       u32 iram_size; /* size of iram */
>> +       u32 dram_size; /* size of dram */
>> +};
>> +
>> +struct cdn_dp_device {
>> +       struct device *dev;
>> +       struct drm_device *drm_dev;
>> +       struct drm_connector connector;
>> +       struct drm_encoder encoder;
>> +       struct drm_display_mode mode;
>> +       struct platform_device *audio_pdev;
>> +
>> +       const struct firmware *fw;      /* cdn dp firmware */
>> +       unsigned int fw_version;        /* cdn fw version */
>> +       u32 fw_retry;
>> +       bool fw_loaded;
>> +       void __iomem *regs;
>> +       struct regmap *grf;
>> +       unsigned int irq;
>> +       struct clk *core_clk;
>> +       struct clk *pclk;
>> +       struct clk *spdif_clk;
>> +       struct reset_control *spdif_rst;
>> +       struct audio_info audio_info;
>> +       struct video_info video_info;
>> +       struct extcon_dev *extcon[MAX_PHY];
>> +       struct phy *phy[MAX_PHY];
>> +       struct notifier_block event_nb;
>> +       struct delayed_work event_wq;
>> +
>> +       u8 port_id;
>> +       u8 cap_lanes;
>> +       bool hpd_status;
>> +       bool phy_status[MAX_PHY];
>> +
>> +       int dpms_mode;
>> +       struct drm_dp_link link;
>> +       bool sink_has_audio;
>> +};
>> +
>> +void dp_clock_reset_seq(struct cdn_dp_device *dp);
> cdn_ prefix here?
>
>> +
>> +void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, int clk);
>> +int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
>> +                        u32 i_size, const u32 *d_mem, u32 d_size);
>> +int cdn_dp_active(struct cdn_dp_device *dp, bool enable);
>> +int cdn_dp_set_host_cap(struct cdn_dp_device *dp);
>> +int cdn_dp_event_config(struct cdn_dp_device *dp);
>> +int cdn_dp_get_event(struct cdn_dp_device *dp);
>> +int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
>> +int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value);
>> +int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr);
>> +int cdn_dp_get_edid_block(void *dp, u8 *edid,
>> +                         unsigned int block, size_t length);
>> +int cdn_dp_training_start(struct cdn_dp_device *dp);
>> +int cdn_dp_get_lt_status(struct cdn_dp_device *dp);
>> +int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active);
>> +int cdn_dp_config_video(struct cdn_dp_device *dp);
>> +int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio);
>> +int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable);
>> +int cdn_dp_audio_config_set(struct cdn_dp_device *dp, struct audio_info *audio);
>> +
> I really don't like that you've declared these in core.h, yet defined
> them in reg.c.
>
>> +#endif  /* _ROCKCHIP_EDP_CORE_H */
> Make sure you update here too
>
>
>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
>> new file mode 100644
>> index 0000000..8d5becd
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
>> @@ -0,0 +1,740 @@
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + * Author: Chris Zhong <zyw at rock-chips.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * 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/clk.h>
>> +#include <linux/device.h>
>> +#include <linux/delay.h>
>> +#include <linux/io.h>
>> +#include <linux/iopoll.h>
>> +#include <linux/reset.h>
>> +
>> +#include "cdn-dp-core.h"
>> +#include "cdn-dp-reg.h"
>> +
>> +#define CDN_DP_SPDIF_CLK               200000000
>> +#define FW_ALIVE_TIMEOUT_US            1000000
>> +#define MAILBOX_TIMEOUT_US             5000000
>> +
>> +void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, int clk)
>> +{
>> +       writel(clk / 1000000, dp->regs + SW_CLK_H);
>> +}
>> +
>> +void dp_clock_reset_seq(struct cdn_dp_device *dp)
>> +{
>> +       writel(0xfff, dp->regs + SOURCE_DPTX_CAR);
>> +       writel(0x7, dp->regs + SOURCE_PHY_CAR);
>> +       writel(0xf, dp->regs + SOURCE_PKT_CAR);
>> +       writel(0xff, dp->regs + SOURCE_AIF_CAR);
>> +       writel(0xf, dp->regs + SOURCE_CIPHER_CAR);
>> +       writel(0x3, dp->regs + SOURCE_CRYPTO_CAR);
>> +       writel(0, dp->regs + APB_INT_MASK);
> Pull these hardcoded values out into #defines
>
>> +}
>> +
>> +static int cdn_dp_mailbox_read(struct cdn_dp_device *dp)
>> +{
>> +       int val, ret;
>> +
>> +       if (!dp->fw_loaded)
>> +               return 0;
> This seems like an error. Return -errno and log a message
The audio will call this function before firmware during probe, return 
err cause audio register failed.

>
>> +
>> +       ret = readx_poll_timeout(readl, dp->regs + MAILBOX_EMPTY_ADDR,
>> +                                val, !val, 1000, MAILBOX_TIMEOUT_US);
> Pull the 1000 out into a #define (here and below)
>
>> +       if (ret < 0) {
>> +               dev_err(dp->dev, "failed to read mailbox, keep alive = %x\n",
>> +                       readl(dp->regs + KEEP_ALIVE));
>> +               return ret;
>> +       }
>> +
>> +       return readl(dp->regs + MAILBOX0_RD_DATA) & 0xff;
>> +}
>> +
>> +static int cdp_dp_mailbox_write(struct cdn_dp_device *dp, u8 val)
>> +{
>> +       int ret, full;
>> +
>> +       if (!dp->fw_loaded)
>> +               return 0;
> Same here
>
>> +
>> +       ret = readx_poll_timeout(readl, dp->regs + MAILBOX_FULL_ADDR,
>> +                                full, !full, 1000, MAILBOX_TIMEOUT_US);
>> +       if (ret < 0) {
>> +               dev_err(dp->dev, "mailbox is full, keep alive = %x\n",
>> +                       readl(dp->regs + KEEP_ALIVE));
>> +               return ret;
>> +       }
>> +
>> +       writel(val, dp->regs + MAILBOX0_WR_DATA);
>> +
>> +       return 0;
>> +}
>> +
>> +static int cdn_dp_mailbox_response(struct cdn_dp_device *dp, u8 module_id,
> I think _receive would be a better name than _response
>
>> +                                  u8 opcode, u8 *buff, u8 buff_size)
>> +{
>> +       int ret, i = 0;
>> +       u8 header[4];
>> +
>> +       /* read the header of the message */
>> +       while (i < 4) {
> for (i = 0; i < 4; i++) {
>
>> +               ret = cdn_dp_mailbox_read(dp);
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               header[i++] = ret;
> header[i] = ret;
>
>> +       }
>> +
>> +       if (opcode != header[0] || module_id != header[1] ||
>> +           buff_size != ((header[2] << 8) | header[3])) {
> pull header[2] << 8 | header[3] out into a local, mbox_size.
>
>> +               dev_err(dp->dev, "mailbox response failed");
>> +
>> +               /*
>> +                * If the message in mailbox is not what we want, we need to
>> +                * clear the mailbox by read.
>> +                */
>> +               i = (header[2] << 8) | header[3];
>> +               while (i--)
> for (i = 0; i < mbox_size; i++)
>
>> +                       if (cdn_dp_mailbox_read(dp) < 0)
>> +                               break;
>> +
>> +               return -EINVAL;
>> +       }
>> +
>> +       i = 0;
>> +       while (i < buff_size) {
> for (i = 0; i < buff_size; i++) {
>
>> +               ret = cdn_dp_mailbox_read(dp);
>> +               if (ret < 0)
>> +                       return ret;
>> +
>> +               buff[i++] = ret;
>   buff[i] = ret;
>
>> +       }
>> +
>> +       return 0;
>
> Are you always guaranteed to have the entire message in the mailbox?
> If not, you might consider supporting partial messages and returning
> the length read.
>
>> +}
>> +
>> +static int cdn_dp_mailbox_send(struct cdn_dp_device *dp, u8 module_id,
>> +                              u8 opcode, u16 size, u8 *message)
>> +{
>> +       u8 header[4];
>> +       int ret, i;
>> +
>> +       header[0] = opcode;
>> +       header[1] = module_id;
>> +       header[2] = size >> 8;
> (size >> 8) & 0xff;
>
>> +       header[3] = size & 0xff;
>> +
>> +       for (i = 0; i < 4; i++) {
>> +               ret = cdp_dp_mailbox_write(dp, header[i]);
>> +               if (ret)
>> +                       return ret;
>> +       }
>> +
>> +       while (size--) {
> for (i = 0; i < size; i++) {
>
>> +               ret = cdp_dp_mailbox_write(dp, *message++);
> message[i]
>
>> +               if (ret)
> Log something here?
>
>> +                       return ret;
>> +       }
>> +
>> +       return 0;
> Perhaps consider returning the number of bytes that were sent.
>
>> +}
>> +
>> +static int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
>> +{
>> +       u8 msg[6];
>> +
>> +       msg[0] = (addr >> 8) & 0xff;
>> +       msg[1] = addr & 0xff;
>> +       msg[2] = (val >> 24) & 0xff;
>> +       msg[3] = (val >> 16) & 0xff;
>> +       msg[4] = (val >> 8) & 0xff;
>> +       msg[5] = val & 0xff;
>> +       return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_REGISTER,
>> +                                  ARRAY_SIZE(msg), msg);
>> +}
>> +
>> +int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
>> +                        u32 i_size, const u32 *d_mem, u32 d_size)
>> +{
>> +       int i, reg, ret;
> reg should be u32
>
>> +
>> +       /* reset ucpu before load firmware*/
>> +       writel(APB_IRAM_PATH | APB_DRAM_PATH | APB_XT_RESET,
>> +              dp->regs + APB_CTRL);
>> +
>> +       for (i = 0; i < i_size; i += 4)
>> +               writel(*i_mem++, dp->regs + ADDR_IMEM + i);
>> +
>> +       for (i = 0; i < d_size; i += 4)
>> +               writel(*d_mem++, dp->regs + ADDR_DMEM + i);
>> +
>> +       /* un-reset ucpu */
>> +       writel(0, dp->regs + APB_CTRL);
>> +
>> +       /* check the keep alive register to make sure fw working */
>> +       ret = readx_poll_timeout(readl, dp->regs + KEEP_ALIVE,
>> +                                reg, reg, 2000, FW_ALIVE_TIMEOUT_US);
>> +       if (ret < 0) {
>> +               dev_err(dp->dev, "failed to loaded the FW reg = %x\n", reg);
>> +               return -EINVAL;
>> +       }
>> +
>> +       reg = readl(dp->regs + VER_L) & 0xff;
>> +       dp->fw_version = reg;
>> +       reg = readl(dp->regs + VER_H) & 0xff;
>> +       dp->fw_version |= reg << 8;
>> +       reg = readl(dp->regs + VER_LIB_L_ADDR) & 0xff;
>> +       dp->fw_version |= reg << 16;
>> +       reg = readl(dp->regs + VER_LIB_H_ADDR) & 0xff;
>> +       dp->fw_version |= reg << 24;
>> +
> Are there any sanity checks you can do on the fw_version before returning?
This is first version of firmware, so do not nedd check the number, But 
I think adding a dev_dbg log here is better.

>
>> +       dp->fw_loaded = 1;
>> +
>> +       return 0;
>> +}
>> +
>> +int cdn_dp_reg_write_bit(struct cdn_dp_device *dp, u16 addr, u8 start_bit,
>> +                        u8 bits_no, u32 val)
>> +{
>> +       u8 field[8];
>> +
>> +       field[0] = (addr >> 8) & 0xff;
>> +       field[1] = addr & 0xff;
>> +       field[2] = start_bit;
>> +       field[3] = bits_no;
>> +       field[4] = (val >> 24) & 0xff;
>> +       field[5] = (val >> 16) & 0xff;
>> +       field[6] = (val >> 8) & 0xff;
>> +       field[7] = val & 0xff;
>> +
>> +       return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_FIELD,
>> +                           sizeof(field), field);
>> +}
>> +
>> +int cdn_dp_active(struct cdn_dp_device *dp, bool enable)
>> +{
>> +       u8 active = enable ? 1 : 0;
>> +       u8 resp;
>> +       int ret;
>> +
>> +       /* set firmware status, 1: avtive; 0: standby */
> s/avtive/active/
>
>> +       ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_GENERAL,
>> +                                 GENERAL_MAIN_CONTROL, 1, &active);
>> +       if (ret)
>> +               return ret;
>> +
>> +       ret = cdn_dp_mailbox_response(dp, MB_MODULE_ID_GENERAL,
>> +                                     GENERAL_MAIN_CONTROL, &resp, 1);
>> +       if (ret)
>> +               return ret;
>> +
>> +       return resp ? 0 : -EINVAL;
>> +}
>> +
>> +int cdn_dp_set_host_cap(struct cdn_dp_device *dp)
>> +{
>> +       u8 msg[8];
>> +       int ret;
>> +
>> +       msg[0] = DP_LINK_BW_5_4;
> Why is this always 5.4GHz? Does the hw support any other rates?
It is setting the max rate to 5.4G, not all support rate.

>
>> +       msg[1] = dp->cap_lanes;
>> +       msg[2] = VOLTAGE_LEVEL_2;
>> +       msg[3] = PRE_EMPHASIS_LEVEL_3;
>> +       msg[4] = PRBS7 | D10_2 | TRAINING_PTN1 | TRAINING_PTN2;
> You don't need to use training pattern 3 for hbr modes?
This is the fault of pattern name, it should be:

msg[4] = TPS1 | TPS2 | TPS3 | TPS4;



>
>> +       msg[5] = FAST_LT_NOT_SUPPORT;
>> +       msg[6] = LANE_MAPPING_NORMAL;
>> +       msg[7] = ENHANCED;
>> +
>> +       ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
>> +                                 DPTX_SET_HOST_CAPABILITIES,
>> +                                 ARRAY_SIZE(msg), msg);
> ARRAY_SIZE counts the number of elements, not the size, so I think
> you're better off using sizeof instead. This applies to the rest of
> the dp_mailbox_send instances as well.
>
>> +       if (ret)
>> +               return ret;
>> +
>> +       ret = cdn_dp_reg_write(dp, DP_AUX_SWAP_INVERSION_CONTROL,
>> +                              AUX_HOST_INVERT);
>> +       if (ret)
>> +               return ret;
>> +
>> +       return 0;
>> +}
>> +
>> +int cdn_dp_event_config(struct cdn_dp_device *dp)
>> +{
>> +       u8 msg[5] = {0, 0, 0, 0, 0};
> memset(msg, 0, sizeof(msg));
>
>> +
>> +       msg[0] = DPTX_EVENT_ENABLE_HPD | DPTX_EVENT_ENABLE_TRAINING;
>> +
>> +       return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_ENABLE_EVENT,
>> +                                   ARRAY_SIZE(msg), msg);
>> +}
>> +
>> +int cdn_dp_get_event(struct cdn_dp_device *dp)
> Should return u32
>
>> +{
>> +       return readl(dp->regs + SW_EVENTS0);
>> +}
>> +
>> +int cdn_dp_get_hpd_status(struct cdn_dp_device *dp)
>> +{
>> +       u8 status;
>> +       int ret;
>> +
>> +       ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_HPD_STATE,
>> +                                 0, NULL);
>> +       if (ret)
>> +               return ret;
>> +
>> +       ret = cdn_dp_mailbox_response(dp, MB_MODULE_ID_DP_TX,
>> +                                     DPTX_HPD_STATE, &status, 1);
>> +       if (ret)
>> +               return ret;
>> +
>> +       return status;
>> +}
>> +
>> +int cdn_dp_get_edid_block(void *data, u8 *edid,
>> +                         unsigned int block, size_t length)
>> +{
>> +       struct cdn_dp_device *dp = data;
>> +       u8 msg[2], reg[EDID_DATA + EDID_BLOCK_SIZE];
>> +       int ret;
>> +
>> +       if (length != EDID_BLOCK_SIZE)
>> +               return -EINVAL;
>> +
>> +       msg[0] = block / 2;
>> +       msg[1] = block % 2;
>> +
>> +       ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_GET_EDID,
>> +                                 2, msg);
> sizeof(msg), msg);
>
>> +       if (ret)
>> +               return ret;
>> +
>> +       ret = cdn_dp_mailbox_response(dp, MB_MODULE_ID_DP_TX,
>> +                                     DPTX_GET_EDID, reg,
>> +                                     EDID_DATA + EDID_BLOCK_SIZE);
>> +       if (ret)
>> +               return ret;
>> +
>> +       if (reg[EDID_LENGTH_BYTE] != EDID_BLOCK_SIZE ||
>> +           reg[EDID_SEGMENT_BUMBER] != block / 2) {
>> +               dev_err(dp->dev, "edid block size err\n");
>> +               return -EINVAL;
>> +       }
>> +
>> +       memcpy(edid, &reg[EDID_DATA], EDID_BLOCK_SIZE);
> You can avoid the intermediate reg[] buffer if you restructure the
> _response/_receive mailbox function. I'd suggest splitting it into a
> _validate_response() function that reads the header and makes sure the
> lengths match. The second part would be a _read_response() which
> simply reads the specified number of bytes from the mailbox. For
> convenience, you could make cdn_dp_mailbox_receive() a helper which
> calls both validate and read for times when you're reading the same
> number as you're validating.
>
> So in this function, you'd validate the response has EDID_DATA +
> EDID_BLOCK_SIZE bytes, then read the EDID_DATA prefix into a local,
> then read EDID_BLOCK_DATA directly into edid.
>
> I think you'll also need a _drain function to clear out the mailbox in
> case of error (otherwise the next read will fail).
>
>> +
>> +       return 0;
>> +}
>> +
>> +int cdn_dp_training_start(struct cdn_dp_device *dp)
>> +{
>> +       u8 msg, event[2];
>> +       unsigned long timeout;
>> +       int ret;
>> +
>> +       msg = LINK_TRAINING_RUN;
>> +
>> +       /* start training */
>> +       ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_TRAINING_CONTROL,
>> +                                 1, &msg);
>> +       if (ret)
>> +               return ret;
>> +
>> +       /* the whole training should finish in 500ms */
>> +       timeout = jiffies + msecs_to_jiffies(500);
> Pull 500 out into a #define
>
>> +       while (1) {
> while(time_before(jiffies, timeout))
>
>> +               msleep(20);
> Pull 20 out into a #define
>
>> +               ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
>> +                                         DPTX_READ_EVENT, 0, NULL);
>> +               if (ret)
>> +                       return ret;
>> +
>> +               ret = cdn_dp_mailbox_response(dp, MB_MODULE_ID_DP_TX,
>> +                                             DPTX_READ_EVENT, event, 2);
>> +               if (ret)
>> +                       return ret;
>> +
>> +               if (event[1] & EQ_PHASE_FINISHED)
>> +                       break;
> return 0
>
>> +
>> +               if (time_after(jiffies, timeout))
>> +                       return -ETIMEDOUT;
>> +       }
>> +
>> +       return 0;
> return -ETIMEDOUT
>
>> +}
>> +
>> +int cdn_dp_get_lt_status(struct cdn_dp_device *dp)
> s/lt/training/
>
>> +{
>> +       u8 status[10];
>> +       int ret;
>> +
>> +       ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_READ_LINK_STAT,
>> +                                 0, NULL);
>> +       if (ret)
>> +               return ret;
>> +
>> +       ret = cdn_dp_mailbox_response(dp, MB_MODULE_ID_DP_TX,
>> +                                     DPTX_READ_LINK_STAT, status, 10);
> s/10/sizeof(status)/
>
>> +       if (ret)
>> +               return ret;
>> +
>> +       dp->link.rate = status[0];
>> +       dp->link.num_lanes = status[1];
>> +
>> +       return 0;
>> +}
>> +
>> +int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active)
>> +{
>> +       u8 msg;
>> +
>> +       msg = !!active;
>> +
>> +       return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_SET_VIDEO,
>> +                                  1, &msg);
> s/1/sizeof(msg)/
>
>> +}
>> +
>> +static int cdn_dp_get_msa_misc(struct video_info *video,
>> +                              struct drm_display_mode *mode)
>> +{
>> +       u8 val0, val1;
> u8 val[2];
>
>> +       u32 msa_misc;
>> +
>> +       switch (video->color_fmt) {
>> +       case PXL_RGB:
>> +       case Y_ONLY:
>> +               val0 = 0;
>> +               break;
> Perhaps this would be better as listed under the default case.
>
>> +       case YCBCR_4_4_4:
>> +               /* set default color space conversion to BT601 */
>> +               val0 = 6 + BT_601 * 8;
>> +               break;
>> +       case YCBCR_4_2_2:
>> +               /* set default color space conversion to BT601 */
>> +               val0 = 5 + BT_601 * 8;
>> +               break;
>> +       case YCBCR_4_2_0:
>> +               val0 = 5;
>> +               break;
>> +       };
>> +
>> +       switch (video->color_depth) {
>> +       case 6:
>> +               val1 = 0;
>> +               break;
>> +       case 8:
>> +               val1 = 1;
>> +               break;
>> +       case 10:
>> +               val1 = 2;
>> +               break;
>> +       case 12:
>> +               val1 = 3;
>> +               break;
>> +       case 16:
>> +               val1 = 4;
>> +               break;
>> +       };
>> +
>> +       msa_misc = 2 * val0 + 32 * val1 +
>> +                  ((video->color_fmt == Y_ONLY) ? (1 << 14) : 0);
> Perhaps a stupid question, but is this documented anywhere? If not,
> could you add a comment describing what you're doing?
>
>> +
>> +       return msa_misc;
>> +}
>> +
>> +int cdn_dp_config_video(struct cdn_dp_device *dp)
>> +{
>> +       struct video_info *video = &dp->video_info;
>> +       struct drm_display_mode *mode = &dp->mode;
>> +       u64 symbol;
>> +       u32 val, link_rate;
>> +       u8 bit_per_pix;
>> +       u8 tu_size_reg = TU_SIZE;
>> +       int ret;
>> +
>> +       bit_per_pix = (video->color_fmt == YCBCR_4_2_2) ?
>> +                     (video->color_depth * 2) : (video->color_depth * 3);
>> +
>> +       link_rate = drm_dp_bw_code_to_link_rate(dp->link.rate);
>> +
>> +       val = VIF_BYPASS_INTERLACE;
>> +
> remove extra line
>
>> +       ret = cdn_dp_reg_write(dp, BND_HSYNC2VSYNC, val);
>> +       if (ret)
>> +               return ret;
>> +
>> +       ret = cdn_dp_reg_write(dp, HSYNC2VSYNC_POL_CTRL, 0);
>> +       if (ret)
>> +               return ret;
>> +
>> +       /* get a best tu_size and symbol */
>> +       do {
>> +               tu_size_reg += 2;
>> +               symbol = tu_size_reg * mode->clock * bit_per_pix;
>> +               symbol /= dp->link.num_lanes * link_rate * 8;
>> +       } while ((symbol == 1) || (tu_size_reg - symbol < 4));
>> +
>> +       val = symbol + (tu_size_reg << 8);
>> +
> remove extra line
>
>> +       ret = cdn_dp_reg_write(dp, DP_FRAMER_TU, val);
>> +       if (ret)
>> +               return ret;
>> +
>> +       switch (video->color_depth) {
>> +       case 6:
>> +               val = BCS_6;
>> +               break;
>> +       case 8:
>> +               val = BCS_8;
>> +               break;
>> +       case 10:
>> +               val = BCS_10;
>> +               break;
>> +       case 12:
>> +               val = BCS_12;
>> +               break;
>> +       case 16:
>> +               val = BCS_16;
>> +               break;
>> +       };
>> +
>> +       val += video->color_fmt << 8;
>> +       ret = cdn_dp_reg_write(dp, DP_FRAMER_PXL_REPR, val);
>> +       if (ret)
>> +               return ret;
>> +
>> +       val = video->h_sync_polarity ? DP_FRAMER_SP_HSP : 0;
>> +       val |= video->v_sync_polarity ? DP_FRAMER_SP_VSP : 0;
>> +       ret = cdn_dp_reg_write(dp, DP_FRAMER_SP, val);
>> +       if (ret)
>> +               return ret;
>> +
>> +       val = (mode->hsync_start - mode->hdisplay) << 16;
>> +       val |= mode->htotal - mode->hsync_end;
>> +       ret = cdn_dp_reg_write(dp, DP_FRONT_BACK_PORCH, val);
>> +       if (ret)
>> +               return ret;
>> +
>> +       val = mode->hdisplay * bit_per_pix / 8;
>> +       ret = cdn_dp_reg_write(dp, DP_BYTE_COUNT, val);
>> +       if (ret)
>> +               return ret;
>> +
>> +       val = mode->htotal | ((mode->htotal - mode->hsync_start) << 16);
>> +       ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_0, val);
>> +       if (ret)
>> +               return ret;
>> +
>> +       val = mode->hsync_end - mode->hsync_start;
>> +       val |= (mode->hdisplay << 16) | (video->h_sync_polarity << 15);
>> +       ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_1, val);
>> +       if (ret)
>> +               return ret;
>> +
>> +       val = mode->vtotal;
>> +       val |= ((mode->vtotal - mode->vsync_start) << 16);
>> +
> remove extra line
>
>> +       ret = cdn_dp_reg_write(dp, MSA_VERTICAL_0, val);
>> +       if (ret)
>> +               return ret;
>> +
>> +       val = mode->vsync_end - mode->vsync_start;
>> +       val |= mode->vdisplay << 16;
>> +       val |= (video->v_sync_polarity << 15);
> Make this consistent with how you assigned val for MSA_HORIZONTAL_1
> (ie: split both or combine both)
>
>> +       ret = cdn_dp_reg_write(dp, MSA_VERTICAL_1, val);
>> +       if (ret)
>> +               return ret;
>> +
>> +       val = cdn_dp_get_msa_misc(video, mode);
>> +       ret = cdn_dp_reg_write(dp, MSA_MISC, val);
>> +       if (ret)
>> +               return ret;
>> +
>> +       ret = cdn_dp_reg_write(dp, STREAM_CONFIG, 1);
>> +       if (ret)
>> +               return ret;
>> +
>> +       val = mode->hsync_end - mode->hsync_start;
>> +       val |= (mode->hdisplay << 16);
>> +       ret = cdn_dp_reg_write(dp, DP_HORIZONTAL, val);
>> +       if (ret)
>> +               return ret;
>> +
>> +       val = mode->vtotal;
>> +       val -= (mode->vtotal - mode->vdisplay);
>> +       val |= (mode->vtotal - mode->vsync_start) << 16;
>> +
> extra line
>
>> +       ret = cdn_dp_reg_write(dp, DP_VERTICAL_0, val);
>> +       if (ret)
>> +               return ret;
>> +
>> +       val = mode->vtotal;
>> +       ret = cdn_dp_reg_write(dp, DP_VERTICAL_1, val);
>> +       if (ret)
>> +               return ret;
>> +
>> +       val =  0;
>> +       return cdn_dp_reg_write_bit(dp, DP_VB_ID, 2, 1, val);
>> +}
>> +
>> +int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio)
>> +{
>> +       int ret = cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, AUDIO_PACK_EN(0));
>> +
> remove this empty line
>
>> +       if (ret)
>> +               return ret;
> insert empy line
>
>> +       writel(0x1F0707, dp->regs + SPDIF_CTRL_ADDR);
> pull this magic value into a #define
>
>> +       writel(0, dp->regs + AUDIO_SRC_CNTL);
>> +       writel(0, dp->regs + AUDIO_SRC_CNFG);
>> +       writel(1, dp->regs + AUDIO_SRC_CNTL);
>> +       writel(0, dp->regs + AUDIO_SRC_CNTL);
>> +
>> +       writel(0, dp->regs + SMPL2PKT_CNTL);
>> +       writel(1, dp->regs + SMPL2PKT_CNTL);
>> +       writel(0, dp->regs + SMPL2PKT_CNTL);
>> +
>> +       writel(1, dp->regs + FIFO_CNTL);
>> +       writel(0, dp->regs + FIFO_CNTL);
> Can you document why you need to toggle these bits as opposed to just
> setting them to 0?
>
>> +
>> +       if (audio->format == AFMT_SPDIF)
>> +               clk_disable_unprepare(dp->spdif_clk);
>> +
>> +       return 0;
>> +}
>> +
>> +int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable)
>> +{
>> +       return cdn_dp_reg_write_bit(dp, DP_VB_ID, 4, 1, enable);
>> +}
>> +
>> +int cdn_dp_audio_config_set(struct cdn_dp_device *dp, struct audio_info *audio)
>> +{
>> +       int lanes_param, i2s_port_en_val, val, i;
> int lanes_param = 3, i2s_port_en_val = 0xf, i;
>
>> +       int ret;
>> +
>> +       if (audio->channels == 2 && dp->link.num_lanes == 1)
>> +               lanes_param = 1;
>> +       else if (audio->channels == 2)
>> +               lanes_param = 3;
>> +       else
>> +               lanes_param = 0;
>> +
>> +       if (audio->channels == 2)
>> +               i2s_port_en_val = 1;
>> +       else if (audio->channels == 4)
>> +               i2s_port_en_val = 3;
>> +       else
>> +               i2s_port_en_val = 0xf;
>
> I think with the above initialization values, you can replace these 2
> conditionals with:
>
> if (audio->channels == 2) {
>    if (dp->link.num_lanes == 1)
>      lanes_param = 3;
>    i2s_port_en_val = 1;
> } else if (audio->channels == 4) {
>    i2s_port_en_val = 3;
> }
>
>
>> +
>> +       if (audio->format == AFMT_SPDIF) {
>> +               reset_control_assert(dp->spdif_rst);
>> +               reset_control_deassert(dp->spdif_rst);
>> +       }
>> +
>> +       ret = cdn_dp_reg_write(dp, CM_LANE_CTRL, 0x8000);
> pull value out into #define (and use BIT(15) if appropriate)
>
>> +       if (ret)
>> +               return ret;
>> +
>> +       ret = cdn_dp_reg_write(dp, CM_CTRL, 0);
>> +       if (ret)
>> +               return ret;
>> +
>> +       if (audio->format == AFMT_I2S) {
>> +               writel(0x0, dp->regs + SPDIF_CTRL_ADDR);
>> +
>> +               writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL);
>> +
>> +               val = audio->channels - 1;
>> +               val |= (audio->channels / 2 - 1) << 5;
>> +               val |= BIT(8);
>> +               val |= lanes_param << 11;
>> +               writel(val, dp->regs + SMPL2PKT_CNFG);
>> +
>> +               if (audio->sample_width == 16)
>> +                       val = 0;
>> +               else if (audio->sample_width == 24)
>> +                       val = 1 << 9;
>> +               else
>> +                       val = 2 << 9;
>> +
>> +               val |= (audio->channels - 1) << 2;
>> +               val |= i2s_port_en_val << 17;
>> +               val |= 2 << 11;
>> +               writel(val, dp->regs + AUDIO_SRC_CNFG);
>> +
>> +               for (i = 0; i < (audio->channels + 1) / 2; i++) {
>> +                       if (audio->sample_width == 16)
>> +                               val = (0x08 << 8) | (0x08 << 20);
>> +                       else if (audio->sample_width == 24)
>> +                               val = (0x0b << 8) | (0x0b << 20);
>> +
>> +                       val |= ((2 * i) << 4) | ((2 * i + 1) << 16);
>> +                       writel(val, dp->regs + STTS_BIT_CH(i));
>> +               }
>> +
>> +               switch (audio->sample_rate) {
>> +               case 32000:
>> +                       val = SAMPLING_FREQ(3) |
>> +                             ORIGINAL_SAMP_FREQ(0xc);
>> +                       break;
>> +               case 44100:
>> +                       val = SAMPLING_FREQ(0) |
>> +                             ORIGINAL_SAMP_FREQ(0xf);
>> +                       break;
>> +               case 48000:
>> +                       val = SAMPLING_FREQ(2) |
>> +                             ORIGINAL_SAMP_FREQ(0xd);
>> +                       break;
>> +               case 88200:
>> +                       val = SAMPLING_FREQ(8) |
>> +                             ORIGINAL_SAMP_FREQ(0x7);
>> +                       break;
>> +               case 96000:
>> +                       val = SAMPLING_FREQ(0xa) |
>> +                             ORIGINAL_SAMP_FREQ(5);
>> +                       break;
>> +               case 176400:
>> +                       val = SAMPLING_FREQ(0xc) |
>> +                             ORIGINAL_SAMP_FREQ(3);
>> +                       break;
>> +               case 192000:
>> +                       val = SAMPLING_FREQ(0xe) |
>> +                             ORIGINAL_SAMP_FREQ(1);
>> +                       break;
>> +               }
>> +               val |= 4;
>> +               writel(val, dp->regs + COM_CH_STTS_BITS);
>> +
>> +               writel(2, dp->regs + SMPL2PKT_CNTL);
>> +               writel(2, dp->regs + AUDIO_SRC_CNTL);
> Pull all of this code into a new cdn_dp_audio_config_i2s() function
> (with the lane_param and i2s_port_en_val assignments) and call it from
> here
>
>> +       } else {
>> +               val = 0x1F0707;
> Magic values need to be in #defines
>
>> +               writel(val, dp->regs + SPDIF_CTRL_ADDR);
>> +
>> +               writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL);
>> +
>> +               val = 0x101 | (3 << 11);
> Same here, these mean nothing.
>
>> +               writel(val, dp->regs + SMPL2PKT_CNFG);
>> +               writel(2, dp->regs + SMPL2PKT_CNTL);
>> +
>> +               val = 0x3F0707;
> And here. And anywhere else I missed.
>
>> +               writel(val, dp->regs + SPDIF_CTRL_ADDR);
>> +
>> +               clk_prepare_enable(dp->spdif_clk);
>> +               clk_set_rate(dp->spdif_clk, CDN_DP_SPDIF_CLK);
>> +       }
>> +
>> +       return cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, AUDIO_PACK_EN(1));
>> +}
>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
>> new file mode 100644
>> index 0000000..b33793e
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
>> @@ -0,0 +1,409 @@
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + * Author: Chris Zhong <zyw at rock-chips.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * 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.
>> + */
>> +
>> +#ifndef _CDN_DP_REG_H
>> +#define _CDN_DP_REG_H
>> +
>> +#include <linux/bitops.h>
>> +
>> +#define ADDR_IMEM              0x10000
>> +#define ADDR_DMEM              0x20000
>> +
>> +/* APB CFG addr */
>> +#define APB_CTRL                       0
>> +#define XT_INT_CTRL                    0x04
>> +#define MAILBOX_FULL_ADDR              0x08
>> +#define MAILBOX_EMPTY_ADDR             0x0c
>> +#define MAILBOX0_WR_DATA               0x10
>> +#define MAILBOX0_RD_DATA               0x14
>> +#define KEEP_ALIVE                     0x18
>> +#define VER_L                          0x1c
>> +#define VER_H                          0x20
>> +#define VER_LIB_L_ADDR                 0x24
>> +#define VER_LIB_H_ADDR                 0x28
>> +#define SW_DEBUG_L                     0x2c
>> +#define SW_DEBUG_H                     0x30
>> +#define MAILBOX_INT_MASK               0x34
>> +#define MAILBOX_INT_STATUS             0x38
>> +#define SW_CLK_L                       0x3c
>> +#define SW_CLK_H                       0x40
>> +#define SW_EVENTS0                     0x44
>> +#define SW_EVENTS1                     0x48
>> +#define SW_EVENTS2                     0x4c
>> +#define SW_EVENTS3                     0x50
>> +#define XT_OCD_CTRL                    0x60
>> +#define APB_INT_MASK                   0x6c
>> +#define APB_STATUS_MASK                        0x70
>> +
>> +/* audio decoder addr */
>> +#define AUDIO_SRC_CNTL                 0x30000
>> +#define AUDIO_SRC_CNFG                 0x30004
>> +#define COM_CH_STTS_BITS               0x30008
> Can you split these up into individual bits?
>
>> +#define STTS_BIT_CH(x)                 (0x3000c + ((x) << 2))
>> +#define SPDIF_CTRL_ADDR                        0x3004c
>> +#define SPDIF_CH1_CS_3100_ADDR         0x30050
>> +#define SPDIF_CH1_CS_6332_ADDR         0x30054
>> +#define SPDIF_CH1_CS_9564_ADDR         0x30058
>> +#define SPDIF_CH1_CS_12796_ADDR                0x3005c
>> +#define SPDIF_CH1_CS_159128_ADDR       0x30060
>> +#define SPDIF_CH1_CS_191160_ADDR       0x30064
>> +#define SPDIF_CH2_CS_3100_ADDR         0x30068
>> +#define SPDIF_CH2_CS_6332_ADDR         0x3006c
>> +#define SPDIF_CH2_CS_9564_ADDR         0x30070
>> +#define SPDIF_CH2_CS_12796_ADDR                0x30074
>> +#define SPDIF_CH2_CS_159128_ADDR       0x30078
>> +#define SPDIF_CH2_CS_191160_ADDR       0x3007c
>> +#define SMPL2PKT_CNTL                  0x30080
>> +#define SMPL2PKT_CNFG                  0x30084
>> +#define FIFO_CNTL                      0x30088
>> +#define FIFO_STTS                      0x3008c
> These cntl and cnfig values should be split up so we know what the
> individual bits do
>
>> +
>> +/* source pif addr */
>> +#define SOURCE_PIF_WR_ADDR             0x30800
>> +#define SOURCE_PIF_WR_REQ              0x30804
>> +#define SOURCE_PIF_RD_ADDR             0x30808
>> +#define SOURCE_PIF_RD_REQ              0x3080c
>> +#define SOURCE_PIF_DATA_WR             0x30810
>> +#define SOURCE_PIF_DATA_RD             0x30814
>> +#define SOURCE_PIF_FIFO1_FLUSH         0x30818
>> +#define SOURCE_PIF_FIFO2_FLUSH         0x3081c
>> +#define SOURCE_PIF_STATUS              0x30820
>> +#define SOURCE_PIF_INTERRUPT_SOURCE    0x30824
>> +#define SOURCE_PIF_INTERRUPT_MASK      0x30828
> Same here, split these up! Everywhere else applicable too.
>
>> +#define SOURCE_PIF_PKT_ALLOC_REG       0x3082c
>> +#define SOURCE_PIF_PKT_ALLOC_WR_EN     0x30830
>> +#define SOURCE_PIF_SW_RESET            0x30834
>> +
>> +/* bellow registers need access by mailbox */
>> +/* source car addr */
>> +#define SOURCE_HDTX_CAR                        0x0900
>> +#define SOURCE_DPTX_CAR                        0x0904
>> +#define SOURCE_PHY_CAR                 0x0908
>> +#define SOURCE_CEC_CAR                 0x090c
>> +#define SOURCE_CBUS_CAR                        0x0910
>> +#define SOURCE_PKT_CAR                 0x0918
>> +#define SOURCE_AIF_CAR                 0x091c
>> +#define SOURCE_CIPHER_CAR              0x0920
>> +#define SOURCE_CRYPTO_CAR              0x0924
>> +
>> +/* clock meters addr */
>> +#define CM_CTRL                                0x0a00
>> +#define CM_I2S_CTRL                    0x0a04
>> +#define CM_SPDIF_CTRL                  0x0a08
>> +#define CM_VID_CTRL                    0x0a0c
>> +#define CM_LANE_CTRL                   0x0a10
>> +#define I2S_NM_STABLE                  0x0a14
>> +#define I2S_NCTS_STABLE                        0x0a18
>> +#define SPDIF_NM_STABLE                        0x0a1c
>> +#define SPDIF_NCTS_STABLE              0x0a20
>> +#define NMVID_MEAS_STABLE              0x0a24
>> +#define I2S_MEAS                       0x0a40
>> +#define SPDIF_MEAS                     0x0a80
>> +#define NMVID_MEAS                     0x0ac0
>> +
>> +/* source vif addr */
>> +#define BND_HSYNC2VSYNC                        0x0b00
>> +#define HSYNC2VSYNC_F1_L1              0x0b04
>> +#define HSYNC2VSYNC_F2_L1              0x0b08
>> +#define HSYNC2VSYNC_STATUS             0x0b0c
>> +#define HSYNC2VSYNC_POL_CTRL           0x0b10
>> +
>> +/* dptx phy addr */
>> +#define DP_TX_PHY_CONFIG_REG           0x2000
>> +#define DP_TX_PHY_STATUS_REG           0x2004
>> +#define DP_TX_PHY_SW_RESET             0x2008
>> +#define DP_TX_PHY_SCRAMBLER_SEED       0x200c
>> +#define DP_TX_PHY_TRAINING_01_04       0x2010
>> +#define DP_TX_PHY_TRAINING_05_08       0x2014
>> +#define DP_TX_PHY_TRAINING_09_10       0x2018
>> +#define TEST_COR                       0x23fc
>> +
>> +/* dptx hpd addr */
>> +#define HPD_IRQ_DET_MIN_TIMER          0x2100
>> +#define HPD_IRQ_DET_MAX_TIMER          0x2104
>> +#define HPD_UNPLGED_DET_MIN_TIMER      0x2108
>> +#define HPD_STABLE_TIMER               0x210c
>> +#define HPD_FILTER_TIMER               0x2110
>> +#define HPD_EVENT_MASK                 0x211c
>> +#define HPD_EVENT_DET                  0x2120
>> +
>> +/* dpyx framer addr */
>> +#define DP_FRAMER_GLOBAL_CONFIG                0x2200
>> +#define DP_SW_RESET                    0x2204
>> +#define DP_FRAMER_TU                   0x2208
>> +#define DP_FRAMER_PXL_REPR             0x220c
>> +#define DP_FRAMER_SP                   0x2210
>> +#define AUDIO_PACK_CONTROL             0x2214
>> +#define DP_VC_TABLE(x)                 (0x2218 + ((x) << 2))
>> +#define DP_VB_ID                       0x2258
>> +#define DP_MTPH_LVP_CONTROL            0x225c
>> +#define DP_MTPH_SYMBOL_VALUES          0x2260
>> +#define DP_MTPH_ECF_CONTROL            0x2264
>> +#define DP_MTPH_ACT_CONTROL            0x2268
>> +#define DP_MTPH_STATUS                 0x226c
>> +#define DP_INTERRUPT_SOURCE            0x2270
>> +#define DP_INTERRUPT_MASK              0x2274
>> +#define DP_FRONT_BACK_PORCH            0x2278
>> +#define DP_BYTE_COUNT                  0x227c
>> +
>> +/* dptx stream addr */
>> +#define MSA_HORIZONTAL_0               0x2280
>> +#define MSA_HORIZONTAL_1               0x2284
>> +#define MSA_VERTICAL_0                 0x2288
>> +#define MSA_VERTICAL_1                 0x228c
>> +#define MSA_MISC                       0x2290
>> +#define STREAM_CONFIG                  0x2294
>> +#define AUDIO_PACK_STATUS              0x2298
>> +#define VIF_STATUS                     0x229c
>> +#define PCK_STUFF_STATUS_0             0x22a0
>> +#define PCK_STUFF_STATUS_1             0x22a4
>> +#define INFO_PACK_STATUS               0x22a8
>> +#define RATE_GOVERNOR_STATUS           0x22ac
>> +#define DP_HORIZONTAL                  0x22b0
>> +#define DP_VERTICAL_0                  0x22b4
>> +#define DP_VERTICAL_1                  0x22b8
>> +#define DP_BLOCK_SDP                   0x22bc
>> +
>> +/* dptx glbl addr */
>> +#define DPTX_LANE_EN                   0x2300
>> +#define DPTX_ENHNCD                    0x2304
>> +#define DPTX_INT_MASK                  0x2308
>> +#define DPTX_INT_STATUS                        0x230c
>> +
>> +/* dp aux addr */
>> +#define DP_AUX_HOST_CONTROL            0x2800
>> +#define DP_AUX_INTERRUPT_SOURCE                0x2804
>> +#define DP_AUX_INTERRUPT_MASK          0x2808
>> +#define DP_AUX_SWAP_INVERSION_CONTROL  0x280c
>> +#define DP_AUX_SEND_NACK_TRANSACTION   0x2810
>> +#define DP_AUX_CLEAR_RX                        0x2814
>> +#define DP_AUX_CLEAR_TX                        0x2818
>> +#define DP_AUX_TIMER_STOP              0x281c
>> +#define DP_AUX_TIMER_CLEAR             0x2820
>> +#define DP_AUX_RESET_SW                        0x2824
>> +#define DP_AUX_DIVIDE_2M               0x2828
>> +#define DP_AUX_TX_PREACHARGE_LENGTH    0x282c
>> +#define DP_AUX_FREQUENCY_1M_MAX                0x2830
>> +#define DP_AUX_FREQUENCY_1M_MIN                0x2834
>> +#define DP_AUX_RX_PRE_MIN              0x2838
>> +#define DP_AUX_RX_PRE_MAX              0x283c
>> +#define DP_AUX_TIMER_PRESET            0x2840
>> +#define DP_AUX_NACK_FORMAT             0x2844
>> +#define DP_AUX_TX_DATA                 0x2848
>> +#define DP_AUX_RX_DATA                 0x284c
>> +#define DP_AUX_TX_STATUS               0x2850
>> +#define DP_AUX_RX_STATUS               0x2854
>> +#define DP_AUX_RX_CYCLE_COUNTER                0x2858
>> +#define DP_AUX_MAIN_STATES             0x285c
>> +#define DP_AUX_MAIN_TIMER              0x2860
>> +#define DP_AUX_AFE_OUT                 0x2864
>> +
>> +/* crypto addr */
>> +#define CRYPTO_HDCP_REVISION           0x5800
>> +#define HDCP_CRYPTO_CONFIG             0x5804
>> +#define CRYPTO_INTERRUPT_SOURCE                0x5808
>> +#define CRYPTO_INTERRUPT_MASK          0x580c
>> +#define CRYPTO22_CONFIG                        0x5818
>> +#define CRYPTO22_STATUS                        0x581c
>> +#define SHA_256_DATA_IN                        0x583c
>> +#define SHA_256_DATA_OUT_(x)           (0x5850 + ((x) << 2))
>> +#define AES_32_KEY_(x)                 (0x5870 + ((x) << 2))
>> +#define AES_32_DATA_IN                 0x5880
>> +#define AES_32_DATA_OUT_(x)            (0x5884 + ((x) << 2))
>> +#define CRYPTO14_CONFIG                        0x58a0
>> +#define CRYPTO14_STATUS                        0x58a4
>> +#define CRYPTO14_PRNM_OUT              0x58a8
>> +#define CRYPTO14_KM_0                  0x58ac
>> +#define CRYPTO14_KM_1                  0x58b0
>> +#define CRYPTO14_AN_0                  0x58b4
>> +#define CRYPTO14_AN_1                  0x58b8
>> +#define CRYPTO14_YOUR_KSV_0            0x58bc
>> +#define CRYPTO14_YOUR_KSV_1            0x58c0
>> +#define CRYPTO14_MI_0                  0x58c4
>> +#define CRYPTO14_MI_1                  0x58c8
>> +#define CRYPTO14_TI_0                  0x58cc
>> +#define CRYPTO14_KI_0                  0x58d0
>> +#define CRYPTO14_KI_1                  0x58d4
>> +#define CRYPTO14_BLOCKS_NUM            0x58d8
>> +#define CRYPTO14_KEY_MEM_DATA_0                0x58dc
>> +#define CRYPTO14_KEY_MEM_DATA_1                0x58e0
>> +#define CRYPTO14_SHA1_MSG_DATA         0x58e4
>> +#define CRYPTO14_SHA1_V_VALUE_(x)      (0x58e8 + ((x) << 2))
>> +#define TRNG_CTRL                      0x58fc
>> +#define TRNG_DATA_RDY                  0x5900
>> +#define TRNG_DATA                      0x5904
>> +
>> +/* cipher addr */
>> +#define HDCP_REVISION                  0x60000
>> +#define INTERRUPT_SOURCE               0x60004
>> +#define INTERRUPT_MASK                 0x60008
>> +#define HDCP_CIPHER_CONFIG             0x6000c
>> +#define AES_128_KEY_0                  0x60010
>> +#define AES_128_KEY_1                  0x60014
>> +#define AES_128_KEY_2                  0x60018
>> +#define AES_128_KEY_3                  0x6001c
>> +#define AES_128_RANDOM_0               0x60020
>> +#define AES_128_RANDOM_1               0x60024
>> +#define CIPHER14_KM_0                  0x60028
>> +#define CIPHER14_KM_1                  0x6002c
>> +#define CIPHER14_STATUS                        0x60030
>> +#define CIPHER14_RI_PJ_STATUS          0x60034
>> +#define CIPHER_MODE                    0x60038
>> +#define CIPHER14_AN_0                  0x6003c
>> +#define CIPHER14_AN_1                  0x60040
>> +#define CIPHER22_AUTH                  0x60044
>> +#define CIPHER14_R0_DP_STATUS          0x60048
>> +#define CIPHER14_BOOTSTRAP             0x6004c
>> +
>> +#define APB_IRAM_PATH                  BIT(2)
>> +#define APB_DRAM_PATH                  BIT(1)
>> +#define APB_XT_RESET                   BIT(0)
>> +
>> +/* mailbox */
>> +#define MB_OPCODE_ID                   0
>> +#define MB_MODULE_ID                   1
>> +#define MB_SIZE_MSB_ID                 2
>> +#define MB_SIZE_LSB_ID                 3
>> +#define MB_DATA_ID                     4
>> +
>> +#define MB_MODULE_ID_DP_TX             0x01
>> +#define MB_MODULE_ID_HDCP_TX           0x07
>> +#define MB_MODULE_ID_HDCP_RX           0x08
>> +#define MB_MODULE_ID_HDCP_GENERAL      0x09
>> +#define MB_MODULE_ID_GENERAL           0x0a
>> +
>> +/* general opcode */
>> +#define GENERAL_MAIN_CONTROL            0x01
>> +#define GENERAL_TEST_ECHO               0x02
>> +#define GENERAL_BUS_SETTINGS            0x03
>> +#define GENERAL_TEST_ACCESS             0x04
>> +
>> +#define DPTX_SET_POWER_MNG                     0x00
>> +#define DPTX_SET_HOST_CAPABILITIES             0x01
>> +#define DPTX_GET_EDID                          0x02
>> +#define DPTX_READ_DPCD                         0x03
>> +#define DPTX_WRITE_DPCD                                0x04
>> +#define DPTX_ENABLE_EVENT                      0x05
>> +#define DPTX_WRITE_REGISTER                    0x06
>> +#define DPTX_READ_REGISTER                     0x07
>> +#define DPTX_WRITE_FIELD                       0x08
>> +#define DPTX_TRAINING_CONTROL                  0x09
>> +#define DPTX_READ_EVENT                                0x0a
>> +#define DPTX_READ_LINK_STAT                    0x0b
>> +#define DPTX_SET_VIDEO                         0x0c
>> +#define DPTX_SET_AUDIO                         0x0d
>> +#define DPTX_GET_LAST_AUX_STAUS                        0x0e
>> +#define DPTX_SET_LINK_BREAK_POINT              0x0f
>> +#define DPTX_FORCE_LANES                       0x10
>> +#define DPTX_HPD_STATE                         0x11
>> +
>> +#define DPTX_EVENT_ENABLE_HPD                  BIT(0)
>> +#define DPTX_EVENT_ENABLE_TRAINING             BIT(1)
>> +
>> +#define LINK_TRAINING_NOT_ACTIVE               0
>> +#define LINK_TRAINING_RUN                      1
>> +#define LINK_TRAINING_RESTART                  2
>> +
>> +#define CONTROL_VIDEO_IDLE                     0
>> +#define CONTROL_VIDEO_VALID                    1
>> +
>> +#define VIF_BYPASS_INTERLACE                   BIT(13)
>> +#define INTERLACE_FMT_DET                      BIT(12)
>> +#define INTERLACE_DTCT_WIN                     0x20
>> +
>> +#define DP_FRAMER_SP_INTERLACE_EN              BIT(2)
>> +#define DP_FRAMER_SP_HSP                       BIT(1)
>> +#define DP_FRAMER_SP_VSP                       BIT(0)
>> +
>> +/* capability */
>> +#define AUX_HOST_INVERT                                3
>> +#define        FAST_LT_SUPPORT                         1
>> +#define FAST_LT_NOT_SUPPORT                    0
>> +#define LANE_MAPPING_NORMAL                    0xe4
>> +#define LANE_MAPPING_FLIPPED                   0x1b
>> +#define ENHANCED                               1
>> +
>> +#define        FULL_LT_STARTED                         BIT(0)
>> +#define FASE_LT_STARTED                                BIT(1)
>> +#define CLK_RECOVERY_FINISHED                  BIT(2)
>> +#define EQ_PHASE_FINISHED                      BIT(3)
>> +#define FASE_LT_START_FINISHED                 BIT(4)
>> +#define CLK_RECOVERY_FAILED                    BIT(5)
>> +#define EQ_PHASE_FAILED                                BIT(6)
>> +#define FASE_LT_FAILED                         BIT(7)
>> +
>> +#define DPTX_HPD_EVENT                         BIT(0)
>> +#define DPTX_TRAINING_EVENT                    BIT(1)
>> +#define HDCP_TX_STATUS_EVENT                   BIT(4)
>> +#define HDCP2_TX_IS_KM_STORED_EVENT            BIT(5)
>> +#define HDCP2_TX_STORE_KM_EVENT                        BIT(6)
>> +#define HDCP_TX_IS_RECEIVER_ID_VALID_EVENT     BIT(7)
>> +
>> +#define EDID_LENGTH_BYTE                       0
>> +#define EDID_SEGMENT_BUMBER                    1
>> +#define EDID_DATA                              2
>> +#define EDID_BLOCK_SIZE                                128
>> +
>> +#define TU_SIZE                                        30
>> +
>> +/* audio */
>> +#define AUDIO_PACK_EN(x)                       ((x) << 8)
>> +#define SAMPLING_FREQ(x)                       ((x) << 16)
>> +#define ORIGINAL_SAMP_FREQ(x)                  ((x) << 24)
>> +#define SYNC_WR_TO_CH_ZERO                     BIT(1)
>> +
>> +enum voltage_swing_level {
>> +       VOLTAGE_LEVEL_0,
>> +       VOLTAGE_LEVEL_1,
>> +       VOLTAGE_LEVEL_2,
>> +       VOLTAGE_LEVEL_3,
>> +};
>> +
>> +enum pre_emphasis_level {
>> +       PRE_EMPHASIS_LEVEL_0,
>> +       PRE_EMPHASIS_LEVEL_1,
>> +       PRE_EMPHASIS_LEVEL_2,
>> +       PRE_EMPHASIS_LEVEL_3,
>> +};
>> +
>> +enum pattern_set {
>> +       PRBS7           = BIT(0),
>> +       D10_2           = BIT(1),
>> +       TRAINING_PTN1   = BIT(2),
>> +       TRAINING_PTN2   = BIT(3),
>> +       DP_NONE         = BIT(4)
>> +};
>> +
>> +enum VIC_PXL_ENCODING_FORMAT {
> lowercase name
>
>> +       PXL_RGB = 0x1,
>> +       YCBCR_4_4_4 = 0x2,
>> +       YCBCR_4_2_2 = 0x4,
>> +       YCBCR_4_2_0 = 0x8,
>> +       Y_ONLY = 0x10,
>> +};
>> +
>> +enum VIC_COLOR_DEPTH {
> and here
>
>> +       BCS_6 = 0x1,
>> +       BCS_8 = 0x2,
>> +       BCS_10 = 0x4,
>> +       BCS_12 = 0x8,
>> +       BCS_16 = 0x10,
>> +};
>> +
>> +enum VIC_BT_TYPE {
> and here
>
>> +       BT_601 = 0x0,
>> +       BT_709 = 0x1,
>> +};
>> +
>> +#endif /* _CDN_DP_REG_H */
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> index edd7ec2..98302b3 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> @@ -969,7 +969,7 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
>>                  vop_dsp_hold_valid_irq_disable(vop);
>>          }
>>
>> -       pin_pol = 0x8;
>> +       pin_pol = (s->output_type == DRM_MODE_CONNECTOR_DisplayPort) ? 0 : 0x8;
> There's no sense checking output_type here and then again in the
> switch statement. Instead, pull 0x8 into a #define and only assign it
> to pin_pol in the individual case statements below.
>
>>          pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1;
>>          pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : (1 << 1);
>>          VOP_CTRL_SET(vop, pin_pol, pin_pol);
>> @@ -991,6 +991,10 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
>>                  VOP_CTRL_SET(vop, mipi_pin_pol, pin_pol);
>>                  VOP_CTRL_SET(vop, mipi_en, 1);
>>                  break;
>> +       case DRM_MODE_CONNECTOR_DisplayPort:
>> +               VOP_CTRL_SET(vop, dp_pin_pol, pin_pol);
>> +               VOP_CTRL_SET(vop, dp_en, 1);
>> +               break;
>>          default:
>>                  DRM_ERROR("unsupport connector_type[%d]\n", s->output_type);
>>          }
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>> index ff4f52e..50a045c 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>> @@ -45,6 +45,7 @@ struct vop_ctrl {
>>          struct vop_reg edp_en;
>>          struct vop_reg hdmi_en;
>>          struct vop_reg mipi_en;
>> +       struct vop_reg dp_en;
>>          struct vop_reg out_mode;
>>          struct vop_reg dither_down;
>>          struct vop_reg dither_up;
>> @@ -53,6 +54,7 @@ struct vop_ctrl {
>>          struct vop_reg hdmi_pin_pol;
>>          struct vop_reg edp_pin_pol;
>>          struct vop_reg mipi_pin_pol;
>> +       struct vop_reg dp_pin_pol;
>>
>>          struct vop_reg htotal_pw;
>>          struct vop_reg hact_st_end;
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>> index 5b1ae1f..dcf172e 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>> +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>> @@ -281,6 +281,7 @@ static const struct vop_data rk3288_vop = {
>>   static const struct vop_ctrl rk3399_ctrl_data = {
>>          .standby = VOP_REG(RK3399_SYS_CTRL, 0x1, 22),
>>          .gate_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 23),
>> +       .dp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 11),
>>          .rgb_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 12),
>>          .hdmi_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 13),
>>          .edp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 14),
>> @@ -290,6 +291,7 @@ static const struct vop_ctrl rk3399_ctrl_data = {
>>          .data_blank = VOP_REG(RK3399_DSP_CTRL0, 0x1, 19),
>>          .out_mode = VOP_REG(RK3399_DSP_CTRL0, 0xf, 0),
>>          .rgb_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16),
>> +       .dp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16),
>>          .hdmi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 20),
>>          .edp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 24),
>>          .mipi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 28),
>> --
>> 2.6.3
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel at lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>
>




From mark.yao at rock-chips.com  Wed Jul 13 20:31:24 2016
From: mark.yao at rock-chips.com (Mark Yao)
Date: Thu, 14 Jul 2016 11:31:24 +0800
Subject: [PATCH 0/6] drm/rockchip: some fixes
Message-ID: <1468467090-6108-1-git-send-email-mark.yao@rock-chips.com>

Here are some fixes for drm/rockchip:

These following patches were sent to upstream, seems no doubt about them,
I just rebase them to newest Dave's branch and resent.
  drm/rockchip: vop: correct the source size of uv scale factor setting
  drm/rockchip: vop: add uv_vir register field for RK3036 VOP
  drm/rockchip: fix "should it be static?" warnings
  drm/rockchip: fb: add missing header
  drm/rockchip: dw_hdmi: remove unused #include

This patch is new one, only register rename
  drm/rockchip: vop: correct rk3036 register define

John Keeping (3):
  drm/rockchip: dw_hdmi: remove unused #include
  drm/rockchip: fb: add missing header
  drm/rockchip: fix "should it be static?" warnings

Mark Yao (2):
  drm/rockchip: vop: correct the source size of uv scale factor setting
  drm/rockchip: vop: correct rk3036 register define

Yakir Yang (1):
  drm/rockchip: vop: add uv_vir register field for RK3036 VOP

 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 1 -
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 2 +-
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c  | 3 ++-
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 8 ++++----
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 7 ++++---
 5 files changed, 11 insertions(+), 10 deletions(-)

-- 
1.9.1




From mark.yao at rock-chips.com  Wed Jul 13 20:31:25 2016
From: mark.yao at rock-chips.com (Mark Yao)
Date: Thu, 14 Jul 2016 11:31:25 +0800
Subject: [PATCH 1/6] drm/rockchip: dw_hdmi: remove unused #include
In-Reply-To: <1468467090-6108-1-git-send-email-mark.yao@rock-chips.com>
References: <1468467090-6108-1-git-send-email-mark.yao@rock-chips.com>
Message-ID: <1468467090-6108-2-git-send-email-mark.yao@rock-chips.com>

From: John Keeping <john at metanate.com>

drm_encoder_slave is not used in this file.

Signed-off-by: John Keeping <john at metanate.com>
Signed-off-by: Mark Yao <mark.yao at rock-chips.com>
---
 drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 801110f..0665fb9 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -15,7 +15,6 @@
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_edid.h>
-#include <drm/drm_encoder_slave.h>
 #include <drm/bridge/dw_hdmi.h>
 
 #include "rockchip_drm_drv.h"
-- 
1.9.1




From mark.yao at rock-chips.com  Wed Jul 13 20:31:26 2016
From: mark.yao at rock-chips.com (Mark Yao)
Date: Thu, 14 Jul 2016 11:31:26 +0800
Subject: [PATCH 2/6] drm/rockchip: fb: add missing header
In-Reply-To: <1468467090-6108-1-git-send-email-mark.yao@rock-chips.com>
References: <1468467090-6108-1-git-send-email-mark.yao@rock-chips.com>
Message-ID: <1468467090-6108-3-git-send-email-mark.yao@rock-chips.com>

From: John Keeping <john at metanate.com>

This fixes the following sparse warnings:

drivers/gpu/drm/rockchip/rockchip_drm_fb.c:32:23: warning: symbol 'rockchip_fb_get_gem_obj' was not declared. Should it be static?
drivers/gpu/drm/rockchip/rockchip_drm_fb.c:315:24: warning: symbol 'rockchip_drm_framebuffer_init' was not declared. Should it be static?
drivers/gpu/drm/rockchip/rockchip_drm_fb.c:329:6: warning: symbol 'rockchip_drm_mode_config_init' was not declared. Should it be static?

Signed-off-by: John Keeping <john at metanate.com>
Signed-off-by: Mark Yao <mark.yao at rock-chips.com>
---
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
index 20f12bc..4fe2ab3 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
@@ -20,6 +20,7 @@
 #include <drm/drm_crtc_helper.h>
 
 #include "rockchip_drm_drv.h"
+#include "rockchip_drm_fb.h"
 #include "rockchip_drm_gem.h"
 
 #define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
-- 
1.9.1




From mark.yao at rock-chips.com  Wed Jul 13 20:31:27 2016
From: mark.yao at rock-chips.com (Mark Yao)
Date: Thu, 14 Jul 2016 11:31:27 +0800
Subject: [PATCH 3/6] drm/rockchip: fix "should it be static?" warnings
In-Reply-To: <1468467090-6108-1-git-send-email-mark.yao@rock-chips.com>
References: <1468467090-6108-1-git-send-email-mark.yao@rock-chips.com>
Message-ID: <1468467090-6108-4-git-send-email-mark.yao@rock-chips.com>

From: John Keeping <john at metanate.com>

Combined with the previous commit, this fixes all of the sparse warnings
in drm/rockchip.

Signed-off-by: John Keeping <john at metanate.com>
Signed-off-by: Mark Yao <mark.yao at rock-chips.com>
---
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 2 +-
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c  | 2 +-
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 4 ++--
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 2 +-
 4 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index d665fb0..7216180 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -257,7 +257,7 @@ static void rockchip_drm_unbind(struct device *dev)
 	dev_set_drvdata(dev, NULL);
 }
 
-void rockchip_drm_lastclose(struct drm_device *dev)
+static void rockchip_drm_lastclose(struct drm_device *dev)
 {
 	struct rockchip_drm_private *priv = dev->dev_private;
 
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
index 4fe2ab3..b33debc 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
@@ -246,7 +246,7 @@ rockchip_atomic_commit_tail(struct drm_atomic_state *state)
 	drm_atomic_helper_cleanup_planes(dev, state);
 }
 
-struct drm_mode_config_helper_funcs rockchip_mode_config_helpers = {
+static struct drm_mode_config_helper_funcs rockchip_mode_config_helpers = {
 	.atomic_commit_tail = rockchip_atomic_commit_tail,
 };
 
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 6255e5b..e01c435 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -798,7 +798,7 @@ static const struct drm_plane_helper_funcs plane_helper_funcs = {
 	.atomic_disable = vop_plane_atomic_disable,
 };
 
-void vop_atomic_plane_reset(struct drm_plane *plane)
+static void vop_atomic_plane_reset(struct drm_plane *plane)
 {
 	struct vop_plane_state *vop_plane_state =
 					to_vop_plane_state(plane->state);
@@ -815,7 +815,7 @@ void vop_atomic_plane_reset(struct drm_plane *plane)
 	plane->state->plane = plane;
 }
 
-struct drm_plane_state *
+static struct drm_plane_state *
 vop_atomic_plane_duplicate_state(struct drm_plane *plane)
 {
 	struct vop_plane_state *old_vop_plane_state;
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 3166b46..52661d1 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -299,7 +299,7 @@ static int vop_remove(struct platform_device *pdev)
 	return 0;
 }
 
-struct platform_driver vop_platform_driver = {
+static struct platform_driver vop_platform_driver = {
 	.probe = vop_probe,
 	.remove = vop_remove,
 	.driver = {
-- 
1.9.1




From mark.yao at rock-chips.com  Wed Jul 13 20:31:28 2016
From: mark.yao at rock-chips.com (Mark Yao)
Date: Thu, 14 Jul 2016 11:31:28 +0800
Subject: [PATCH 4/6] drm/rockchip: vop: add uv_vir register field for RK3036
 VOP
In-Reply-To: <1468467090-6108-1-git-send-email-mark.yao@rock-chips.com>
References: <1468467090-6108-1-git-send-email-mark.yao@rock-chips.com>
Message-ID: <1468467090-6108-5-git-send-email-mark.yao@rock-chips.com>

From: Yakir Yang <ykk at rock-chips.com>

The WIN0 of RK3036 VOP could support YUV data format, but driver
forget to add the uv_vir register field for it.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
Signed-off-by: Mark Yao <mark.yao at rock-chips.com>
---
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 52661d1..a348a7a 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -210,6 +210,7 @@ static const struct vop_win_phy rk3036_win0_data = {
 	.yrgb_mst = VOP_REG(RK3036_WIN0_YRGB_MST, 0xffffffff, 0),
 	.uv_mst = VOP_REG(RK3036_WIN0_CBR_MST, 0xffffffff, 0),
 	.yrgb_vir = VOP_REG(RK3036_WIN0_VIR, 0xffff, 0),
+	.uv_vir = VOP_REG(RK3036_WIN0_VIR, 0x1fff, 16),
 };
 
 static const struct vop_win_phy rk3036_win1_data = {
-- 
1.9.1




From mark.yao at rock-chips.com  Wed Jul 13 20:33:27 2016
From: mark.yao at rock-chips.com (Mark Yao)
Date: Thu, 14 Jul 2016 11:33:27 +0800
Subject: [PATCH 5/6] drm/rockchip: vop: correct the source size of uv scale
 factor setting
In-Reply-To: <1468467090-6108-1-git-send-email-mark.yao@rock-chips.com>
References: <1468467090-6108-1-git-send-email-mark.yao@rock-chips.com>
Message-ID: <1468467207-6715-1-git-send-email-mark.yao@rock-chips.com>

When the input color format is YUV, we need to do some external scale
for CBCR. Like,
 * In YUV420 data format:
     cbcr_xscale = dst_w / src_w * 2;
     cbcr_yscale = dst_h / src_h * 2;
 * In YUV422 data format:
     cbcr_xscale = dst_w / src_w * 2;
     cbcr_yscale = dst_h / src_h;
 * In YUV444 data format
     cbcr_xscale = dst_w / src_w;
     cbcr_yscale = dst_h / src_h;

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
Signed-off-by: Mark Yao <mark.yao at rock-chips.com>
---
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index e01c435..aad105b 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -328,9 +328,9 @@ static void scl_vop_cal_scl_fac(struct vop *vop, const struct vop_win_data *win,
 			    scl_cal_scale2(src_h, dst_h));
 		if (is_yuv) {
 			VOP_SCL_SET(vop, win, scale_cbcr_x,
-				    scl_cal_scale2(src_w, dst_w));
+				    scl_cal_scale2(cbcr_src_w, dst_w));
 			VOP_SCL_SET(vop, win, scale_cbcr_y,
-				    scl_cal_scale2(src_h, dst_h));
+				    scl_cal_scale2(cbcr_src_h, dst_h));
 		}
 		return;
 	}
-- 
1.9.1




From mark.yao at rock-chips.com  Wed Jul 13 20:33:41 2016
From: mark.yao at rock-chips.com (Mark Yao)
Date: Thu, 14 Jul 2016 11:33:41 +0800
Subject: [PATCH 6/6] drm/rockchip: vop: correct rk3036 register define
In-Reply-To: <1468467090-6108-1-git-send-email-mark.yao@rock-chips.com>
References: <1468467090-6108-1-git-send-email-mark.yao@rock-chips.com>
Message-ID: <1468467221-6819-1-git-send-email-mark.yao@rock-chips.com>

Signed-off-by: Mark Yao <mark.yao at rock-chips.com>
Reported-by: Tomasz Figa <tfiga at chromium.org>
---
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index a348a7a..919992c 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -190,7 +190,7 @@ static const struct vop_data rk3288_vop = {
 	.win_size = ARRAY_SIZE(rk3288_vop_win_data),
 };
 
-static const struct vop_scl_regs rk3066_win_scl = {
+static const struct vop_scl_regs rk3036_win_scl = {
 	.scale_yrgb_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0),
 	.scale_yrgb_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 16),
 	.scale_cbcr_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0),
@@ -198,7 +198,7 @@ static const struct vop_scl_regs rk3066_win_scl = {
 };
 
 static const struct vop_win_phy rk3036_win0_data = {
-	.scl = &rk3066_win_scl,
+	.scl = &rk3036_win_scl,
 	.data_formats = formats_win_full,
 	.nformats = ARRAY_SIZE(formats_win_full),
 	.enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 0),
-- 
1.9.1




From ykk at rock-chips.com  Wed Jul 13 21:15:04 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Thu, 14 Jul 2016 12:15:04 +0800
Subject: [PATCH v4 0/4] Add PSR function support for Analogix/Rockchip DP
Message-ID: <1468469704-12440-1-git-send-email-ykk@rock-chips.com>


The full name of PSR is Panel Self Refresh, panel device could refresh
itself with the hardware framebuffer in panel, this would make a lots
of sense to save the power consumption.

This v3 version have splited an common PSR driver for Rockchip, which is
biggest changes from v2.

This thread is based on Mark's RK3399 VOP thread[0] and my RK3399 eDP
thread[1].

[0]: https://patchwork.kernel.org/patch/8886041/
[1]: https://patchwork.kernel.org/patch/9204497/


Changes in v4:
- Avoid the weird behavior in rockchip_drm_wait_line_flag(). (Sean)
- Make line_flag_num_x to an array. (Sean)
- Remove the unused vop_cfg_done() in vop_line_flag_irq_enable(). (Stephane, reviewed in Google gerrit)
    [https://chromium-review.googlesource.com/#/c/349084/33/drivers/gpu/drm/rockchip/rockchip_drm_vop.c at 466]
- Tuck the global "psr_list" & "psr_list_mutex" in struct rockchip_drm_private. (Sean)
- Move the access of "psr->state" under "psr->state_mutex"'s protect. (Sean)
- Let "psr->state = PSR_FLUSH" under "psr->state_mutex"'s protect. (Sean)
- Collect psr_enable() and psr_disable() into psr_set_state()
- s/5\ second/PSR_FLUSH_TIMEOUT/ (Sean)
- Flush the psr callback in vop_crtc_disable(). (St?phane, reviewed in Google gerrit)
    [https://chromium-review.googlesource.com/#/c/349084/6/drivers/gpu/drm/rockchip/rockchip_drm_vop.c at 475]
- Add the missing file head with license. (St?phane, reviewed in Google gerrit)
    [https://chromium-review.googlesource.com/#/c/357563/1/drivers/gpu/drm/rockchip/rockchip_drm_psr.h at 3]
- Downgrade the PSR version print message to debug level. (Sean)
- Return 'void' instead of 'int' in analogix_dp_enable_sink_psr(). (Sean)
- Delete the unused read dpcd operations in analogix_dp_enable_sink_psr(). (Sean)
- Delete the arbitrary usleep_range in analogix_dp_enable_psr_crc. (Sean).
- Clean up the hardcoded values in analogix_dp_send_psr_spd(). (Sean)
- Rename "active/inactive" to "enable/disable". (Sean, Dominik)
- Keep set the PSR_VID_CRC_FLUSH gate in analogix_dp_enable_psr_crc().
- Return 'void' instead of 'int' in analogix_dp_psr_set(). (Sean)
- Pull the 10ms delay time out into a #define. (Sean)
- Improved the code of analogix_dp_psr_work(). (Sean)
- Indented with spaces for new numbers in rockchip_dp_device struct. (St?phane, reviewed at Google gerrit)
    [https://chromium-review.googlesource.com/#/c/349085/33/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c at 83]

Changes in v3:
- Export the 'rockchip_drm_wait_line_flag' symbol, and document it.
- Add 'line_flag_num_0' for RK3288/RK3036
- Remove the notify for waiting line_flag event (Daniel)
- split the psr flow into an common abstracted PSR driver
- implement the 'fb->dirty' callback function (Daniel)
- avoid to use notify to acqiure for vact event (Daniel)
- remove psr_active() callback which introduce in v2
- split analogix_dp_enable_psr(), make it more clearly
    analogix_dp_detect_sink_psr()
    analogix_dp_enable_sink_psr()
- remove some nosie register setting comments
- split the common psr logic into a seperate driver, make this to a
  simple sub-psr device driver.

Changes in v2:
- Introduce in v2, split VOP line flag changes out
- introduce in v2, splite the common Analogix DP changes out
- remove vblank notify out (Daniel)
- create a psr_active() callback in vop data struct.

Yakir Yang (4):
  drm/rockchip: vop: export line flag function
  drm/rockchip: add an common abstracted PSR driver
  drm/bridge: analogix_dp: add the PSR function support
  drm/rockchip: analogix_dp: implement PSR function

 drivers/gpu/drm/bridge/analogix/analogix_dp_core.c |  60 ++++++
 drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |   4 +
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  |  49 +++++
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h  |  28 +++
 drivers/gpu/drm/rockchip/Makefile                  |   2 +-
 drivers/gpu/drm/rockchip/analogix_dp-rockchip.c    |  57 ++++++
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c        |   4 +
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h        |   6 +
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c         |  12 ++
 drivers/gpu/drm/rockchip/rockchip_drm_psr.c        | 223 +++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_psr.h        |  26 +++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c        | 147 ++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h        |   2 +
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c        |   4 +
 include/drm/bridge/analogix_dp.h                   |   3 +
 15 files changed, 626 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.h

-- 
1.9.1




From ykk at rock-chips.com  Wed Jul 13 21:15:44 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Thu, 14 Jul 2016 12:15:44 +0800
Subject: [PATCH v4 1/4] drm/rockchip: vop: export line flag function
In-Reply-To: <1468469704-12440-1-git-send-email-ykk@rock-chips.com>
References: <1468469704-12440-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1468469744-12592-1-git-send-email-ykk@rock-chips.com>

VOP have integrated a hardware counter which indicate the exact display
line that vop is scanning. And if we're interested in a specific line,
we can set the line number to vop line_flag register, and then vop would
generate a line_flag interrupt for it.

For example eDP PSR function is interested in the vertical blanking
period, then driver could set the line number to zero.

This patch have exported a symbol that allow other driver to listen the
line flag event with given timeout limit:
-  rockchip_drm_wait_line_flag()

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
Changes in v4:
- Avoid the weird behavior in rockchip_drm_wait_line_flag(). (Sean)
- Make line_flag_num_x to an array. (Sean)
- Remove the unused vop_cfg_done() in vop_line_flag_irq_enable(). (Stephane, reviewed in Google gerrit)
    [https://chromium-review.googlesource.com/#/c/349084/33/drivers/gpu/drm/rockchip/rockchip_drm_vop.c at 466]

Changes in v3:
- Export the 'rockchip_drm_wait_line_flag' symbol, and document it.
- Add 'line_flag_num_0' for RK3288/RK3036
- Remove the notify for waiting line_flag event (Daniel)

Changes in v2:
- Introduce in v2, split VOP line flag changes out

 drivers/gpu/drm/rockchip/rockchip_drm_drv.h |   3 +
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 118 ++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   2 +
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c |   4 +
 4 files changed, 127 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index ea39329..239b830 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -70,4 +70,7 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
 				   struct device *dev);
 void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
 				    struct device *dev);
+int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
+				unsigned int mstimeout);
+
 #endif /* _ROCKCHIP_DRM_DRV_H_ */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index c8a62a8..69d32f1 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -121,6 +121,8 @@ struct vop {
 	/* protected by dev->event_lock */
 	struct drm_pending_vblank_event *event;
 
+	struct completion line_flag_completion;
+
 	const struct vop_data *data;
 
 	uint32_t *regsbak;
@@ -431,6 +433,71 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop)
 	spin_unlock_irqrestore(&vop->irq_lock, flags);
 }
 
+/*
+ * (1) each frame starts at the start of the Vsync pulse which is signaled by
+ *     the "FRAME_SYNC" interrupt.
+ * (2) the active data region of each frame ends at dsp_vact_end
+ * (3) we should program this same number (dsp_vact_end) into dsp_line_frag_num,
+ *      to get "LINE_FLAG" interrupt at the end of the active on screen data.
+ *
+ * VOP_INTR_CTRL0.dsp_line_frag_num = VOP_DSP_VACT_ST_END.dsp_vact_end
+ * Interrupts
+ * LINE_FLAG -------------------------------+
+ * FRAME_SYNC ----+                         |
+ *                |                         |
+ *                v                         v
+ *                | Vsync | Vbp |  Vactive  | Vfp |
+ *                        ^     ^           ^     ^
+ *                        |     |           |     |
+ *                        |     |           |     |
+ * dsp_vs_end ------------+     |           |     |   VOP_DSP_VTOTAL_VS_END
+ * dsp_vact_start --------------+           |     |   VOP_DSP_VACT_ST_END
+ * dsp_vact_end ----------------------------+     |   VOP_DSP_VACT_ST_END
+ * dsp_total -------------------------------------+   VOP_DSP_VTOTAL_VS_END
+ */
+static bool vop_line_flag_irq_is_enabled(struct vop *vop)
+{
+	uint32_t line_flag_irq;
+	unsigned long flags;
+
+	spin_lock_irqsave(&vop->irq_lock, flags);
+
+	line_flag_irq = VOP_INTR_GET_TYPE(vop, enable, LINE_FLAG_INTR);
+
+	spin_unlock_irqrestore(&vop->irq_lock, flags);
+
+	return !!line_flag_irq;
+}
+
+static void vop_line_flag_irq_enable(struct vop *vop, int line_num)
+{
+	unsigned long flags;
+
+	if (WARN_ON(!vop->is_enabled))
+		return;
+
+	spin_lock_irqsave(&vop->irq_lock, flags);
+
+	VOP_CTRL_SET(vop, line_flag_num[0], line_num);
+	VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1);
+
+	spin_unlock_irqrestore(&vop->irq_lock, flags);
+}
+
+static void vop_line_flag_irq_disable(struct vop *vop)
+{
+	unsigned long flags;
+
+	if (WARN_ON(!vop->is_enabled))
+		return;
+
+	spin_lock_irqsave(&vop->irq_lock, flags);
+
+	VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 0);
+
+	spin_unlock_irqrestore(&vop->irq_lock, flags);
+}
+
 static void vop_enable(struct drm_crtc *crtc)
 {
 	struct vop *vop = to_vop(crtc);
@@ -1157,6 +1224,13 @@ static irqreturn_t vop_isr(int irq, void *data)
 		ret = IRQ_HANDLED;
 	}
 
+	if (active_irqs & LINE_FLAG_INTR) {
+		if (!completion_done(&vop->line_flag_completion))
+			complete(&vop->line_flag_completion);
+		active_irqs &= ~LINE_FLAG_INTR;
+		ret = IRQ_HANDLED;
+	}
+
 	if (active_irqs & FS_INTR) {
 		drm_crtc_handle_vblank(crtc);
 		vop_handle_vblank(vop);
@@ -1255,6 +1329,7 @@ static int vop_create_crtc(struct vop *vop)
 
 	init_completion(&vop->dsp_hold_completion);
 	init_completion(&vop->wait_update_complete);
+	init_completion(&vop->line_flag_completion);
 	crtc->port = port;
 	rockchip_register_crtc_funcs(crtc, &private_crtc_funcs);
 
@@ -1411,6 +1486,49 @@ static void vop_win_init(struct vop *vop)
 	}
 }
 
+/**
+ * rockchip_drm_wait_line_flag - acqiure the give line flag event
+ * @crtc: CRTC to enable line flag
+ * @line_num: interested line number
+ * @mstimeout: millisecond for timeout
+ *
+ * Driver would hold here until the interested line flag interrupt have
+ * happened or timeout to wait.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
+				unsigned int mstimeout)
+{
+	struct vop *vop = to_vop(crtc);
+	unsigned long jiffies_left;
+
+	if (!crtc || !vop->is_enabled)
+		return -ENODEV;
+
+	if (line_num > crtc->mode.vtotal || mstimeout <= 0)
+		return -EINVAL;
+
+	if (vop_line_flag_irq_is_enabled(vop))
+		return -EBUSY;
+
+	reinit_completion(&vop->line_flag_completion);
+	vop_line_flag_irq_enable(vop, line_num);
+
+	jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion,
+						   msecs_to_jiffies(mstimeout));
+	vop_line_flag_irq_disable(vop);
+
+	if (jiffies_left == 0) {
+		dev_err(vop->dev, "Timeout waiting for IRQ\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_wait_line_flag);
+
 static int vop_bind(struct device *dev, struct device *master, void *data)
 {
 	struct platform_device *pdev = to_platform_device(dev);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index ff4f52e..1dbc526 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -61,6 +61,8 @@ struct vop_ctrl {
 	struct vop_reg hpost_st_end;
 	struct vop_reg vpost_st_end;
 
+	struct vop_reg line_flag_num[2];
+
 	struct vop_reg cfg_done;
 };
 
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 6f42e56..eea8427 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -122,6 +122,7 @@ static const struct vop_ctrl rk3036_ctrl_data = {
 	.hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0),
 	.vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
 	.vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0),
+	.line_flag_num[0] = VOP_REG(RK3036_INT_STATUS, 0xfff, 12),
 	.cfg_done = VOP_REG(RK3036_REG_CFG_DONE, 0x1, 0),
 };
 
@@ -221,6 +222,7 @@ static const struct vop_ctrl rk3288_ctrl_data = {
 	.vact_st_end = VOP_REG(RK3288_DSP_VACT_ST_END, 0x1fff1fff, 0),
 	.hpost_st_end = VOP_REG(RK3288_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
 	.vpost_st_end = VOP_REG(RK3288_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
+	.line_flag_num[0] = VOP_REG(RK3288_INTR_CTRL0, 0x1fff, 12),
 	.cfg_done = VOP_REG(RK3288_REG_CFG_DONE, 0x1, 0),
 };
 
@@ -299,6 +301,8 @@ static const struct vop_ctrl rk3399_ctrl_data = {
 	.vact_st_end = VOP_REG(RK3399_DSP_VACT_ST_END, 0x1fff1fff, 0),
 	.hpost_st_end = VOP_REG(RK3399_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
 	.vpost_st_end = VOP_REG(RK3399_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
+	.line_flag_num[0] = VOP_REG(RK3399_LINE_FLAG, 0xffff, 0),
+	.line_flag_num[1] = VOP_REG(RK3399_LINE_FLAG, 0xffff, 16),
 	.cfg_done = VOP_REG_MASK(RK3399_REG_CFG_DONE, 0x1, 0),
 };
 
-- 
1.9.1




From ykk at rock-chips.com  Wed Jul 13 21:15:49 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Thu, 14 Jul 2016 12:15:49 +0800
Subject: [PATCH v4 2/4] drm/rockchip: add an common abstracted PSR driver
In-Reply-To: <1468469704-12440-1-git-send-email-ykk@rock-chips.com>
References: <1468469704-12440-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1468469749-12636-1-git-send-email-ykk@rock-chips.com>

The PSR driver have exported four symbols for specific device driver:
- rockchip_drm_psr_register()
- rockchip_drm_psr_unregister()
- rockchip_drm_psr_enable()
- rockchip_drm_psr_disable()
- rockchip_drm_psr_flush()

Encoder driver should call the register/unregister interfaces to hook
itself into common PSR driver, encoder have implement the 'psr_set'
callback which use the set PSR state in hardware side.

Crtc driver would call the enable/disable interfaces when vblank is
enable/disable, after that the common PSR driver would call the encoder
registered callback to set the PSR state.

Fb driver would call the flush interface in 'fb->dirty' callback, this
helper function would force all PSR enabled encoders to exit from PSR
for 3 seconds.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
Changes in v4:
- Tuck the global "psr_list" & "psr_list_mutex" in struct rockchip_drm_private. (Sean)
- Move the access of "psr->state" under "psr->state_mutex"'s protect. (Sean)
- Let "psr->state = PSR_FLUSH" under "psr->state_mutex"'s protect. (Sean)
- Collect psr_enable() and psr_disable() into psr_set_state()
- s/5\ second/PSR_FLUSH_TIMEOUT/ (Sean)
- Flush the psr callback in vop_crtc_disable(). (St?phane, reviewed in Google gerrit)
    [https://chromium-review.googlesource.com/#/c/349084/6/drivers/gpu/drm/rockchip/rockchip_drm_vop.c at 475]
- Add the missing file head with license. (St?phane, reviewed in Google gerrit)
    [https://chromium-review.googlesource.com/#/c/357563/1/drivers/gpu/drm/rockchip/rockchip_drm_psr.h at 3]

Changes in v3:
- split the psr flow into an common abstracted PSR driver
- implement the 'fb->dirty' callback function (Daniel)
- avoid to use notify to acqiure for vact event (Daniel)
- remove psr_active() callback which introduce in v2

Changes in v2: None

 drivers/gpu/drm/rockchip/Makefile           |   2 +-
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c |   4 +
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h |   3 +
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c  |  12 ++
 drivers/gpu/drm/rockchip/rockchip_drm_psr.c | 223 ++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_psr.h |  26 ++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c |  29 ++++
 7 files changed, 298 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.h

diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index 05d0713..9746365 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -3,7 +3,7 @@
 # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
 
 rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
-		rockchip_drm_gem.o rockchip_drm_vop.o
+		rockchip_drm_gem.o rockchip_drm_psr.o rockchip_drm_vop.o
 rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
 
 obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index d665fb0..26c12b3 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -156,6 +156,9 @@ static int rockchip_drm_bind(struct device *dev)
 
 	drm_dev->dev_private = private;
 
+	INIT_LIST_HEAD(&private->psr_list);
+	mutex_init(&private->psr_list_mutex);
+
 	drm_mode_config_init(drm_dev);
 
 	rockchip_drm_mode_config_init(drm_dev);
@@ -218,6 +221,7 @@ static int rockchip_drm_bind(struct device *dev)
 
 	if (is_support_iommu)
 		arm_iommu_release_mapping(mapping);
+
 	return 0;
 err_fbdev_fini:
 	rockchip_drm_fbdev_fini(drm_dev);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index 239b830..9c34c9e 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -61,6 +61,9 @@ struct rockchip_drm_private {
 	struct drm_gem_object *fbdev_bo;
 	const struct rockchip_crtc_funcs *crtc_funcs[ROCKCHIP_MAX_CRTC];
 	struct drm_atomic_state *state;
+
+	struct list_head psr_list;
+	struct mutex psr_list_mutex;
 };
 
 int rockchip_register_crtc_funcs(struct drm_crtc *crtc,
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
index 20f12bc..36afd9c 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
@@ -21,6 +21,7 @@
 
 #include "rockchip_drm_drv.h"
 #include "rockchip_drm_gem.h"
+#include "rockchip_drm_psr.h"
 
 #define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
 
@@ -66,9 +67,20 @@ static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
 				     rockchip_fb->obj[0], handle);
 }
 
+static int rockchip_drm_fb_dirty(struct drm_framebuffer *fb,
+				 struct drm_file *file,
+				 unsigned int flags, unsigned int color,
+				 struct drm_clip_rect *clips,
+				 unsigned int num_clips)
+{
+	rockchip_drm_psr_flush(fb->dev);
+	return 0;
+}
+
 static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
 	.destroy	= rockchip_drm_fb_destroy,
 	.create_handle	= rockchip_drm_fb_create_handle,
+	.dirty		= rockchip_drm_fb_dirty,
 };
 
 static struct rockchip_drm_fb *
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
new file mode 100644
index 0000000..e79ea18
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Yakir Yang <ykk at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_psr.h"
+
+#define PSR_FLUSH_TIMEOUT	msecs_to_jiffies(3000) /* 3 seconds */
+
+static LIST_HEAD(psr_list);
+static DEFINE_MUTEX(psr_list_mutex);
+
+enum psr_state {
+	PSR_FLUSH,
+	PSR_ENABLE,
+	PSR_DISABLE,
+};
+
+struct psr_drv {
+	struct list_head list;
+	enum psr_state state;
+	struct mutex state_mutex;
+
+	struct timer_list flush_timer;
+
+	struct drm_encoder *encoder;
+	void (*set)(struct drm_encoder *encoder, bool enable);
+};
+
+static struct psr_drv *find_psr_by_crtc(struct drm_crtc *crtc)
+{
+	struct rockchip_drm_private *drm_drv = crtc->dev->dev_private;
+	struct psr_drv *psr;
+
+	mutex_lock(&drm_drv->psr_list_mutex);
+	list_for_each_entry(psr, &drm_drv->psr_list, list) {
+		if (psr->encoder->crtc == crtc) {
+			mutex_unlock(&drm_drv->psr_list_mutex);
+			return psr;
+		}
+	}
+	mutex_unlock(&drm_drv->psr_list_mutex);
+
+	return ERR_PTR(-ENODEV);
+}
+
+static void psr_set_state(struct psr_drv *psr, enum psr_state state)
+{
+	mutex_lock(&psr->state_mutex);
+
+	if (psr->state == state) {
+		mutex_unlock(&psr->state_mutex);
+		return;
+	}
+
+	psr->state = state;
+	switch (state) {
+	case PSR_ENABLE:
+		psr->set(psr->encoder, true);
+		break;
+
+	case PSR_DISABLE:
+	case PSR_FLUSH:
+		psr->set(psr->encoder, false);
+		break;
+	};
+
+	mutex_unlock(&psr->state_mutex);
+}
+
+static void psr_flush_handler(unsigned long data)
+{
+	struct psr_drv *psr = (struct psr_drv *)data;
+
+	if (!psr || psr->state != PSR_FLUSH)
+		return;
+
+	psr_set_state(psr, PSR_ENABLE);
+}
+
+/**
+ * rockchip_drm_psr_enable - enable the encoder PSR which bind to given CRTC
+ * @crtc: CRTC to obtain the PSR encoder
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int rockchip_drm_psr_enable(struct drm_crtc *crtc)
+{
+	struct psr_drv *psr = find_psr_by_crtc(crtc);
+
+	if (IS_ERR(psr))
+		return PTR_ERR(psr);
+
+	psr_set_state(psr, PSR_ENABLE);
+	return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_psr_enable);
+
+/**
+ * rockchip_drm_psr_disable - disable the encoder PSR which bind to given CRTC
+ * @crtc: CRTC to obtain the PSR encoder
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int rockchip_drm_psr_disable(struct drm_crtc *crtc)
+{
+	struct psr_drv *psr = find_psr_by_crtc(crtc);
+
+	if (IS_ERR(psr))
+		return PTR_ERR(psr);
+
+	psr_set_state(psr, PSR_DISABLE);
+	return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_psr_disable);
+
+/**
+ * rockchip_drm_psr_flush - force to flush all registered PSR encoders
+ * @dev: drm device
+ *
+ * Disable the PSR function for all registered encoders, and then enable the
+ * PSR function back after PSR_FLUSH_TIMEOUT. If encoder PSR state have been
+ * changed during flush time, then keep the state no change after flush
+ * timeout.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+void rockchip_drm_psr_flush(struct drm_device *dev)
+{
+	struct rockchip_drm_private *drm_drv = dev->dev_private;
+	struct psr_drv *psr;
+
+	mutex_lock(&drm_drv->psr_list_mutex);
+	list_for_each_entry(psr, &drm_drv->psr_list, list) {
+		if (psr->state == PSR_DISABLE)
+			continue;
+
+		mod_timer(&psr->flush_timer,
+			  round_jiffies_up(jiffies + PSR_FLUSH_TIMEOUT));
+
+		psr_set_state(psr, PSR_FLUSH);
+	}
+	mutex_unlock(&drm_drv->psr_list_mutex);
+}
+EXPORT_SYMBOL(rockchip_drm_psr_flush);
+
+/**
+ * rockchip_drm_psr_register - register encoder to psr driver
+ * @encoder: encoder that obtain the PSR function
+ * @psr_set: call back to set PSR state
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int rockchip_drm_psr_register(struct drm_encoder *encoder,
+			void (*psr_set)(struct drm_encoder *, bool enable))
+{
+	struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
+	struct psr_drv *psr;
+
+	if (!encoder || !psr_set)
+		return -EINVAL;
+
+	psr = kzalloc(sizeof(struct psr_drv), GFP_KERNEL);
+	if (!psr)
+		return -ENOMEM;
+
+	setup_timer(&psr->flush_timer, psr_flush_handler, (unsigned long)psr);
+
+	mutex_init(&psr->state_mutex);
+
+	psr->state = PSR_DISABLE;
+	psr->encoder = encoder;
+	psr->set = psr_set;
+
+	mutex_lock(&drm_drv->psr_list_mutex);
+	list_add_tail(&psr->list, &drm_drv->psr_list);
+	mutex_unlock(&drm_drv->psr_list_mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_psr_register);
+
+/**
+ * rockchip_drm_psr_unregister - unregister encoder to psr driver
+ * @encoder: encoder that obtain the PSR function
+ * @psr_set: call back to set PSR state
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
+{
+	struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
+	struct psr_drv *psr;
+
+	mutex_lock(&drm_drv->psr_list_mutex);
+	list_for_each_entry(psr, &drm_drv->psr_list, list) {
+		if (psr->encoder == encoder) {
+			del_timer(&psr->flush_timer);
+			list_del(&psr->list);
+			kfree(psr);
+		}
+	}
+	mutex_unlock(&drm_drv->psr_list_mutex);
+}
+EXPORT_SYMBOL(rockchip_drm_psr_unregister);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.h b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
new file mode 100644
index 0000000..c35b688
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Yakir Yang <ykk at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef __ROCKCHIP_DRM_PSR___
+#define __ROCKCHIP_DRM_PSR___
+
+void rockchip_drm_psr_flush(struct drm_device *dev);
+int rockchip_drm_psr_enable(struct drm_crtc *crtc);
+int rockchip_drm_psr_disable(struct drm_crtc *crtc);
+
+int rockchip_drm_psr_register(struct drm_encoder *encoder,
+			void (*psr_set)(struct drm_encoder *, bool enable));
+void rockchip_drm_psr_unregister(struct drm_encoder *encoder);
+
+#endif /* __ROCKCHIP_DRM_PSR__ */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 69d32f1..e702fb3 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -34,6 +34,7 @@
 #include "rockchip_drm_drv.h"
 #include "rockchip_drm_gem.h"
 #include "rockchip_drm_fb.h"
+#include "rockchip_drm_psr.h"
 #include "rockchip_drm_vop.h"
 
 #define __REG_SET_RELAXED(x, off, mask, shift, v, write_mask) \
@@ -87,6 +88,8 @@
 #define to_vop_win(x) container_of(x, struct vop_win, base)
 #define to_vop_plane_state(x) container_of(x, struct vop_plane_state, base)
 
+#define VOP_PSR_SET_DELAY_TIME		msecs_to_jiffies(10)
+
 struct vop_plane_state {
 	struct drm_plane_state base;
 	int format;
@@ -121,6 +124,9 @@ struct vop {
 	/* protected by dev->event_lock */
 	struct drm_pending_vblank_event *event;
 
+	bool psr_enabled;
+	struct delayed_work psr_work;
+
 	struct completion line_flag_completion;
 
 	const struct vop_data *data;
@@ -629,6 +635,9 @@ static void vop_crtc_disable(struct drm_crtc *crtc)
 
 		crtc->state->event = NULL;
 	}
+
+	vop->psr_enabled = false;
+	INIT_DELAYED_WORK(&vop->psr_work, vop_psr_work);
 }
 
 static void vop_plane_destroy(struct drm_plane *plane)
@@ -923,6 +932,16 @@ static const struct drm_plane_funcs vop_plane_funcs = {
 	.atomic_destroy_state = vop_atomic_plane_destroy_state,
 };
 
+static void vop_psr_work(struct work_struct *work)
+{
+	struct vop *vop = container_of(work, typeof(*vop), psr_work.work);
+
+	if (vop->psr_enabled)
+		rockchip_drm_psr_enable(&vop->crtc);
+	else
+		rockchip_drm_psr_disable(&vop->crtc);
+}
+
 static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
 {
 	struct vop *vop = to_vop(crtc);
@@ -937,6 +956,9 @@ static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
 
 	spin_unlock_irqrestore(&vop->irq_lock, flags);
 
+	vop->psr_enabled = false;
+	schedule_delayed_work(&vop->psr_work, VOP_PSR_SET_DELAY_TIME);
+
 	return 0;
 }
 
@@ -953,6 +975,9 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc)
 	VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0);
 
 	spin_unlock_irqrestore(&vop->irq_lock, flags);
+
+	vop->psr_enabled = true;
+	schedule_delayed_work(&vop->psr_work, VOP_PSR_SET_DELAY_TIME);
 }
 
 static void vop_crtc_wait_for_update(struct drm_crtc *crtc)
@@ -1597,6 +1622,10 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
 		return ret;
 
 	pm_runtime_enable(&pdev->dev);
+
+	vop->psr_enabled = false;
+	INIT_DELAYED_WORK(&vop->psr_work, vop_psr_work);
+
 	return 0;
 }
 
-- 
1.9.1




From ykk at rock-chips.com  Wed Jul 13 21:15:53 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Thu, 14 Jul 2016 12:15:53 +0800
Subject: [PATCH v4 3/4] drm/bridge: analogix_dp: add the PSR function support
In-Reply-To: <1468469704-12440-1-git-send-email-ykk@rock-chips.com>
References: <1468469704-12440-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1468469753-12679-1-git-send-email-ykk@rock-chips.com>

The full name of PSR is Panel Self Refresh, panel device could refresh
itself with the hardware framebuffer in panel, this would make lots of
sense to save the power consumption.

This patch have exported two symbols for platform driver to implement
the PSR function in hardware side:
- analogix_dp_active_psr()
- analogix_dp_inactive_psr()

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
Changes in v4:
- Downgrade the PSR version print message to debug level. (Sean)
- Return 'void' instead of 'int' in analogix_dp_enable_sink_psr(). (Sean)
- Delete the unused read dpcd operations in analogix_dp_enable_sink_psr(). (Sean)
- Delete the arbitrary usleep_range in analogix_dp_enable_psr_crc. (Sean).
- Clean up the hardcoded values in analogix_dp_send_psr_spd(). (Sean)
- Rename "active/inactive" to "enable/disable". (Sean, Dominik)
- Keep set the PSR_VID_CRC_FLUSH gate in analogix_dp_enable_psr_crc().

Changes in v3:
- split analogix_dp_enable_psr(), make it more clearly
    analogix_dp_detect_sink_psr()
    analogix_dp_enable_sink_psr()
- remove some nosie register setting comments

Changes in v2:
- introduce in v2, splite the common Analogix DP changes out

 drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 60 ++++++++++++++++++++++
 drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |  4 ++
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  | 49 ++++++++++++++++++
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h  | 28 ++++++++++
 include/drm/bridge/analogix_dp.h                   |  3 ++
 5 files changed, 144 insertions(+)

diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index 32715da..1fec91a 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -97,6 +97,62 @@ static int analogix_dp_detect_hpd(struct analogix_dp_device *dp)
 	return 0;
 }
 
+int analogix_dp_enable_psr(struct device *dev)
+{
+	struct analogix_dp_device *dp = dev_get_drvdata(dev);
+
+	if (!dp->psr_support)
+		return -EINVAL;
+
+	analogix_dp_send_psr_spd(dp, EDP_VSC_PSR_STATE_ACTIVE |
+				 EDP_VSC_PSR_CRC_VALUES_VALID);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(analogix_dp_enable_psr);
+
+int analogix_dp_disable_psr(struct device *dev)
+{
+	struct analogix_dp_device *dp = dev_get_drvdata(dev);
+
+	if (!dp->psr_support)
+		return -EINVAL;
+
+	analogix_dp_send_psr_spd(dp, 0);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(analogix_dp_disable_psr);
+
+static bool analogix_dp_detect_sink_psr(struct analogix_dp_device *dp)
+{
+	unsigned char psr_version;
+
+	analogix_dp_read_byte_from_dpcd(dp, DP_PSR_SUPPORT, &psr_version);
+	dev_dbg(dp->dev, "Panel PSR version : %x\n", psr_version);
+
+	return (psr_version & DP_PSR_IS_SUPPORTED) ? true : false;
+}
+
+static void analogix_dp_enable_sink_psr(struct analogix_dp_device *dp)
+{
+	unsigned char psr_en;
+
+	/* Disable psr function */
+	analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
+	psr_en &= ~DP_PSR_ENABLE;
+	analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
+
+	/* Main-Link transmitter remains active during PSR active states */
+	psr_en = DP_PSR_MAIN_LINK_ACTIVE | DP_PSR_CRC_VERIFICATION;
+	analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
+
+	/* Enable psr function */
+	psr_en = DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE |
+		 DP_PSR_CRC_VERIFICATION;
+	analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
+
+	analogix_dp_enable_psr_crc(dp);
+}
+
 static unsigned char analogix_dp_calc_edid_check_sum(unsigned char *edid_data)
 {
 	int i;
@@ -921,6 +977,10 @@ static void analogix_dp_commit(struct analogix_dp_device *dp)
 
 	/* Enable video */
 	analogix_dp_start_video(dp);
+
+	dp->psr_support = analogix_dp_detect_sink_psr(dp);
+	if (dp->psr_support)
+		analogix_dp_enable_sink_psr(dp);
 }
 
 int analogix_dp_get_modes(struct drm_connector *connector)
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
index b456380..6ca5dde 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
@@ -177,6 +177,7 @@ struct analogix_dp_device {
 	int			hpd_gpio;
 	bool                    force_hpd;
 	unsigned char           edid[EDID_BLOCK_LENGTH * 2];
+	bool			psr_support;
 
 	struct analogix_dp_plat_data *plat_data;
 };
@@ -278,4 +279,7 @@ int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp);
 void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp);
 void analogix_dp_enable_scrambling(struct analogix_dp_device *dp);
 void analogix_dp_disable_scrambling(struct analogix_dp_device *dp);
+void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp);
+void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, int db1);
+
 #endif /* _ANALOGIX_DP_CORE_H */
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
index 48030f0..26446ae 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
@@ -26,6 +26,9 @@
 #define COMMON_INT_MASK_4	(HOTPLUG_CHG | HPD_LOST | PLUG)
 #define INT_STA_MASK		INT_HPD
 
+static u32 psr_spd_hbx[] = { 0x00, 0x07, 0x02, 0x08 };
+static u32 psr_spd_pbx[] = { 0x00, 0x16, 0xCE, 0x5D };
+
 void analogix_dp_enable_video_mute(struct analogix_dp_device *dp, bool enable)
 {
 	u32 reg;
@@ -1322,3 +1325,49 @@ void analogix_dp_disable_scrambling(struct analogix_dp_device *dp)
 	reg |= SCRAMBLING_DISABLE;
 	writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
 }
+
+void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp)
+{
+	writel(PSR_VID_CRC_FLUSH | PSR_VID_CRC_ENABLE,
+	       dp->reg_base + ANALOGIX_DP_CRC_CON);
+}
+
+void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, int db1)
+{
+	unsigned int val;
+	unsigned int i;
+
+	/* don't send info frame */
+	val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+	val &= ~IF_EN;
+	writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+
+	/* configure single frame update mode */
+	writel(PSR_FRAME_UP_TYPE_BURST | PSR_CRC_SEL_HARDWARE,
+	       dp->reg_base + ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL);
+
+	/* configure VSC HB0~HB3 and PB0~PB3 */
+	for (i = 0; i < 4; i++)
+		writel(psr_spd_hbx[i], dp->reg_base + ANALOGIX_DP_SPD_HB + i*4);
+	for (i = 0; i < 4; i++)
+		writel(psr_spd_pbx[i], dp->reg_base + ANALOGIX_DP_SPD_PB + i*4);
+
+	/* configure DB0 / DB1 values */
+	writel(0x0, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB0);
+	writel(db1, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB1);
+
+	/* set reuse spd inforframe */
+	val = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
+	val |= REUSE_SPD_EN;
+	writel(val, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
+
+	/* mark info frame update */
+	val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+	val = (val | IF_UP) & ~IF_EN;
+	writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+
+	/* send info frame */
+	val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+	val |= IF_EN;
+	writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+}
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
index cdcc6c5..fd232b2 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
@@ -22,6 +22,8 @@
 #define ANALOGIX_DP_VIDEO_CTL_8			0x3C
 #define ANALOGIX_DP_VIDEO_CTL_10		0x44
 
+#define ANALOGIX_DP_SPDIF_AUDIO_CTL_0		0xD8
+
 #define ANALOGIX_DP_PLL_REG_1			0xfc
 #define ANALOGIX_DP_PLL_REG_2			0x9e4
 #define ANALOGIX_DP_PLL_REG_3			0x9e8
@@ -30,6 +32,15 @@
 
 #define ANALOGIX_DP_PD				0x12c
 
+#define ANALOGIX_DP_IF_TYPE			0x244
+#define ANALOGIX_DP_IF_PKT_DB1			0x254
+#define ANALOGIX_DP_IF_PKT_DB2			0x258
+#define ANALOGIX_DP_SPD_HB			0x2F8
+#define ANALOGIX_DP_SPD_PB			0x308
+#define ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL	0x318
+#define ANALOGIX_DP_VSC_SHADOW_DB0		0x31C
+#define ANALOGIX_DP_VSC_SHADOW_DB1		0x320
+
 #define ANALOGIX_DP_LANE_MAP			0x35C
 
 #define ANALOGIX_DP_ANALOG_CTL_1		0x370
@@ -103,6 +114,8 @@
 
 #define ANALOGIX_DP_SOC_GENERAL_CTL		0x800
 
+#define ANALOGIX_DP_CRC_CON			0x890
+
 /* ANALOGIX_DP_TX_SW_RESET */
 #define RESET_DP_TX				(0x1 << 0)
 
@@ -151,6 +164,7 @@
 #define VID_CHK_UPDATE_TYPE_SHIFT		(4)
 #define VID_CHK_UPDATE_TYPE_1			(0x1 << 4)
 #define VID_CHK_UPDATE_TYPE_0			(0x0 << 4)
+#define REUSE_SPD_EN				(0x1 << 3)
 
 /* ANALOGIX_DP_VIDEO_CTL_8 */
 #define VID_HRES_TH(x)				(((x) & 0xf) << 4)
@@ -167,6 +181,12 @@
 #define REF_CLK_27M				(0x0 << 0)
 #define REF_CLK_MASK				(0x1 << 0)
 
+/* ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL */
+#define PSR_FRAME_UP_TYPE_BURST			(0x1 << 0)
+#define PSR_FRAME_UP_TYPE_SINGLE		(0x0 << 0)
+#define PSR_CRC_SEL_HARDWARE			(0x1 << 1)
+#define PSR_CRC_SEL_MANUALLY			(0x0 << 1)
+
 /* ANALOGIX_DP_LANE_MAP */
 #define LANE3_MAP_LOGIC_LANE_0			(0x0 << 6)
 #define LANE3_MAP_LOGIC_LANE_1			(0x1 << 6)
@@ -376,4 +396,12 @@
 #define VIDEO_MODE_SLAVE_MODE			(0x1 << 0)
 #define VIDEO_MODE_MASTER_MODE			(0x0 << 0)
 
+/* ANALOGIX_DP_PKT_SEND_CTL */
+#define IF_UP					(0x1 << 4)
+#define IF_EN					(0x1 << 0)
+
+/* ANALOGIX_DP_CRC_CON */
+#define PSR_VID_CRC_FLUSH			(0x1 << 2)
+#define PSR_VID_CRC_ENABLE			(0x1 << 0)
+
 #endif /* _ANALOGIX_DP_REG_H */
diff --git a/include/drm/bridge/analogix_dp.h b/include/drm/bridge/analogix_dp.h
index 261b86d..9cd8838 100644
--- a/include/drm/bridge/analogix_dp.h
+++ b/include/drm/bridge/analogix_dp.h
@@ -38,6 +38,9 @@ struct analogix_dp_plat_data {
 			 struct drm_connector *);
 };
 
+int analogix_dp_enable_psr(struct device *dev);
+int analogix_dp_disable_psr(struct device *dev);
+
 int analogix_dp_resume(struct device *dev);
 int analogix_dp_suspend(struct device *dev);
 
-- 
1.9.1




From ykk at rock-chips.com  Wed Jul 13 21:15:58 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Thu, 14 Jul 2016 12:15:58 +0800
Subject: [PATCH v4 4/4] drm/rockchip: analogix_dp: implement PSR function
In-Reply-To: <1468469704-12440-1-git-send-email-ykk@rock-chips.com>
References: <1468469704-12440-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1468469758-12722-1-git-send-email-ykk@rock-chips.com>

Alway enable the PSR function for Rockchip analogix_dp driver. If panel
don't support PSR, then the core analogix_dp would ignore this setting.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
Changes in v4:
- Return 'void' instead of 'int' in analogix_dp_psr_set(). (Sean)
- Pull the 10ms delay time out into a #define. (Sean)
- Improved the code of analogix_dp_psr_work(). (Sean)
- Indented with spaces for new numbers in rockchip_dp_device struct. (St?phane, reviewed at Google gerrit)
    [https://chromium-review.googlesource.com/#/c/349085/33/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c at 83]

Changes in v3:
- split the common psr logic into a seperate driver, make this to a
  simple sub-psr device driver.

Changes in v2:
- remove vblank notify out (Daniel)
- create a psr_active() callback in vop data struct.

 drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 57 +++++++++++++++++++++++++
 1 file changed, 57 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
index e81e19a..aa916f4 100644
--- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
@@ -32,6 +32,7 @@
 #include <drm/bridge/analogix_dp.h>
 
 #include "rockchip_drm_drv.h"
+#include "rockchip_drm_psr.h"
 #include "rockchip_drm_vop.h"
 
 #define RK3288_GRF_SOC_CON6		0x25c
@@ -41,6 +42,9 @@
 
 #define HIWORD_UPDATE(val, mask)	(val | (mask) << 16)
 
+#define PSR_SET_DELAY_TIME		msecs_to_jiffies(10)
+#define PSR_WAIT_LINE_FLAG_TIMEOUT_MS	100
+
 #define to_dp(nm)	container_of(nm, struct rockchip_dp_device, nm)
 
 /**
@@ -68,11 +72,55 @@ struct rockchip_dp_device {
 	struct regmap            *grf;
 	struct reset_control     *rst;
 
+	struct delayed_work      psr_work;
+	unsigned int             psr_state;
+
 	const struct rockchip_dp_chip_data *data;
 
 	struct analogix_dp_plat_data plat_data;
 };
 
+static void analogix_dp_psr_set(struct drm_encoder *encoder, bool enabled)
+{
+	struct rockchip_dp_device *dp = to_dp(encoder);
+
+	dev_dbg(dp->dev, "%s PSR...\n", enabled ? "Entry" : "Exit");
+
+	if (enabled)
+		dp->psr_state = EDP_VSC_PSR_STATE_ACTIVE;
+	else
+		dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE;
+
+	schedule_delayed_work(&dp->psr_work, PSR_SET_DELAY_TIME);
+}
+
+static void analogix_dp_psr_work(struct work_struct *work)
+{
+	struct rockchip_dp_device *dp =
+				container_of(work, typeof(*dp), psr_work.work);
+	struct drm_crtc *crtc = dp->encoder.crtc;
+	int psr_state = dp->psr_state;
+	int vact_end;
+	int ret;
+
+	if (!crtc)
+		return;
+
+	vact_end = crtc->mode.vtotal - crtc->mode.vsync_start + crtc->mode.vdisplay;
+
+	ret = rockchip_drm_wait_line_flag(dp->encoder.crtc, vact_end,
+					  PSR_WAIT_LINE_FLAG_TIMEOUT_MS);
+	if (ret) {
+		dev_err(dp->dev, "line flag interrupt did not arrive\n");
+		return;
+	}
+
+	if (psr_state == EDP_VSC_PSR_STATE_ACTIVE)
+		analogix_dp_enable_psr(dp->dev);
+	else
+		analogix_dp_disable_psr(dp->dev);
+}
+
 static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
 {
 	reset_control_assert(dp->rst);
@@ -340,12 +388,21 @@ static int rockchip_dp_bind(struct device *dev, struct device *master,
 	dp->plat_data.power_off = rockchip_dp_powerdown;
 	dp->plat_data.get_modes = rockchip_dp_get_modes;
 
+	dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE;
+	INIT_DELAYED_WORK(&dp->psr_work, analogix_dp_psr_work);
+
+	rockchip_drm_psr_register(&dp->encoder, analogix_dp_psr_set);
+
 	return analogix_dp_bind(dev, dp->drm_dev, &dp->plat_data);
 }
 
 static void rockchip_dp_unbind(struct device *dev, struct device *master,
 			       void *data)
 {
+	struct rockchip_dp_device *dp = dev_get_drvdata(dev);
+
+	rockchip_drm_psr_unregister(&dp->encoder);
+
 	return analogix_dp_unbind(dev, master, data);
 }
 
-- 
1.9.1




From william.wu at rock-chips.com  Thu Jul 14 01:59:17 2016
From: william.wu at rock-chips.com (William Wu)
Date: Thu, 14 Jul 2016 16:59:17 +0800
Subject: [PATCH v7 0/5] support rockchip dwc3 driver
Message-ID: <1468486762-21960-1-git-send-email-william.wu@rock-chips.com>

This series add support for rockchip dwc3 driver,
and add additional optional properties for specific
platforms (e.g., rockchip rk3399 platform).

William Wu (5):
  usb: dwc3: of-simple: add compatible for rockchip rk3399
  usb: dwc3: add dis_u2_freeclk_exists_quirk
  usb: dwc3: make usb2 phy utmi interface configurable in DT
  usb: dwc3: add dis_del_phy_power_chg_quirk
  usb: dwc3: rockchip: add devicetree bindings documentation

 Documentation/devicetree/bindings/usb/dwc3.txt     |  8 +++
 .../devicetree/bindings/usb/rockchip,dwc3.txt      | 59 ++++++++++++++++++++++
 drivers/usb/dwc3/core.c                            | 35 +++++++++++++
 drivers/usb/dwc3/core.h                            | 18 +++++++
 drivers/usb/dwc3/dwc3-of-simple.c                  |  1 +
 5 files changed, 121 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/usb/rockchip,dwc3.txt

-- 
1.9.1




From william.wu at rock-chips.com  Thu Jul 14 01:59:18 2016
From: william.wu at rock-chips.com (William Wu)
Date: Thu, 14 Jul 2016 16:59:18 +0800
Subject: [PATCH v7 1/5] usb: dwc3: of-simple: add compatible for rockchip
 rk3399
In-Reply-To: <1468486762-21960-1-git-send-email-william.wu@rock-chips.com>
References: <1468486762-21960-1-git-send-email-william.wu@rock-chips.com>
Message-ID: <1468486762-21960-2-git-send-email-william.wu@rock-chips.com>

Rockchip platform merely enable usb3 clocks and
populate its children. So we can use this generic
glue layer to support Rockchip dwc3.

Signed-off-by: William Wu <william.wu at rock-chips.com>
---
Changes in v7:
- None

Changes in v6:
- None

Changes in v5:
- change compatible from "rockchip,dwc3" to "rockchip,rk3399-dwc3" (Heiko)

Changes in v4:
- None

Changes in v3:
- None

Changes in v2:
- sort the list of_dwc3_simple_match (Doug)

 drivers/usb/dwc3/dwc3-of-simple.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c
index 9743353..05c9349 100644
--- a/drivers/usb/dwc3/dwc3-of-simple.c
+++ b/drivers/usb/dwc3/dwc3-of-simple.c
@@ -161,6 +161,7 @@ static const struct dev_pm_ops dwc3_of_simple_dev_pm_ops = {
 
 static const struct of_device_id of_dwc3_simple_match[] = {
 	{ .compatible = "qcom,dwc3" },
+	{ .compatible = "rockchip,rk3399-dwc3" },
 	{ .compatible = "xlnx,zynqmp-dwc3" },
 	{ /* Sentinel */ }
 };
-- 
1.9.1




From william.wu at rock-chips.com  Thu Jul 14 01:59:19 2016
From: william.wu at rock-chips.com (William Wu)
Date: Thu, 14 Jul 2016 16:59:19 +0800
Subject: [PATCH v7 2/5] usb: dwc3: add dis_u2_freeclk_exists_quirk
In-Reply-To: <1468486762-21960-1-git-send-email-william.wu@rock-chips.com>
References: <1468486762-21960-1-git-send-email-william.wu@rock-chips.com>
Message-ID: <1468486762-21960-3-git-send-email-william.wu@rock-chips.com>

Add a quirk to clear the GUSB2PHYCFG.U2_FREECLK_EXISTS bit,
which specifies whether the USB2.0 PHY provides a free-running
PHY clock, which is active when the clock control input is active.

Signed-off-by: William Wu <william.wu at rock-chips.com>
---
Changes in v7:
- None

Changes in v6:
- use '-' instead of '_' in dts (Rob Herring)

Changes in v5:
- None

Changes in v4:
- rebase on top of balbi testing/next, remove pdata (balbi)

Changes in v3:
- None

Changes in v2:
- None

 Documentation/devicetree/bindings/usb/dwc3.txt | 3 +++
 drivers/usb/dwc3/core.c                        | 5 +++++
 drivers/usb/dwc3/core.h                        | 5 +++++
 3 files changed, 13 insertions(+)

diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt
index 7d7ce08..020b0e9 100644
--- a/Documentation/devicetree/bindings/usb/dwc3.txt
+++ b/Documentation/devicetree/bindings/usb/dwc3.txt
@@ -39,6 +39,9 @@ Optional properties:
 			disabling the suspend signal to the PHY.
  - snps,dis_rxdet_inp3_quirk: when set core will disable receiver detection
 			in PHY P3 power state.
+ - snps,dis-u2-freeclk-exists-quirk: when set, clear the u2_freeclk_exists
+			in GUSB2PHYCFG, specify that USB2 PHY doesn't provide
+			a free-running PHY clock.
  - snps,is-utmi-l1-suspend: true when DWC3 asserts output signal
 			utmi_l1_suspend_n, false when asserts utmi_sleep_n
  - snps,hird-threshold: HIRD threshold
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 9466431..0b7bfd2 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -500,6 +500,9 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
 	if (dwc->dis_enblslpm_quirk)
 		reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
 
+	if (dwc->dis_u2_freeclk_exists_quirk)
+		reg &= ~DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS;
+
 	dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
 
 	return 0;
@@ -924,6 +927,8 @@ static int dwc3_probe(struct platform_device *pdev)
 				"snps,dis_enblslpm_quirk");
 	dwc->dis_rxdet_inp3_quirk = device_property_read_bool(dev,
 				"snps,dis_rxdet_inp3_quirk");
+	dwc->dis_u2_freeclk_exists_quirk = device_property_read_bool(dev,
+				"snps,dis-u2-freeclk-exists-quirk");
 
 	dwc->tx_de_emphasis_quirk = device_property_read_bool(dev,
 				"snps,tx_de_emphasis_quirk");
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 45d6de5..f321a5c 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -199,6 +199,7 @@
 
 /* Global USB2 PHY Configuration Register */
 #define DWC3_GUSB2PHYCFG_PHYSOFTRST	(1 << 31)
+#define DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS	(1 << 30)
 #define DWC3_GUSB2PHYCFG_SUSPHY		(1 << 6)
 #define DWC3_GUSB2PHYCFG_ULPI_UTMI	(1 << 4)
 #define DWC3_GUSB2PHYCFG_ENBLSLPM	(1 << 8)
@@ -799,6 +800,9 @@ struct dwc3_scratchpad_array {
  * @dis_u2_susphy_quirk: set if we disable usb2 suspend phy
  * @dis_enblslpm_quirk: set if we clear enblslpm in GUSB2PHYCFG,
  *                      disabling the suspend signal to the PHY.
+ * @dis_u2_freeclk_exists_quirk : set if we clear u2_freeclk_exists
+ *			in GUSB2PHYCFG, specify that USB2 PHY doesn't
+ *			provide a free-running PHY clock.
  * @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk
  * @tx_de_emphasis: Tx de-emphasis value
  * 	0	- -6dB de-emphasis
@@ -942,6 +946,7 @@ struct dwc3 {
 	unsigned		dis_u2_susphy_quirk:1;
 	unsigned		dis_enblslpm_quirk:1;
 	unsigned		dis_rxdet_inp3_quirk:1;
+	unsigned		dis_u2_freeclk_exists_quirk:1;
 
 	unsigned		tx_de_emphasis_quirk:1;
 	unsigned		tx_de_emphasis:2;
-- 
1.9.1




From william.wu at rock-chips.com  Thu Jul 14 01:59:20 2016
From: william.wu at rock-chips.com (William Wu)
Date: Thu, 14 Jul 2016 16:59:20 +0800
Subject: [PATCH v7 3/5] usb: dwc3: make usb2 phy utmi interface configurable
 in DT
In-Reply-To: <1468486762-21960-1-git-send-email-william.wu@rock-chips.com>
References: <1468486762-21960-1-git-send-email-william.wu@rock-chips.com>
Message-ID: <1468486762-21960-4-git-send-email-william.wu@rock-chips.com>

Add snps,phyif-utmi-width devicetree property to configure
the UTMI+ PHY with an 8- or 16-bit interface. UTMI+ PHY
interface is a hardware property, and it's platform dependent.
Normally,the PHYIF can be configured during coreconsultant.
But for some specific USB cores(e.g. rk3399 SoC DWC3), the
default PHYIF configuration value is fault, so we need to
reconfigure it by software.

And refer to the DWC3 databook, the GUSB2PHYCFG.USBTRDTIM
must be set to the corresponding value according to the
UTMI+ PHY interface.

Signed-off-by: William Wu <william.wu at rock-chips.com>
---
Changes in v7:
- remove quirk and use only one property to configure utmi (Heiko, Rob Herring)

Changes in v6:
- use '-' instead of '_' in dts (Rob Herring)

Changes in v5:
- None

Changes in v4:
- rebase on top of balbi testing/next, remove pdata (balbi)

Changes in v3:
- None

Changes in v2:
- add a quirk for phyif_utmi (balbi)

 Documentation/devicetree/bindings/usb/dwc3.txt |  3 +++
 drivers/usb/dwc3/core.c                        | 25 +++++++++++++++++++++++++
 drivers/usb/dwc3/core.h                        | 10 ++++++++++
 3 files changed, 38 insertions(+)

diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt
index 020b0e9..00cc541 100644
--- a/Documentation/devicetree/bindings/usb/dwc3.txt
+++ b/Documentation/devicetree/bindings/usb/dwc3.txt
@@ -47,6 +47,9 @@ Optional properties:
  - snps,hird-threshold: HIRD threshold
  - snps,hsphy_interface: High-Speed PHY interface selection between "utmi" for
    UTMI+ and "ulpi" for ULPI when the DWC_USB3_HSPHY_INTERFACE has value 3.
+ - snps,phyif-utmi-width: the value to configure the core to support a UTMI+ PHY
+			with an 8- or 16-bit interface. Value 8 select 8-bit
+			interface, value 16 select 16-bit interface.
  - snps,quirk-frame-length-adjustment: Value for GFLADJ_30MHZ field of GFLADJ
 	register for post-silicon frame length adjustment when the
 	fladj_30mhz_sdbnd signal is invalid or incorrect.
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 0b7bfd2..40c54db 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -408,6 +408,8 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
 static int dwc3_phy_setup(struct dwc3 *dwc)
 {
 	u32 reg;
+	u32 usbtrdtim;
+	u8 phyif;
 	int ret;
 
 	reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
@@ -503,6 +505,16 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
 	if (dwc->dis_u2_freeclk_exists_quirk)
 		reg &= ~DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS;
 
+	if (dwc->phyif_utmi_width > 0) {
+		reg &= ~(DWC3_GUSB2PHYCFG_PHYIF_MASK |
+		       DWC3_GUSB2PHYCFG_USBTRDTIM_MASK);
+		usbtrdtim = (dwc->phyif_utmi_width == 16) ?
+			    USBTRDTIM_UTMI_16_BIT : USBTRDTIM_UTMI_8_BIT;
+		phyif = (dwc->phyif_utmi_width == 16) ? 1 : 0;
+		reg |= DWC3_GUSB2PHYCFG_PHYIF(phyif) |
+		       DWC3_GUSB2PHYCFG_USBTRDTIM(usbtrdtim);
+	}
+
 	dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
 
 	return 0;
@@ -900,6 +912,19 @@ static int dwc3_probe(struct platform_device *pdev)
 				"snps,is-utmi-l1-suspend");
 	device_property_read_u8(dev, "snps,hird-threshold",
 				&hird_threshold);
+
+	ret = device_property_read_u8(dev, "snps,phyif-utmi-width",
+				      &dwc->phyif_utmi_width);
+	if (ret < 0) {
+		dwc->phyif_utmi_width = 0;
+	} else if (dwc->phyif_utmi_width != 16 &&
+		dwc->phyif_utmi_width != 8) {
+		dev_err(dev, "unsupported utmi interface width %d\n",
+			dwc->phyif_utmi_width);
+		ret = -EINVAL;
+		goto err0;
+	}
+
 	dwc->usb3_lpm_capable = device_property_read_bool(dev,
 				"snps,usb3_lpm_capable");
 
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index f321a5c..99a72c7 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -203,6 +203,12 @@
 #define DWC3_GUSB2PHYCFG_SUSPHY		(1 << 6)
 #define DWC3_GUSB2PHYCFG_ULPI_UTMI	(1 << 4)
 #define DWC3_GUSB2PHYCFG_ENBLSLPM	(1 << 8)
+#define DWC3_GUSB2PHYCFG_PHYIF(n)	(n << 3)
+#define DWC3_GUSB2PHYCFG_PHYIF_MASK	DWC3_GUSB2PHYCFG_PHYIF(1)
+#define DWC3_GUSB2PHYCFG_USBTRDTIM(n)	(n << 10)
+#define DWC3_GUSB2PHYCFG_USBTRDTIM_MASK	DWC3_GUSB2PHYCFG_USBTRDTIM(0xf)
+#define USBTRDTIM_UTMI_8_BIT		9
+#define USBTRDTIM_UTMI_16_BIT		5
 
 /* Global USB2 PHY Vendor Control Register */
 #define DWC3_GUSB2PHYACC_NEWREGREQ	(1 << 25)
@@ -770,6 +776,9 @@ struct dwc3_scratchpad_array {
  * @test_mode_nr: test feature selector
  * @lpm_nyet_threshold: LPM NYET response threshold
  * @hird_threshold: HIRD threshold
+ * @phyif_utmi_width: UTMI+ PHY interface width value
+ *	8	- 8 bits
+ *	16	- 16 bits
  * @hsphy_interface: "utmi" or "ulpi"
  * @connected: true when we're connected to a host, false otherwise
  * @delayed_status: true when gadget driver asks for delayed status
@@ -917,6 +926,7 @@ struct dwc3 {
 	u8			test_mode_nr;
 	u8			lpm_nyet_threshold;
 	u8			hird_threshold;
+	u8			phyif_utmi_width;
 
 	const char		*hsphy_interface;
 
-- 
1.9.1




From william.wu at rock-chips.com  Thu Jul 14 01:59:21 2016
From: william.wu at rock-chips.com (William Wu)
Date: Thu, 14 Jul 2016 16:59:21 +0800
Subject: [PATCH v7 4/5] usb: dwc3: add dis_del_phy_power_chg_quirk
In-Reply-To: <1468486762-21960-1-git-send-email-william.wu@rock-chips.com>
References: <1468486762-21960-1-git-send-email-william.wu@rock-chips.com>
Message-ID: <1468486762-21960-5-git-send-email-william.wu@rock-chips.com>

Add a quirk to clear the GUSB3PIPECTL.DELAYP1TRANS bit,
which specifies whether disable delay PHY power change
from P0 to P1/P2/P3 when link state changing from U0
to U1/U2/U3 respectively.

Signed-off-by: William Wu <william.wu at rock-chips.com>
---
Changes in v7:
- None

Changes in v6:
- use '-' instead of '_' in dts (Rob Herring)

Changes in v5:
- None

Changes in v4:
- rebase on top of balbi testing/next, remove pdata (balbi)

Changes in v3:
- None

Changes in v2:
- None

 Documentation/devicetree/bindings/usb/dwc3.txt | 2 ++
 drivers/usb/dwc3/core.c                        | 5 +++++
 drivers/usb/dwc3/core.h                        | 3 +++
 3 files changed, 10 insertions(+)

diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt
index 00cc541..7832e19 100644
--- a/Documentation/devicetree/bindings/usb/dwc3.txt
+++ b/Documentation/devicetree/bindings/usb/dwc3.txt
@@ -42,6 +42,8 @@ Optional properties:
  - snps,dis-u2-freeclk-exists-quirk: when set, clear the u2_freeclk_exists
 			in GUSB2PHYCFG, specify that USB2 PHY doesn't provide
 			a free-running PHY clock.
+ - snps,dis-del-phy-power-chg-quirk: when set core will change PHY power
+			from P0 to P1/P2/P3 without delay.
  - snps,is-utmi-l1-suspend: true when DWC3 asserts output signal
 			utmi_l1_suspend_n, false when asserts utmi_sleep_n
  - snps,hird-threshold: HIRD threshold
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 40c54db..bff5ae4 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -450,6 +450,9 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
 	if (dwc->dis_u3_susphy_quirk)
 		reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
 
+	if (dwc->dis_del_phy_power_chg_quirk)
+		reg &= ~DWC3_GUSB3PIPECTL_DEPOCHANGE;
+
 	dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
 
 	reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
@@ -954,6 +957,8 @@ static int dwc3_probe(struct platform_device *pdev)
 				"snps,dis_rxdet_inp3_quirk");
 	dwc->dis_u2_freeclk_exists_quirk = device_property_read_bool(dev,
 				"snps,dis-u2-freeclk-exists-quirk");
+	dwc->dis_del_phy_power_chg_quirk = device_property_read_bool(dev,
+				"snps,dis-del-phy-power-chg-quirk");
 
 	dwc->tx_de_emphasis_quirk = device_property_read_bool(dev,
 				"snps,tx_de_emphasis_quirk");
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 99a72c7..753d977a 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -812,6 +812,8 @@ struct dwc3_scratchpad_array {
  * @dis_u2_freeclk_exists_quirk : set if we clear u2_freeclk_exists
  *			in GUSB2PHYCFG, specify that USB2 PHY doesn't
  *			provide a free-running PHY clock.
+ * @dis_del_phy_power_chg_quirk: set if we disable delay phy power
+ *			change quirk.
  * @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk
  * @tx_de_emphasis: Tx de-emphasis value
  * 	0	- -6dB de-emphasis
@@ -957,6 +959,7 @@ struct dwc3 {
 	unsigned		dis_enblslpm_quirk:1;
 	unsigned		dis_rxdet_inp3_quirk:1;
 	unsigned		dis_u2_freeclk_exists_quirk:1;
+	unsigned		dis_del_phy_power_chg_quirk:1;
 
 	unsigned		tx_de_emphasis_quirk:1;
 	unsigned		tx_de_emphasis:2;
-- 
1.9.1




From william.wu at rock-chips.com  Thu Jul 14 02:02:06 2016
From: william.wu at rock-chips.com (William Wu)
Date: Thu, 14 Jul 2016 17:02:06 +0800
Subject: [PATCH v7 5/5] usb: dwc3: rockchip: add devicetree bindings
 documentation
In-Reply-To: <1468486762-21960-1-git-send-email-william.wu@rock-chips.com>
References: <1468486762-21960-1-git-send-email-william.wu@rock-chips.com>
Message-ID: <1468486926-22037-1-git-send-email-william.wu@rock-chips.com>

This patch adds the devicetree documentation required for Rockchip
USB3.0 core wrapper consisting of USB3.0 IP from Synopsys.

It supports DRD mode, and could operate in device mode (SS, HS, FS)
and host mode (SS, HS, FS, LS).

Signed-off-by: William Wu <william.wu at rock-chips.com>
Acked-by: Rob Herring <robh at kernel.org>
---
Changes in v7:
- add Acked-by (Rob Herring)

Changes in v6:
- rename bus_clk, and add usbdrd3_1 node as an example (Heiko)

Changes in v5:
- rename clock-names, and remove unnecessary clocks (Heiko)

Changes in v4:
- modify commit log, and add phy documentation location (Sergei)

Changes in v3:
- add dwc3 address (balbi)

Changes in v2:
- add rockchip,dwc3.txt to Documentation/devicetree/bindings/ (balbi, Brian)

 .../devicetree/bindings/usb/rockchip,dwc3.txt      | 59 ++++++++++++++++++++++
 1 file changed, 59 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/usb/rockchip,dwc3.txt

diff --git a/Documentation/devicetree/bindings/usb/rockchip,dwc3.txt b/Documentation/devicetree/bindings/usb/rockchip,dwc3.txt
new file mode 100644
index 0000000..0536a93
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/rockchip,dwc3.txt
@@ -0,0 +1,59 @@
+Rockchip SuperSpeed DWC3 USB SoC controller
+
+Required properties:
+- compatible:	should contain "rockchip,rk3399-dwc3" for rk3399 SoC
+- clocks:	A list of phandle + clock-specifier pairs for the
+		clocks listed in clock-names
+- clock-names:	Should contain the following:
+  "ref_clk"	Controller reference clk, have to be 24 MHz
+  "suspend_clk"	Controller suspend clk, have to be 24 MHz or 32 KHz
+  "bus_clk"	Master/Core clock, have to be >= 62.5 MHz for SS
+		operation and >= 30MHz for HS operation
+  "grf_clk"	Controller grf clk
+
+Required child node:
+A child node must exist to represent the core DWC3 IP block. The name of
+the node is not important. The content of the node is defined in dwc3.txt.
+
+Phy documentation is provided in the following places:
+Documentation/devicetree/bindings/phy/rockchip,dwc3-usb-phy.txt
+
+Example device nodes:
+
+	usbdrd3_0: usb at fe800000 {
+		compatible = "rockchip,rk3399-dwc3";
+		clocks = <&cru SCLK_USB3OTG0_REF>, <&cru SCLK_USB3OTG0_SUSPEND>,
+			 <&cru ACLK_USB3OTG0>, <&cru ACLK_USB3_GRF>;
+		clock-names = "ref_clk", "suspend_clk",
+			      "bus_clk", "grf_clk";
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+		status = "disabled";
+		usbdrd_dwc3_0: dwc3 at fe800000 {
+			compatible = "snps,dwc3";
+			reg = <0x0 0xfe800000 0x0 0x100000>;
+			interrupts = <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>;
+			dr_mode = "otg";
+			status = "disabled";
+		};
+	};
+
+	usbdrd3_1: usb at fe900000 {
+		compatible = "rockchip,rk3399-dwc3";
+		clocks = <&cru SCLK_USB3OTG1_REF>, <&cru SCLK_USB3OTG1_SUSPEND>,
+			 <&cru ACLK_USB3OTG1>, <&cru ACLK_USB3_GRF>;
+		clock-names = "ref_clk", "suspend_clk",
+			      "bus_clk", "grf_clk";
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+		status = "disabled";
+		usbdrd_dwc3_1: dwc3 at fe900000 {
+			compatible = "snps,dwc3";
+			reg = <0x0 0xfe900000 0x0 0x100000>;
+			interrupts = <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH>;
+			dr_mode = "otg";
+			status = "disabled";
+		};
+	};
-- 
1.9.1




From w.egorov at phytec.de  Thu Jul 14 04:42:37 2016
From: w.egorov at phytec.de (Wadim Egorov)
Date: Thu, 14 Jul 2016 13:42:37 +0200
Subject: [PATCH] mfd: RK808: Fetch PMIC variant from chip id register
Message-ID: <1468496557-26309-1-git-send-email-w.egorov@phytec.de>

Add and use the chip id registers to determine the PMIC variant.

Signed-off-by: Wadim Egorov <w.egorov at phytec.de>
---

This patch is based on top of

[v6,1/5] mfd: RK808: Add RK818 support
(https://patchwork.kernel.org/patch/9172223/)

---
 drivers/mfd/rk808.c       | 17 +++++++++--------
 include/linux/mfd/rk808.h |  6 ++++--
 2 files changed, 13 insertions(+), 10 deletions(-)

diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c
index 66f7b08..fbec1d8 100644
--- a/drivers/mfd/rk808.c
+++ b/drivers/mfd/rk808.c
@@ -291,8 +291,8 @@ static void rk808_device_shutdown(void)
 }
 
 static const struct of_device_id rk808_of_match[] = {
-	{ .compatible = "rockchip,rk808", .data = (void *) RK808_ID },
-	{ .compatible = "rockchip,rk818", .data = (void *) RK818_ID },
+	{ .compatible = "rockchip,rk808" },
+	{ .compatible = "rockchip,rk818" },
 	{ },
 };
 MODULE_DEVICE_TABLE(of, rk808_of_match);
@@ -302,7 +302,6 @@ static int rk808_probe(struct i2c_client *client,
 {
 	struct device_node *np = client->dev.of_node;
 	struct rk808 *rk808;
-	const struct of_device_id *match;
 	const struct rk808_reg_data *pre_init_reg;
 	const struct mfd_cell *cells;
 	int nr_pre_init_regs;
@@ -315,12 +314,14 @@ static int rk808_probe(struct i2c_client *client,
 	if (!rk808)
 		return -ENOMEM;
 
-	match = of_match_device(rk808_of_match, &client->dev);
-	if (!match) {
-		dev_err(&client->dev, "Unable to match OF ID\n");
-		return -ENODEV;
+	rk808->variant = i2c_smbus_read_word_data(client, RK808_ID_MSB);
+	if (rk808->variant < 0) {
+		dev_err(&client->dev, "Failed to read the chip id at 0x%02x\n",
+			RK808_ID_MSB);
+		return rk808->variant;
 	}
-	rk808->variant = (long)match->data;
+
+	dev_dbg(&client->dev, "Chip id: 0x%x\n", (unsigned int)rk808->variant);
 
 	switch (rk808->variant) {
 	case RK808_ID:
diff --git a/include/linux/mfd/rk808.h b/include/linux/mfd/rk808.h
index e5ae21d..fc5db6f 100644
--- a/include/linux/mfd/rk808.h
+++ b/include/linux/mfd/rk808.h
@@ -69,6 +69,8 @@ enum rk808_reg {
 #define RK808_RTC_INT_REG	0x12
 #define RK808_RTC_COMP_LSB_REG	0x13
 #define RK808_RTC_COMP_MSB_REG	0x14
+#define RK808_ID_MSB		0x17
+#define RK808_ID_LSB		0x18
 #define RK808_CLK32OUT_REG	0x20
 #define RK808_VB_MON_REG	0x21
 #define RK808_THERMAL_REG	0x22
@@ -318,8 +320,8 @@ enum {
 };
 
 enum {
-	RK808_ID,
-	RK818_ID,
+	RK808_ID = 0x0000,
+	RK818_ID = 0x8181,
 };
 
 struct rk808 {
-- 
1.9.1



From matthias.bgg at gmail.com  Thu Jul 14 05:28:22 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Thu, 14 Jul 2016 14:28:22 +0200
Subject: [PATCH] arm64: dts: rockchip: Add basic support for orion-r68
Message-ID: <1468499302-16508-1-git-send-email-matthias.bgg@gmail.com>

This patch adds basic support for the Tronsmart orion r86 set-top-box.

Signed-off-by: Matthias Brugger <mbrugger at suse.com>
---
Changes since v1:
- change memory node to 2GB
- change compatible and file name
- add uart4
- add second led

 Documentation/devicetree/bindings/arm/rockchip.txt |   4 +
 arch/arm64/boot/dts/rockchip/Makefile              |   1 +
 .../boot/dts/rockchip/rk3368-orion-r68-meta.dts    | 382 +++++++++++++++++++++
 3 files changed, 387 insertions(+)
 create mode 100644 arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts

diff --git a/Documentation/devicetree/bindings/arm/rockchip.txt b/Documentation/devicetree/bindings/arm/rockchip.txt
index 715d960..1d5c65e0 100644
--- a/Documentation/devicetree/bindings/arm/rockchip.txt
+++ b/Documentation/devicetree/bindings/arm/rockchip.txt
@@ -110,3 +110,7 @@ Rockchip platforms device tree bindings
 - Rockchip RK3399 evb:
     Required root node properties:
       - compatible = "rockchip,rk3399-evb", "rockchip,rk3399";
+
+- Tronsmart Orion R68 Meta
+    Required root node properties:
+      - compatible = "tronsmart,orion-r68-meta", "rockchip,rk3368";
diff --git a/arch/arm64/boot/dts/rockchip/Makefile b/arch/arm64/boot/dts/rockchip/Makefile
index 7037a16..87669f6 100644
--- a/arch/arm64/boot/dts/rockchip/Makefile
+++ b/arch/arm64/boot/dts/rockchip/Makefile
@@ -1,5 +1,6 @@
 dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-evb-act8846.dtb
 dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-geekbox.dtb
+dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-orion-r68-meta.dtb
 dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-r88.dtb
 dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-evb.dtb
 
diff --git a/arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts b/arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts
new file mode 100644
index 0000000..8c8671f
--- /dev/null
+++ b/arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts
@@ -0,0 +1,382 @@
+/*
+ * Copyright (c) 2016 Matthias Brugger <mbrugger at suse.com>
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of the
+ *     License, or (at your option) any later version.
+ *
+ *     This file 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.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/dts-v1/;
+#include "rk3368.dtsi"
+
+/ {
+	model = "Rockchip Orion R68";
+	compatible = "tronsmart,orion-r68-meta", "rockchip,rk3368";
+
+	chosen {
+		stdout-path = "serial2:115200n8";
+	};
+
+	memory {
+		device_type = "memory";
+		reg = <0x0 0x0 0x0 0x80000000>;
+	};
+
+	emmc_pwrseq: emmc-pwrseq {
+		compatible = "mmc-pwrseq-emmc";
+		pinctrl-0 = <&emmc_reset>;
+		pinctrl-names = "default";
+		reset-gpios = <&gpio2 3 GPIO_ACTIVE_HIGH>;
+	};
+
+	ext_gmac: external-gmac-clock {
+		compatible = "fixed-clock";
+		#clock-cells = <0>;
+		clock-frequency = <125000000>;
+		clock-output-names = "ext_gmac";
+	};
+
+	keys: gpio-keys {
+		compatible = "gpio-keys";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pwr_key>;
+
+		button at 0 {
+			gpio-key,wakeup = <1>;
+			gpios = <&gpio0 5 GPIO_ACTIVE_HIGH>;
+			label = "GPIO Power";
+			linux,code = <116>;
+		};
+	};
+
+	leds: gpio-leds {
+		compatible = "gpio-leds";
+
+		red {
+			gpios = <&gpio3 29 GPIO_ACTIVE_HIGH>;
+			label = "orion:red:led";
+			pinctrl-names = "default";
+			pinctrl-0 = <&led_ctl>;
+			default-state = "on";
+		};
+
+		blue {
+			gpios = <&gpio0 12 GPIO_ACTIVE_HIGH>;
+			label = "orion:blue:led";
+			pinctrl-names = "default";
+			pinctrl-0 = <&stby_pwren>;
+			default-state = "off";
+		};
+	};
+
+	vcc_18: vcc18-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_18";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+		regulator-always-on;
+		regulator-boot-on;
+		vin-supply = <&vcc_sys>;
+	};
+
+	/* supplies both host and otg */
+	vcc_host: vcc-host-regulator {
+		compatible = "regulator-fixed";
+		gpio = <&gpio0 4 GPIO_ACTIVE_LOW>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&host_vbus_drv>;
+		regulator-name = "vcc_host";
+		regulator-always-on;
+		regulator-boot-on;
+		vin-supply = <&vcc_sys>;
+	};
+
+	vccio_sd: vcc-io-sd-regulator {
+		regulator-name= "vccio_sd";
+		gpio = <&gpio0 9 GPIO_ACTIVE_LOW>;
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+	};
+
+	vcc_sd: vcc-sd-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_sd";
+		gpio = <&gpio3 11 GPIO_ACTIVE_LOW>;
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-always-on;
+		regulator-boot-on;
+		vin-supply = <&vcc_io>;
+	};
+
+	vcc_io: vcc-io-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_io";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-always-on;
+		regulator-boot-on;
+		vin-supply = <&vcc_sys>;
+	};
+
+	vcc_lan: vcc-lan-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_lan";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-always-on;
+		regulator-boot-on;
+		vin-supply = <&vcc_io>;
+	};
+
+	vcc_sys: vcc-sys-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_sys";
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+		regulator-always-on;
+		regulator-boot-on;
+	};
+
+	vccio_wl: vccio-wl-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vccio_wl";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-always-on;
+		regulator-boot-on;
+		vin-supply = <&vcc_io>;
+	};
+
+	vdd_10: vdd-10-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vdd_10";
+		regulator-min-microvolt = <1000000>;
+		regulator-max-microvolt = <1000000>;
+		regulator-always-on;
+		regulator-boot-on;
+		vin-supply = <&vcc_sys>;
+	};
+};
+
+&emmc {
+	bus-width = <8>;
+	cap-mmc-highspeed;
+	disable-wp;
+	mmc-pwrseq = <&emmc_pwrseq>;
+	non-removable;
+	num-slots = <1>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&emmc_clk &emmc_cmd &emmc_bus8>;
+	status = "okay";
+};
+
+&gmac {
+	assigned-clocks = <&cru SCLK_MAC>;
+	assigned-clock-parents = <&ext_gmac>;
+	phy-supply = <&vcc_lan>;
+	phy-mode = "rgmii";
+	clock_in_out = "input";
+	snps,reset-gpio = <&gpio3 12 0>;
+	snps,reset-active-low;
+	snps,reset-delays-us = <0 10000 1000000>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&rgmii_pins>;
+	tx_delay = <0x30>;
+	rx_delay = <0x10>;
+	status = "ok";
+};
+
+&i2c0 {
+	status = "okay";
+
+	vdd_cpu: syr827 at 40 {
+		compatible = "silergy,syr827";
+		reg = <0x40>;
+		fcs,suspend-voltage-selector = <1>;
+		regulator-name = "vdd_cpu";
+		regulator-enable-ramp-delay = <300>;
+		regulator-min-microvolt = <712500>;
+		regulator-max-microvolt = <1500000>;
+		regulator-ramp-delay = <8000>;
+		regulator-always-on;
+		regulator-boot-on;
+		vin-supply = <&vcc_sys>;
+	};
+
+	hym8563: hym8563 at 51 {
+		compatible = "haoyu,hym8563";
+		reg = <0x51>;
+		#clock-cells = <0>;
+		clock-frequency = <32768>;
+		clock-output-names = "xin32k";
+		/* rtc_int is not connected */
+	};
+};
+
+&pinctrl {
+	pcfg_pull_none_drv_8ma: pcfg-pull-none-drv-8ma {
+		bias-disable;
+		drive-strength = <8>;
+	};
+
+	pcfg_pull_up_drv_8ma: pcfg-pull-up-drv-8ma {
+		bias-pull-up;
+		drive-strength = <8>;
+	};
+
+	emmc {
+		emmc_bus8: emmc-bus8 {
+			rockchip,pins = <1 18 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
+					<1 19 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
+					<1 20 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
+					<1 21 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
+					<1 22 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
+					<1 23 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
+					<1 24 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
+					<1 25 RK_FUNC_2 &pcfg_pull_up_drv_8ma>;
+		};
+
+		emmc-clk {
+			rockchip,pins = <2 4 RK_FUNC_2 &pcfg_pull_none_drv_8ma>;
+		};
+
+		emmc-cmd {
+			rockchip,pins = <1 26 RK_FUNC_2 &pcfg_pull_up_drv_8ma>;
+		};
+
+		emmc_reset: emmc-reset {
+			rockchip,pins = <2 3 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+
+	keys {
+		pwr_key: pwr-key {
+			rockchip,pins = <0 5 RK_FUNC_GPIO &pcfg_pull_down>;
+		};
+	};
+
+	leds {
+		stby_pwren: stby-pwren {
+			rockchip,pins = <0 12 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+
+		led_ctl: led-ctl {
+			rockchip,pins = <3 29 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+
+	sdmmc {
+		sdmmc_clk: sdmmc-clk {
+			rockchip,pins = <2 9 RK_FUNC_1 &pcfg_pull_none_drv_8ma>;
+		};
+
+		sdmmc_cmd: sdmmc-cmd {
+			rockchip,pins = <2 10 RK_FUNC_1 &pcfg_pull_up_drv_8ma>;
+		};
+
+		sdmmc_cd: sdmmc-cd {
+			rockchip,pins = <2 11 RK_FUNC_1 &pcfg_pull_up_drv_8ma>;
+		};
+
+		sdmmc_bus1: sdmmc-bus1 {
+			rockchip,pins = <2 5 RK_FUNC_1 &pcfg_pull_up_drv_8ma>;
+		};
+
+		sdmmc_bus4: sdmmc-bus4 {
+			rockchip,pins = <2 5 RK_FUNC_1 &pcfg_pull_up_drv_8ma>,
+					<2 6 RK_FUNC_1 &pcfg_pull_up_drv_8ma>,
+					<2 7 RK_FUNC_1 &pcfg_pull_up_drv_8ma>,
+					<2 8 RK_FUNC_1 &pcfg_pull_up_drv_8ma>;
+		};
+	};
+
+	usb {
+		host_vbus_drv: host-vbus-drv {
+			rockchip,pins = <0 4 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+};
+
+&saradc {
+	vref-supply = <&vcc_18>;
+	status = "okay";
+};
+
+&sdmmc {
+	clock-frequency = <50000000>;
+	clock-freq-min-max = <400000 50000000>;
+	cap-sd-highspeed;
+	supports-sd;
+	broken-cd;
+	card-detect-delay = <200>;
+
+	pinctrl-names = "default";
+	pinctrl-0 = <&sdmmc_clk &sdmmc_cmd &sdmmc_cd &sdmmc_bus4>;
+	bus-width = <4>;
+	num-slots = <1>;
+	ignore-pm-notify;
+	keep-power-in-suspend;
+
+	vmmc-supply = <&vcc_sd>;
+	vqmmc-supply = <&vccio_sd>;
+	status = "okay";
+};
+
+&uart2 {
+	status = "okay";
+};
+
+&uart4 {
+	status = "okay";
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart4_xfer>;
+};
+
+&usb_host0_ehci {
+	status = "okay";
+};
+
+&usb_otg {
+	status = "okay";
+};
+
+&wdt {
+	status = "okay";
+};
-- 
2.6.6



From seanpaul at chromium.org  Thu Jul 14 07:02:22 2016
From: seanpaul at chromium.org (Sean Paul)
Date: Thu, 14 Jul 2016 07:02:22 -0700
Subject: [v5 PATCH 5/5] drm/rockchip: cdn-dp: add cdn DP support for rk3399
In-Reply-To: <57870210.4020307@rock-chips.com>
References: <1468336188-565-1-git-send-email-zyw@rock-chips.com>
 <1468336188-565-6-git-send-email-zyw@rock-chips.com>
 <CAOw6vb+=F=X1MeWhUbs42MzP_CFfMkYrFUqKmWjVwwohufFOhw@mail.gmail.com>
 <57870210.4020307@rock-chips.com>
Message-ID: <CAOw6vbKCCoQHO-4TB3qCOuAA09aheXx0H6jRjQQnrkb4KqmtCg@mail.gmail.com>

On Wed, Jul 13, 2016 at 8:08 PM, Chris Zhong <zyw at rock-chips.com> wrote:
> Hi Sean
>
> Thanks for your detailed review. I'm working to modify most of code
> according to comment.
> And there is reply for some comment
>
> On 07/13/2016 09:59 PM, Sean Paul wrote:
>>
>> On Tue, Jul 12, 2016 at 8:09 AM, Chris Zhong <zyw at rock-chips.com> wrote:
>>>
>>> Add support for cdn DP controller which is embedded in the rk3399
>>> SoCs. The DP is compliant with DisplayPort Specification,
>>> Version 1.3, This IP is compatible with the rockchip type-c PHY IP.
>>> There is a uCPU in DP controller, it need a firmware to work,
>>> please put the firmware file to /lib/firmware/cdn/dptx.bin. The
>>> uCPU in charge of aux communication and link training, the host use
>>> mailbox to communicate with the ucpu.
>>> The dclk pin_pol of vop must not be invert for DP.
>>>
>>> Signed-off-by: Chris Zhong <zyw at rock-chips.com>
>>>
>>> ---
>>>
>>> Changes in v5:
>>> - alphabetical order
>>> - do not use long, use u32 or u64
>>> - return MODE_CLOCK_HIGH when requested > actual
>>> - Optimized Coding Style
>>> - add a formula to get better tu size and symbol value.
>>>
>>> Changes in v4:
>>> - use phy framework to control DP phy
>>> - support 2 phys
>>>
>>> Changes in v3:
>>> - use EXTCON_DISP_DP and EXTCON_DISP_DP_ALT cable to get dp port state.
>>> - reset spdif before config it
>>> - modify the firmware clk to 100Mhz
>>> - retry load firmware if fw file is requested too early
>>>
>>> Changes in v2:
>>> - Alphabetic order
>>> - remove excess error message
>>> - use define clk_rate
>>> - check all return value
>>> - remove dev_set_name(dp->dev, "cdn-dp");
>>> - use schedule_delayed_work
>>> - remove never-called functions
>>> - remove some unnecessary ()
>>>
>>> Changes in v1:
>>> - use extcon API
>>> - use hdmi-codec for the DP Asoc
>>> - do not initialize the "ret"
>>> - printk a err log when drm_of_encoder_active_endpoint_id
>>> - modify the dclk pin_pol to a single line
>>>
>>>   drivers/gpu/drm/rockchip/Kconfig            |   9 +
>>>   drivers/gpu/drm/rockchip/Makefile           |   1 +
>>>   drivers/gpu/drm/rockchip/cdn-dp-core.c      | 761
>>> ++++++++++++++++++++++++++++
>>>   drivers/gpu/drm/rockchip/cdn-dp-core.h      | 113 +++++
>>>   drivers/gpu/drm/rockchip/cdn-dp-reg.c       | 740
>>> +++++++++++++++++++++++++++
>>>   drivers/gpu/drm/rockchip/cdn-dp-reg.h       | 409 +++++++++++++++
>>
>> Could you explain the file naming convention in the rk driver? We've
>> got analogix_dp-rockchip.c, dw_hdmi-rockchip.c, dw-mipi-dsi.c, and now
>> cdn-dp-(core|reg).[ch]. I'm honestly not sure whether these filenames
>> are consistent with the rest, but bleh.
>>
> cdn is the IP vendor's name
> dp is the controller's name.
>
>>>   drivers/gpu/drm/rockchip/rockchip_drm_vop.c |   6 +-
>>>   drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   2 +
>>>   drivers/gpu/drm/rockchip/rockchip_vop_reg.c |   2 +
>>>   9 files changed, 2042 insertions(+), 1 deletion(-)
>>>   create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.c
>>>   create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.h
>>>   create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.c
>>>   create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.h
>>>
>>> diff --git a/drivers/gpu/drm/rockchip/Kconfig
>>> b/drivers/gpu/drm/rockchip/Kconfig
>>> index d30bdc3..20da9a8 100644
>>> --- a/drivers/gpu/drm/rockchip/Kconfig
>>> +++ b/drivers/gpu/drm/rockchip/Kconfig
>>> @@ -25,6 +25,15 @@ config ROCKCHIP_ANALOGIX_DP
>>>            for the Analogix Core DP driver. If you want to enable DP
>>>            on RK3288 based SoC, you should selet this option.
>>>
>>> +config ROCKCHIP_CDN_DP
>>> +        tristate "Rockchip cdn DP"
>>> +        depends on DRM_ROCKCHIP
>>> +        help
>>> +         This selects support for Rockchip SoC specific extensions
>>> +         for the cdn Dp driver. If you want to enable Dp on
>>
>> s/Dp/DP/
>>
>>> +         RK3399 based SoC, you should selet this
>>
>> s/selet/select/
>>
>>> +         option.
>>> +
>>>   config ROCKCHIP_DW_HDMI
>>>           tristate "Rockchip specific extensions for Synopsys DW HDMI"
>>>           depends on DRM_ROCKCHIP
>>> diff --git a/drivers/gpu/drm/rockchip/Makefile
>>> b/drivers/gpu/drm/rockchip/Makefile
>>> index 05d0713..abdecd5 100644
>>> --- a/drivers/gpu/drm/rockchip/Makefile
>>> +++ b/drivers/gpu/drm/rockchip/Makefile
>>> @@ -7,6 +7,7 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
>>>   rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
>>>
>>>   obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
>>> +obj-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
>>>   obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
>>>   obj-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
>>>   obj-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
>>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c
>>> b/drivers/gpu/drm/rockchip/cdn-dp-core.c
>>> new file mode 100644
>>> index 0000000..5b8a15e
>>> --- /dev/null
>>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
>>> @@ -0,0 +1,761 @@
>>> +/*
>>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>>> + * Author: Chris Zhong <zyw at rock-chips.com>
>>> + *
>>> + * This software is licensed under the terms of the GNU General Public
>>> + * License version 2, as published by the Free Software Foundation, and
>>> + * may be copied, distributed, and modified under those terms.
>>> + *
>>> + * 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 <drm/drmP.h>
>>> +#include <drm/drm_atomic_helper.h>
>>> +#include <drm/drm_crtc_helper.h>
>>> +#include <drm/drm_dp_helper.h>
>>> +#include <drm/drm_edid.h>
>>> +#include <drm/drm_of.h>
>>> +
>>> +#include <linux/clk.h>
>>> +#include <linux/component.h>
>>> +#include <linux/extcon.h>
>>> +#include <linux/firmware.h>
>>> +#include <linux/regmap.h>
>>> +#include <linux/reset.h>
>>> +#include <linux/mfd/syscon.h>
>>> +#include <linux/phy/phy.h>
>>> +
>>> +#include <sound/hdmi-codec.h>
>>> +
>>> +#include <video/of_videomode.h>
>>> +#include <video/videomode.h>
>>
>> Doesn't seem like you use these.
>>
>>
>>> +
>>> +#include "cdn-dp-core.h"
>>> +#include "cdn-dp-reg.h"
>>> +#include "rockchip_drm_vop.h"
>>> +
>>> +#define connector_to_dp(c) \
>>> +               container_of(c, struct cdn_dp_device, connector)
>>> +
>>> +#define encoder_to_dp(c) \
>>> +               container_of(c, struct cdn_dp_device, encoder)
>>> +
>>> +/* dp grf register offset */
>>> +#define DP_VOP_SEL             0x6224
>>> +#define DP_SEL_VOP_LIT         BIT(12)
>>> +#define DP_CLK_RATE            100000000
>>
>> If this clock rate never changes, it should probably live somewhere in
>> the clock driver.
>
>
>
>>
>>> +#define WAIT_HPD_STABLE                300
>>
>> Encode the units in these two define names, ie: DP_CLK_RATE_HZ,
>> WAIT_HPD_STABLE_MS
>>
>>> +
>>> +static int cdn_dp_clk_enable(struct cdn_dp_device *dp)
>>> +{
>>> +       int ret;
>>> +
>>> +       ret = clk_prepare_enable(dp->pclk);
>>> +       if (ret < 0) {
>>> +               dev_err(dp->dev, "cannot enable dp pclk %d\n", ret);
>>> +               goto err_pclk;
>>> +       }
>>> +
>>> +       ret = clk_prepare_enable(dp->core_clk);
>>> +       if (ret < 0) {
>>> +               dev_err(dp->dev, "cannot enable core_clk %d\n", ret);
>>> +               goto err_core_clk;
>>> +       }
>>> +
>>> +       ret = clk_set_rate(dp->core_clk, DP_CLK_RATE);
>>> +       if (ret < 0) {
>>> +               dev_err(dp->dev, "cannot set dp core clk to %d %d\n",
>>> +                       DP_CLK_RATE, ret);
>>> +               goto err_set_rate;
>>> +       }
>>> +
>>> +       /* notice fw the clk freq value */
>>
>> This might be clearer if it was rephrased to "update fw with the
>> current core clk frequency". I still think the rate should be set in
>> the clock driver, and perhaps you can query the rate to get the
>> current value.
>
> This function is setting the controller register, it let the uCPU know what
> freq it is, then the uCPU use this value to do something.
>

I think that's consistent with my suggestion. Either way, this comment
doesn't make sense, so please either remove it or improve it.

>>
>>> +       cdn_dp_set_fw_clk(dp, DP_CLK_RATE);
>>> +
>>> +       return 0;
>>> +
>>> +err_set_rate:
>>> +       clk_disable_unprepare(dp->core_clk);
>>> +err_core_clk:
>>> +       clk_disable_unprepare(dp->pclk);
>>> +err_pclk:
>>> +       return ret;
>>> +}
>>> +
>>> +static enum drm_connector_status
>>> +cdn_dp_connector_detect(struct drm_connector *connector, bool force)
>>> +{
>>> +       struct cdn_dp_device *dp = connector_to_dp(connector);
>>> +
>>> +       return dp->hpd_status ? connector_status_connected :
>>> +                               connector_status_disconnected;
>>
>> If you just stored hpd_status as drm_connector_status, you could avoid
>> having to translate the bool.
>>
>>> +}
>>> +
>>> +static void cdn_dp_connector_destroy(struct drm_connector *connector)
>>> +{
>>> +       drm_connector_unregister(connector);
>>> +       drm_connector_cleanup(connector);
>>> +}
>>> +
>>> +static struct drm_connector_funcs cdn_dp_atomic_connector_funcs = {
>>> +       .dpms = drm_atomic_helper_connector_dpms,
>>> +       .detect = cdn_dp_connector_detect,
>>> +       .destroy = cdn_dp_connector_destroy,
>>> +       .fill_modes = drm_helper_probe_single_connector_modes,
>>> +       .reset = drm_atomic_helper_connector_reset,
>>> +       .atomic_duplicate_state =
>>> drm_atomic_helper_connector_duplicate_state,
>>> +       .atomic_destroy_state =
>>> drm_atomic_helper_connector_destroy_state,
>>> +};
>>> +
>>> +static int cdn_dp_connector_get_modes(struct drm_connector *connector)
>>> +{
>>> +       struct cdn_dp_device *dp = connector_to_dp(connector);
>>> +       struct edid *edid;
>>> +
>>> +       if (!dp->fw_loaded)
>>
>> Can this actually happen? Seems like we shouldn't be returning
>> connected from detect if the fw isn't loaded, so this should never be
>> true. If that is the case, I think BUG_ON or WARN_ON would be more
>> appropriate here.
>>
>>
>>> +               return 0;
>>> +
>>> +       edid = drm_do_get_edid(connector, cdn_dp_get_edid_block, dp);
>>> +       if (edid) {
>>> +               dev_dbg(dp->dev, "got edid: width[%d] x height[%d]\n",
>>> +                       edid->width_cm, edid->height_cm);
>>> +
>>> +               dp->sink_has_audio = drm_detect_monitor_audio(edid);
>>> +               drm_mode_connector_update_edid_property(connector, edid);
>>> +               drm_add_edid_modes(connector, edid);
>>> +               /* Store the ELD */
>>
>> This comment isn't useful
>>
>>> +               drm_edid_to_eld(connector, edid);
>>> +               kfree(edid);
>>> +       } else {
>>> +               dev_dbg(dp->dev, "failed to get edid\n");
>>
>> This seems to warrant a stronger level than dbg, perhaps warn. Also,
>> should this really return success?
>>
>>> +       }
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +static struct drm_encoder *
>>> +       cdn_dp_connector_best_encoder(struct drm_connector *connector)
>>> +{
>>> +       struct cdn_dp_device *dp = connector_to_dp(connector);
>>> +
>>> +       return &dp->encoder;
>>> +}
>>> +
>>> +static int cdn_dp_connector_mode_valid(struct drm_connector *connector,
>>> +                                      struct drm_display_mode *mode)
>>> +{
>>> +       struct cdn_dp_device *dp = connector_to_dp(connector);
>>> +       struct drm_display_info *display_info =
>>> &dp->connector.display_info;
>>> +       u32 requested = mode->clock * display_info->bpc * 3 / 1000;
>>> +       u32 actual, rate;
>>> +
>>> +       rate = drm_dp_bw_code_to_link_rate(dp->link.rate);
>>> +       actual = rate * dp->link.num_lanes / 100;
>>> +
>>> +       /* efficiency is about 0.8 */
>>> +       actual = actual * 8 / 10;
>>> +
>>> +       if (requested > actual) {
>>> +               dev_dbg(dp->dev, "requested=%d, actual=%d, clock=%d\n",
>>> +                       requested, actual, mode->clock);
>>> +               return MODE_CLOCK_HIGH;
>>> +       }
>>
>> This doesn't seem like something that is likely to happen given the
>> trained rate should be a function of the display that's attached. Is
>> that correct?
>
> Yes, the link training should be a function of the display. But sometimes
> the edid give
> us a excess size for default resolution, it need somewhere to do this mode
> valid checking.
> Maybe we can do it at mode_set.
>

That's kind of interesting. You've seen displays which give you a lane
count that is insufficient for the modes it provides in the edid?
You're always training at 5.4 GHz, so I wouldn't expect this.

> ...
>>
>>
>>> +       hdr = (struct cdn_firmware_header *)fw->data;
>>> +       if (fw->size != le32_to_cpu(hdr->size_bytes))
>>> +               return -EINVAL;
>>> +
>>> +       iram_data = (const u32 *)(fw->data + hdr->header_size);
>>> +       dram_data = (const u32 *)(fw->data + hdr->header_size +
>>> hdr->iram_size);
>>> +
>>> +       ret = cdn_dp_load_firmware(dp, iram_data, hdr->iram_size,
>>> +                                  dram_data, hdr->dram_size);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       release_firmware(fw);
>>
>> Move this out of here and into the same scope as request_firmware so
>> there are no leaks.
>>
>>> +
>>> +       ret = cdn_dp_active(dp, true);
>>> +       if (ret) {
>>> +               dev_err(dp->dev, "active ucpu failed: %d\n", ret);
>>> +               return ret;
>>> +       }
>>> +
>>> +       return cdn_dp_event_config(dp);
>>> +}
>>> +
>>> +static int cdn_dp_init(struct cdn_dp_device *dp)
>>> +{
>>> +       struct device *dev = dp->dev;
>>> +       struct device_node *np = dev->of_node;
>>> +       struct platform_device *pdev = to_platform_device(dev);
>>> +       struct resource *res;
>>> +       int ret;
>>> +
>>> +       dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
>>> +       if (IS_ERR(dp->grf)) {
>>> +               dev_err(dev, "cdn-dp needs rockchip,grf property\n");
>>> +               return PTR_ERR(dp->grf);
>>> +       }
>>> +
>>> +       dp->irq = platform_get_irq(pdev, 0);
>>> +       if (dp->irq < 0) {
>>> +               dev_err(dev, "cdn-dp can not get irq\n");
>>> +               return dp->irq;
>>> +       }
>>> +
>>> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> +       dp->regs = devm_ioremap_resource(dev, res);
>>> +       if (IS_ERR(dp->regs)) {
>>> +               dev_err(dev, "ioremap reg failed\n");
>>> +               return PTR_ERR(dp->regs);
>>> +       }
>>> +
>>> +       dp->core_clk = devm_clk_get(dev, "core-clk");
>>> +       if (IS_ERR(dp->core_clk)) {
>>> +               dev_err(dev, "cannot get core_clk_dp\n");
>>> +               return PTR_ERR(dp->core_clk);
>>> +       }
>>> +
>>> +       dp->pclk = devm_clk_get(dev, "pclk");
>>> +       if (IS_ERR(dp->pclk)) {
>>> +               dev_err(dev, "cannot get pclk\n");
>>> +               return PTR_ERR(dp->pclk);
>>> +       }
>>> +
>>> +       dp->spdif_clk = devm_clk_get(dev, "spdif");
>>> +       if (IS_ERR(dp->spdif_clk)) {
>>> +               dev_err(dev, "cannot get spdif_clk\n");
>>> +               return PTR_ERR(dp->spdif_clk);
>>> +       }
>>> +
>>> +       dp->spdif_rst = devm_reset_control_get(dev, "spdif");
>>> +       if (IS_ERR(dp->spdif_rst)) {
>>> +               dev_err(dev, "no spdif reset control found\n");
>>> +               return PTR_ERR(dp->spdif_rst);
>>> +       }
>>> +
>>> +       dp->dpms_mode = DRM_MODE_DPMS_OFF;
>>> +
>>> +       ret = cdn_dp_clk_enable(dp);
>>> +       if (ret < 0)
>>> +               return ret;
>>> +
>>> +       dp_clock_reset_seq(dp);
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +static int cdn_dp_audio_hw_params(struct device *dev,
>>> +                                 struct hdmi_codec_daifmt *daifmt,
>>> +                                 struct hdmi_codec_params *params)
>>> +{
>>> +       struct cdn_dp_device *dp = dev_get_drvdata(dev);
>>> +       struct audio_info audio = {
>>> +               .sample_width = params->sample_width,
>>> +               .sample_rate = params->sample_rate,
>>> +               .channels = params->channels,
>>> +       };
>>> +       int ret;
>>> +
>>> +       if (!dp->encoder.crtc)
>>> +               return -ENODEV;
>>> +
>>> +       switch (daifmt->fmt) {
>>> +       case HDMI_I2S:
>>> +               audio.format = AFMT_I2S;
>>> +               break;
>>> +       case HDMI_SPDIF:
>>> +               audio.format = AFMT_SPDIF;
>>> +               break;
>>> +       default:
>>> +               dev_err(dev, "%s: Invalid format %d\n", __func__,
>>> daifmt->fmt);
>>> +               return -EINVAL;
>>> +       }
>>> +
>>> +       ret = cdn_dp_audio_config_set(dp, &audio);
>>> +       if (!ret)
>>> +               dp->audio_info = audio;
>>> +
>>> +       return ret;
>>> +}
>>> +
>>> +static void cdn_dp_audio_shutdown(struct device *dev)
>>> +{
>>> +       struct cdn_dp_device *dp = dev_get_drvdata(dev);
>>> +       int ret = cdn_dp_audio_stop(dp, &dp->audio_info);
>>> +
>>> +       if (!ret)
>>> +               dp->audio_info.format = AFMT_UNUSED;
>>> +}
>>> +
>>> +static int cdn_dp_audio_digital_mute(struct device *dev, bool enable)
>>> +{
>>> +       struct cdn_dp_device *dp = dev_get_drvdata(dev);
>>> +
>>> +       return cdn_dp_audio_mute(dp, enable);
>>> +}
>>> +
>>> +static int cdn_dp_audio_get_eld(struct device *dev, uint8_t *buf, size_t
>>> len)
>>> +{
>>> +       struct cdn_dp_device *dp = dev_get_drvdata(dev);
>>> +       struct drm_mode_config *config = &dp->encoder.dev->mode_config;
>>> +       struct drm_connector *connector;
>>> +       int ret = -ENODEV;
>>> +
>>> +       mutex_lock(&config->mutex);
>>> +       list_for_each_entry(connector, &config->connector_list, head) {
>>> +               if (&dp->encoder == connector->encoder) {
>>> +                       memcpy(buf, connector->eld,
>>> +                              min(sizeof(connector->eld), len));
>>> +                       ret = 0;
>>> +               }
>>> +       }
>>> +       mutex_unlock(&config->mutex);
>>> +
>>> +       return ret;
>>> +}
>>> +
>>> +static const struct hdmi_codec_ops audio_codec_ops = {
>>> +       .hw_params = cdn_dp_audio_hw_params,
>>> +       .audio_shutdown = cdn_dp_audio_shutdown,
>>> +       .digital_mute = cdn_dp_audio_digital_mute,
>>> +       .get_eld = cdn_dp_audio_get_eld,
>>> +};
>>> +
>>> +static int cdn_dp_audio_codec_init(struct cdn_dp_device *dp,
>>> +                                  struct device *dev)
>>> +{
>>> +       struct hdmi_codec_pdata codec_data = {
>>> +               .i2s = 1,
>>> +               .spdif = 1,
>>> +               .ops = &audio_codec_ops,
>>> +               .max_i2s_channels = 8,
>>> +       };
>>> +
>>> +       dp->audio_pdev = platform_device_register_data(
>>> +                        dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO,
>>> +                        &codec_data, sizeof(codec_data));
>>> +
>>> +       return PTR_ERR_OR_ZERO(dp->audio_pdev);
>>> +}
>>> +
>>> +static void cdn_dp_get_state(struct cdn_dp_device *dp, struct extcon_dev
>>> *edev)
>>> +{
>>> +       bool dfp, dptx;
>>> +       u8 cap_lanes;
>>> +
>>> +       dfp = extcon_get_cable_state_(edev, EXTCON_USB_HOST);
>>> +       dptx = extcon_get_cable_state_(edev, EXTCON_DISP_DP);
>>> +
>>> +       if (dfp && dptx)
>>> +               cap_lanes = 2;
>>> +       else if (dptx)
>>> +               cap_lanes = 4;
>>> +       else
>>> +               cap_lanes = 0;
>>> +
>>> +       if (cap_lanes != dp->cap_lanes) {
>>> +               dp->cap_lanes = cap_lanes;
>>> +               schedule_delayed_work(&dp->event_wq, 0);
>>> +       }
>>
>> A get function shouldn't be scheduling work. Keep the assignment here
>> and move the schedule into the pd_event function.
>>
>>> +}
>>> +
>>> +static int cdn_dp_pd_event(struct notifier_block *nb,
>>> +                          unsigned long event, void *priv)
>>> +{
>>> +       struct cdn_dp_device *dp;
>>> +       struct extcon_dev *edev = priv;
>>> +       u8 i;
>>> +
>>> +       dp = container_of(nb, struct cdn_dp_device, event_nb);
>>> +
>>> +       for (i = 0; i < MAX_PHY; i++) {
>>> +               if (edev == dp->extcon[i]) {
>>> +                       dp->port_id = i;
>>> +                       break;
>>> +               }
>>> +       }
>>> +
>>> +       cdn_dp_get_state(dp, edev);
>>
>> There's a race here. If the cap_lanes change in between scheduling the
>> work and the work function running (ie, plug/unplug), the work
>> function will have out-of-date information. You should call this from
>> the work function instead.
>>
>>
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +static void cdn_dp_pd_event_wq(struct work_struct *work)
>>> +{
>>> +       struct cdn_dp_device *dp;
>>> +       int ret;
>>> +
>>> +       dp = container_of(work, struct cdn_dp_device, event_wq.work);
>>> +
>>> +       ret = request_firmware(&dp->fw, "cdn/dptx.bin", dp->dev);
>>
>> This filename needs to be abstracted into a #define.
>>
>> You're also failing to call release_firmware in a bunch of cases
>> below. It needs to be sorted out.
>>
>>
>>> +       if (ret == -ENOENT && !dp->fw_loaded) {
>>
>> So the case where ret == -ENOENT and dp->fw_loaded is valid? Or is that a
>> bug?
>>
>> I'm not convinced you need fw_loaded stored in cdn_dp_device. It seems
>> like something you could either get rid of (hopefully), or convert to
>> a local.
>
> it is not bug, "ret == -ENOENT" is for file missing, maybe user space is not
> ready.
>
> if dp->fw_loaded it means the firmware has been loaded, do not load again.
>

So how can the fw be already loaded and yet the fw file is missing?

>
>>
>>> +               /*
>>> +                * If can not find the file, retry to load the firmware
>>> in 1
>>> +                * second, if still failed after 1 minute, give up.
>>> +                */
>>> +               if (dp->fw_retry++ < 60) {
>>> +                       schedule_delayed_work(&dp->event_wq,
>>> +                                             msecs_to_jiffies(HZ));
>>
>> I'd rather do this as an exponential back-off so if it doesn't find
>> the file, it waits progressively longer.
>>
>> Perhaps something like:
>>
>> #define MAX_FW_WAIT_SECS 64
>>
>> [...]
>>
>> if (dp->fw_wait <= MAX_FW_WAIT_SECS) {
>>    schedule_delayed_work(&dp_>event_wq, msecs_to_jiffies(dp->fw_wait *
>> HZ));
>>    dp->fw_wait *= 2;
>> }
>
> If so, sometimes boot with DP will later than boot and then plug DP cable.
>

Yeah, I realize that. It's a trade-off between waking up every second
vs potentially delaying the external display coming up. I think I'm Ok
with the external display taking a bit longer.


>
>>> +               }
>>> +
>>> +               dev_err(dp->dev, "failed to request firmware %d\n", ret);
>>
>> Print the wait/retries.
>>
>>> +               return;
>>> +       }
>>> +
>>> +       if (!dp->cap_lanes) {
>>> +               if (dp->phy_status[dp->port_id])
>>> +                       phy_power_off(dp->phy[dp->port_id]);
>>> +               dp->phy_status[dp->port_id] = 0;
>>> +               dp->hpd_status = false;
>>> +               drm_helper_hpd_irq_event(dp->drm_dev);
>>> +               return;
>>> +       }
>>> +
>>> +       if (!dp->phy_status[dp->port_id]) {
>>> +               ret = phy_power_on(dp->phy[dp->port_id]);
>>> +               if (ret)
>>
>> Log something in this case
>>
>>> +                       return;
>>> +
>>> +               msleep(WAIT_HPD_STABLE);
>>
>> 300ms is a long time, do we really need to wait that long?
>
> The 300ms is a safe value, maybe this time can be reduced
>

Yeah, the DisplayPort spec states the HPD pulse should be considered a
plug event after 2ms, so I strongly suspect this can be pulled in.

>>
>>> +       }
>>> +
>>> +       dp->phy_status[dp->port_id] = 1;
>>
>> Do you really need phy_status? It seems like if things are
>> well-behaved you shouldn't need it. Of course, if you're turning
>> on/off multiple times, perhaps there's a bug elsewhere.
>
> This status is needed, since the phy_power_on would increase the
> power_count, this status can avoid power on multiple times
>

Yeah, I realize the phy_power is refcounted. Why do you have unmatched
on/off calls?

>>
>>> +
>>> +       ret = cdn_dp_firmware_init(dp);
>>> +       if (ret)
>>> +               return;
>>> +
>>> +       /* read hpd status failed, or the hpd does not exist. */
>>> +       ret = cdn_dp_get_hpd_status(dp);
>>> +       if (ret <= 0)
>>
>> Log something?
>>
>>> +               return;
>>> +
>>> +       /*
>>> +        * Set the capability and start the training process once
>>> +        * the hpd is detected.
>>> +        */
>>> +       ret = cdn_dp_set_host_cap(dp);
>>> +       if (ret) {
>>> +               dev_err(dp->dev, "set host capabilities failed\n");
>>
>> print ret
>>
>>> +               return;
>>> +       }
>>> +
>>> +       ret = cdn_dp_training_start(dp);
>>> +       if (ret) {
>>> +               dev_err(dp->dev, "hw lt err:%d\n", ret);
>>
>> s/lt/link training/
>>
>>> +               return;
>>> +       }
>>> +
>>> +       ret = cdn_dp_get_lt_status(dp);
>>
>> Change to cdn_dp_get_training_status
>>
>>> +       if (ret) {
>>> +               dev_err(dp->dev, "hw lt get status failed\n");
>>
>> print ret
>>
>>> +               return;
>>> +       }
>>> +
>>> +       dev_info(dp->dev, "rate:%d, lanes:%d\n",
>>> +                dp->link.rate, dp->link.num_lanes);
>>> +
>>
>> I think everything from cdn_dp_firmare_init to here should be moved
>> out of the wq and deferred until modeset time. That way you're
>> training the link at the same time as you're setting the mode, which
>> should obviate the need to check the clocks in mode_valid.
>
>
> cdn_dp_training_start and cdn_dp_get_lt_status can be move the mode_set,
> but the others are need by get_modes, so stay here is better.
>

Ok, that sounds like an improvement.

>>
>>> +       dp->hpd_status = true;
>>> +       drm_helper_hpd_irq_event(dp->drm_dev);
>>> +}
>>> +
>>> +static int cdn_dp_bind(struct device *dev, struct device *master,
>>> +                      void *data)
>>> +{
>>> +       struct cdn_dp_device *dp = dev_get_drvdata(dev);
>>> +       struct drm_encoder *encoder;
>>> +       struct drm_connector *connector;
>>> +       struct drm_device *drm_dev = data;
>>> +       int ret, i;
>>> +
>>> +       ret = cdn_dp_init(dp);
>>> +       if (ret < 0)
>>> +               return ret;
>>> +
>>> +       dp->drm_dev = drm_dev;
>>> +
>>> +       encoder = &dp->encoder;
>>> +
>>> +       encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
>>> +
>>> dev->of_node);
>>> +       DRM_DEBUG_KMS("possible_crtcs = 0x%x\n",
>>> encoder->possible_crtcs);
>>> +
>>> +       ret = drm_encoder_init(drm_dev, encoder, &cdn_dp_encoder_funcs,
>>> +                              DRM_MODE_ENCODER_TMDS, NULL);
>>> +       if (ret) {
>>> +               DRM_ERROR("failed to initialize encoder with drm\n");
>>> +               return ret;
>>> +       }
>>> +
>>> +       drm_encoder_helper_add(encoder, &cdn_dp_encoder_helper_funcs);
>>> +
>>> +       connector = &dp->connector;
>>> +       connector->polled = DRM_CONNECTOR_POLL_HPD;
>>> +       connector->dpms = DRM_MODE_DPMS_OFF;
>>> +
>>> +       ret = drm_connector_init(drm_dev, connector,
>>> +                                &cdn_dp_atomic_connector_funcs,
>>> +                                DRM_MODE_CONNECTOR_DisplayPort);
>>> +       if (ret) {
>>> +               DRM_ERROR("failed to initialize connector with drm\n");
>>> +               goto err_free_encoder;
>>> +       }
>>> +
>>> +       drm_connector_helper_add(connector,
>>> &cdn_dp_connector_helper_funcs);
>>> +
>>> +       ret = drm_mode_connector_attach_encoder(connector, encoder);
>>> +       if (ret) {
>>> +               DRM_ERROR("failed to attach connector and encoder\n");
>>> +               goto err_free_connector;
>>> +       }
>>> +
>>> +       cdn_dp_audio_codec_init(dp, dev);
>>> +
>>> +       dp->event_nb.notifier_call = cdn_dp_pd_event;
>>> +       INIT_DELAYED_WORK(&dp->event_wq, cdn_dp_pd_event_wq);
>>> +
>>> +       for (i = 0; i < MAX_PHY; i++) {
>>> +               if (IS_ERR(dp->extcon[i]))
>>> +                       continue;
>>> +
>>> +               ret = extcon_register_notifier(dp->extcon[i],
>>> EXTCON_DISP_DP,
>>> +                                              &dp->event_nb);
>>> +               if (ret) {
>>> +                       dev_err(dev, "regitster EXTCON_DISP_DP notifier
>>> err\n");
>>> +                       return ret;
>>> +               }
>>> +
>>> +               cdn_dp_get_state(dp, dp->extcon[i]);
>>> +       }
>>> +
>>> +       return 0;
>>> +
>>> +err_free_connector:
>>> +       drm_connector_cleanup(connector);
>>> +err_free_encoder:
>>> +       drm_encoder_cleanup(encoder);
>>> +       return ret;
>>> +}
>>> +
>>> +static void cdn_dp_unbind(struct device *dev, struct device *master,
>>> void *data)
>>> +{
>>> +       struct cdn_dp_device *dp = dev_get_drvdata(dev);
>>> +       struct drm_encoder *encoder = &dp->encoder;
>>> +       int i;
>>> +
>>> +       platform_device_unregister(dp->audio_pdev);
>>> +       cdn_dp_encoder_disable(encoder);
>>> +       encoder->funcs->destroy(encoder);
>>> +       drm_connector_unregister(&dp->connector);
>>> +       drm_connector_cleanup(&dp->connector);
>>
>> just call connector->destroy() here
>>
>>> +       drm_encoder_cleanup(encoder);
>>
>> this is already called by encoder->destroy()
>>
>>> +
>>> +       for (i = 0; i < MAX_PHY; i++) {
>>> +               if (!IS_ERR(dp->extcon[i]))
>>> +                       extcon_unregister_notifier(dp->extcon[i],
>>> EXTCON_USB,
>>> +                                                  &dp->event_nb);
>>> +       }
>>> +}
>>> +
>>> +static const struct component_ops cdn_dp_component_ops = {
>>> +       .bind = cdn_dp_bind,
>>> +       .unbind = cdn_dp_unbind,
>>> +};
>>> +
>>> +static int cdn_dp_probe(struct platform_device *pdev)
>>> +{
>>> +       struct device *dev = &pdev->dev;
>>> +       struct cdn_dp_device *dp;
>>> +       u8 count = 0;
>>> +       int i;
>>> +
>>> +       dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
>>> +       if (!dp)
>>> +               return -ENOMEM;
>>> +       dp->dev = dev;
>>> +
>>> +       for (i = 0; i < MAX_PHY; i++) {
>>> +               dp->extcon[i] = extcon_get_edev_by_phandle(dev, i);
>>> +               dp->phy[i] = devm_of_phy_get_by_index(dev, dev->of_node,
>>> i);
>>> +
>>> +               if (!IS_ERR(dp->extcon[i]) && !IS_ERR(dp->phy[i]))
>>> +                       count++;
>>> +
>>> +               if (PTR_ERR(dp->extcon[i]) == -EPROBE_DEFER ||
>>> +                   PTR_ERR(dp->phy[i]) == -EPROBE_DEFER)
>>> +                       return -EPROBE_DEFER;
>>> +       }
>>> +
>>> +       if (!count) {
>>> +               dev_err(dev, "missing extcon or phy\n");
>>> +               return -EINVAL;
>>> +       }
>>> +
>>> +       dev_set_drvdata(dev, dp);
>>> +
>>> +       return component_add(dev, &cdn_dp_component_ops);
>>> +}
>>> +
>>> +static int cdn_dp_remove(struct platform_device *pdev)
>>> +{
>>> +       component_del(&pdev->dev, &cdn_dp_component_ops);
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +static const struct of_device_id cdn_dp_dt_ids[] = {
>>> +       { .compatible = "rockchip,rk3399-cdn-dp" },
>>> +       {}
>>> +};
>>> +
>>> +MODULE_DEVICE_TABLE(of, cdn_dp_dt_ids);
>>> +
>>> +static struct platform_driver cdn_dp_driver = {
>>> +       .probe = cdn_dp_probe,
>>> +       .remove = cdn_dp_remove,
>>> +       .driver = {
>>> +                  .name = "cdn-dp",
>>> +                  .owner = THIS_MODULE,
>>> +                  .of_match_table = of_match_ptr(cdn_dp_dt_ids),
>>> +       },
>>> +};
>>> +
>>> +module_platform_driver(cdn_dp_driver);
>>> +
>>> +MODULE_AUTHOR("Chris Zhong <zyw at rock-chips.com>");
>>> +MODULE_DESCRIPTION("cdn DP Driver");
>>> +MODULE_LICENSE("GPL v2");
>>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h
>>> b/drivers/gpu/drm/rockchip/cdn-dp-core.h
>>> new file mode 100644
>>> index 0000000..31ba22d
>>> --- /dev/null
>>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
>>> @@ -0,0 +1,113 @@
>>> +/*
>>> + * Copyright (C) 2016 Chris Zhong <zyw at rock-chips.com>
>>> + * Copyright (C) 2016 ROCKCHIP, Inc.
>>> + *
>>> + * 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.
>>> + */
>>> +
>>> +#ifndef _ROCKCHIP_EDP_CORE_H
>>> +#define _ROCKCHIP_EDP_CORE_H
>>
>> I think the include guard should match the filename
>>
>>> +
>>> +#include <drm/drmP.h>
>>> +#include <drm/drm_crtc_helper.h>
>>> +#include <drm/drm_dp_helper.h>
>>> +#include <drm/drm_panel.h>
>>> +#include "rockchip_drm_drv.h"
>>> +#include "cdn-dp-reg.h"
>>> +
>>> +#define MAX_PHY                2
>>
>> This should probably be stored in the .data member of of_device_id.
>>
>>> +
>>> +enum AUDIO_FORMAT {
>>
>> enum audio_format
>>
>>> +       AFMT_I2S = 0,
>>> +       AFMT_SPDIF = 1,
>>> +       AFMT_UNUSED,
>>> +};
>>> +
>>> +struct audio_info {
>>> +       enum AUDIO_FORMAT format;
>>> +       int sample_rate;
>>> +       int channels;
>>> +       int sample_width;
>>> +};
>>> +
>>> +struct video_info {
>>> +       bool h_sync_polarity;
>>> +       bool v_sync_polarity;
>>> +       bool interlaced;
>>> +       int color_depth;
>>> +       enum VIC_PXL_ENCODING_FORMAT color_fmt;
>>> +};
>>> +
>>> +struct cdn_firmware_header {
>>> +       u32 size_bytes; /* size of the entire header+image(s) in bytes */
>>> +       u32 header_size; /* size of just the header in bytes */
>>> +       u32 iram_size; /* size of iram */
>>> +       u32 dram_size; /* size of dram */
>>> +};
>>> +
>>> +struct cdn_dp_device {
>>> +       struct device *dev;
>>> +       struct drm_device *drm_dev;
>>> +       struct drm_connector connector;
>>> +       struct drm_encoder encoder;
>>> +       struct drm_display_mode mode;
>>> +       struct platform_device *audio_pdev;
>>> +
>>> +       const struct firmware *fw;      /* cdn dp firmware */
>>> +       unsigned int fw_version;        /* cdn fw version */
>>> +       u32 fw_retry;
>>> +       bool fw_loaded;
>>> +       void __iomem *regs;
>>> +       struct regmap *grf;
>>> +       unsigned int irq;
>>> +       struct clk *core_clk;
>>> +       struct clk *pclk;
>>> +       struct clk *spdif_clk;
>>> +       struct reset_control *spdif_rst;
>>> +       struct audio_info audio_info;
>>> +       struct video_info video_info;
>>> +       struct extcon_dev *extcon[MAX_PHY];
>>> +       struct phy *phy[MAX_PHY];
>>> +       struct notifier_block event_nb;
>>> +       struct delayed_work event_wq;
>>> +
>>> +       u8 port_id;
>>> +       u8 cap_lanes;
>>> +       bool hpd_status;
>>> +       bool phy_status[MAX_PHY];
>>> +
>>> +       int dpms_mode;
>>> +       struct drm_dp_link link;
>>> +       bool sink_has_audio;
>>> +};
>>> +
>>> +void dp_clock_reset_seq(struct cdn_dp_device *dp);
>>
>> cdn_ prefix here?
>>
>>> +
>>> +void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, int clk);
>>> +int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
>>> +                        u32 i_size, const u32 *d_mem, u32 d_size);
>>> +int cdn_dp_active(struct cdn_dp_device *dp, bool enable);
>>> +int cdn_dp_set_host_cap(struct cdn_dp_device *dp);
>>> +int cdn_dp_event_config(struct cdn_dp_device *dp);
>>> +int cdn_dp_get_event(struct cdn_dp_device *dp);
>>> +int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
>>> +int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value);
>>> +int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr);
>>> +int cdn_dp_get_edid_block(void *dp, u8 *edid,
>>> +                         unsigned int block, size_t length);
>>> +int cdn_dp_training_start(struct cdn_dp_device *dp);
>>> +int cdn_dp_get_lt_status(struct cdn_dp_device *dp);
>>> +int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active);
>>> +int cdn_dp_config_video(struct cdn_dp_device *dp);
>>> +int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info
>>> *audio);
>>> +int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable);
>>> +int cdn_dp_audio_config_set(struct cdn_dp_device *dp, struct audio_info
>>> *audio);
>>> +
>>
>> I really don't like that you've declared these in core.h, yet defined
>> them in reg.c.
>>
>>> +#endif  /* _ROCKCHIP_EDP_CORE_H */
>>
>> Make sure you update here too
>>
>>
>>> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c
>>> b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
>>> new file mode 100644
>>> index 0000000..8d5becd
>>> --- /dev/null
>>> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
>>> @@ -0,0 +1,740 @@
>>> +/*
>>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>>> + * Author: Chris Zhong <zyw at rock-chips.com>
>>> + *
>>> + * This software is licensed under the terms of the GNU General Public
>>> + * License version 2, as published by the Free Software Foundation, and
>>> + * may be copied, distributed, and modified under those terms.
>>> + *
>>> + * 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/clk.h>
>>> +#include <linux/device.h>
>>> +#include <linux/delay.h>
>>> +#include <linux/io.h>
>>> +#include <linux/iopoll.h>
>>> +#include <linux/reset.h>
>>> +
>>> +#include "cdn-dp-core.h"
>>> +#include "cdn-dp-reg.h"
>>> +
>>> +#define CDN_DP_SPDIF_CLK               200000000
>>> +#define FW_ALIVE_TIMEOUT_US            1000000
>>> +#define MAILBOX_TIMEOUT_US             5000000
>>> +
>>> +void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, int clk)
>>> +{
>>> +       writel(clk / 1000000, dp->regs + SW_CLK_H);
>>> +}
>>> +
>>> +void dp_clock_reset_seq(struct cdn_dp_device *dp)
>>> +{
>>> +       writel(0xfff, dp->regs + SOURCE_DPTX_CAR);
>>> +       writel(0x7, dp->regs + SOURCE_PHY_CAR);
>>> +       writel(0xf, dp->regs + SOURCE_PKT_CAR);
>>> +       writel(0xff, dp->regs + SOURCE_AIF_CAR);
>>> +       writel(0xf, dp->regs + SOURCE_CIPHER_CAR);
>>> +       writel(0x3, dp->regs + SOURCE_CRYPTO_CAR);
>>> +       writel(0, dp->regs + APB_INT_MASK);
>>
>> Pull these hardcoded values out into #defines
>>
>>> +}
>>> +
>>> +static int cdn_dp_mailbox_read(struct cdn_dp_device *dp)
>>> +{
>>> +       int val, ret;
>>> +
>>> +       if (!dp->fw_loaded)
>>> +               return 0;
>>
>> This seems like an error. Return -errno and log a message
>
> The audio will call this function before firmware during probe, return err
> cause audio register failed.
>

Hmm, so what happens when you return 0? Isn't that a potentially valid
value that could cause problems on the audio side?

>>
>>> +
>>> +       ret = readx_poll_timeout(readl, dp->regs + MAILBOX_EMPTY_ADDR,
>>> +                                val, !val, 1000, MAILBOX_TIMEOUT_US);
>>
>> Pull the 1000 out into a #define (here and below)
>>
>>> +       if (ret < 0) {
>>> +               dev_err(dp->dev, "failed to read mailbox, keep alive =
>>> %x\n",
>>> +                       readl(dp->regs + KEEP_ALIVE));
>>> +               return ret;
>>> +       }
>>> +
>>> +       return readl(dp->regs + MAILBOX0_RD_DATA) & 0xff;
>>> +}
>>> +
>>> +static int cdp_dp_mailbox_write(struct cdn_dp_device *dp, u8 val)
>>> +{
>>> +       int ret, full;
>>> +
>>> +       if (!dp->fw_loaded)
>>> +               return 0;
>>
>> Same here
>>
>>> +
>>> +       ret = readx_poll_timeout(readl, dp->regs + MAILBOX_FULL_ADDR,
>>> +                                full, !full, 1000, MAILBOX_TIMEOUT_US);
>>> +       if (ret < 0) {
>>> +               dev_err(dp->dev, "mailbox is full, keep alive = %x\n",
>>> +                       readl(dp->regs + KEEP_ALIVE));
>>> +               return ret;
>>> +       }
>>> +
>>> +       writel(val, dp->regs + MAILBOX0_WR_DATA);
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +static int cdn_dp_mailbox_response(struct cdn_dp_device *dp, u8
>>> module_id,
>>
>> I think _receive would be a better name than _response
>>
>>> +                                  u8 opcode, u8 *buff, u8 buff_size)
>>> +{
>>> +       int ret, i = 0;
>>> +       u8 header[4];
>>> +
>>> +       /* read the header of the message */
>>> +       while (i < 4) {
>>
>> for (i = 0; i < 4; i++) {
>>
>>> +               ret = cdn_dp_mailbox_read(dp);
>>> +               if (ret < 0)
>>> +                       return ret;
>>> +
>>> +               header[i++] = ret;
>>
>> header[i] = ret;
>>
>>> +       }
>>> +
>>> +       if (opcode != header[0] || module_id != header[1] ||
>>> +           buff_size != ((header[2] << 8) | header[3])) {
>>
>> pull header[2] << 8 | header[3] out into a local, mbox_size.
>>
>>> +               dev_err(dp->dev, "mailbox response failed");
>>> +
>>> +               /*
>>> +                * If the message in mailbox is not what we want, we need
>>> to
>>> +                * clear the mailbox by read.
>>> +                */
>>> +               i = (header[2] << 8) | header[3];
>>> +               while (i--)
>>
>> for (i = 0; i < mbox_size; i++)
>>
>>> +                       if (cdn_dp_mailbox_read(dp) < 0)
>>> +                               break;
>>> +
>>> +               return -EINVAL;
>>> +       }
>>> +
>>> +       i = 0;
>>> +       while (i < buff_size) {
>>
>> for (i = 0; i < buff_size; i++) {
>>
>>> +               ret = cdn_dp_mailbox_read(dp);
>>> +               if (ret < 0)
>>> +                       return ret;
>>> +
>>> +               buff[i++] = ret;
>>
>>   buff[i] = ret;
>>
>>> +       }
>>> +
>>> +       return 0;
>>
>>
>> Are you always guaranteed to have the entire message in the mailbox?
>> If not, you might consider supporting partial messages and returning
>> the length read.
>>
>>> +}
>>> +
>>> +static int cdn_dp_mailbox_send(struct cdn_dp_device *dp, u8 module_id,
>>> +                              u8 opcode, u16 size, u8 *message)
>>> +{
>>> +       u8 header[4];
>>> +       int ret, i;
>>> +
>>> +       header[0] = opcode;
>>> +       header[1] = module_id;
>>> +       header[2] = size >> 8;
>>
>> (size >> 8) & 0xff;
>>
>>> +       header[3] = size & 0xff;
>>> +
>>> +       for (i = 0; i < 4; i++) {
>>> +               ret = cdp_dp_mailbox_write(dp, header[i]);
>>> +               if (ret)
>>> +                       return ret;
>>> +       }
>>> +
>>> +       while (size--) {
>>
>> for (i = 0; i < size; i++) {
>>
>>> +               ret = cdp_dp_mailbox_write(dp, *message++);
>>
>> message[i]
>>
>>> +               if (ret)
>>
>> Log something here?
>>
>>> +                       return ret;
>>> +       }
>>> +
>>> +       return 0;
>>
>> Perhaps consider returning the number of bytes that were sent.
>>
>>> +}
>>> +
>>> +static int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
>>> +{
>>> +       u8 msg[6];
>>> +
>>> +       msg[0] = (addr >> 8) & 0xff;
>>> +       msg[1] = addr & 0xff;
>>> +       msg[2] = (val >> 24) & 0xff;
>>> +       msg[3] = (val >> 16) & 0xff;
>>> +       msg[4] = (val >> 8) & 0xff;
>>> +       msg[5] = val & 0xff;
>>> +       return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
>>> DPTX_WRITE_REGISTER,
>>> +                                  ARRAY_SIZE(msg), msg);
>>> +}
>>> +
>>> +int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
>>> +                        u32 i_size, const u32 *d_mem, u32 d_size)
>>> +{
>>> +       int i, reg, ret;
>>
>> reg should be u32
>>
>>> +
>>> +       /* reset ucpu before load firmware*/
>>> +       writel(APB_IRAM_PATH | APB_DRAM_PATH | APB_XT_RESET,
>>> +              dp->regs + APB_CTRL);
>>> +
>>> +       for (i = 0; i < i_size; i += 4)
>>> +               writel(*i_mem++, dp->regs + ADDR_IMEM + i);
>>> +
>>> +       for (i = 0; i < d_size; i += 4)
>>> +               writel(*d_mem++, dp->regs + ADDR_DMEM + i);
>>> +
>>> +       /* un-reset ucpu */
>>> +       writel(0, dp->regs + APB_CTRL);
>>> +
>>> +       /* check the keep alive register to make sure fw working */
>>> +       ret = readx_poll_timeout(readl, dp->regs + KEEP_ALIVE,
>>> +                                reg, reg, 2000, FW_ALIVE_TIMEOUT_US);
>>> +       if (ret < 0) {
>>> +               dev_err(dp->dev, "failed to loaded the FW reg = %x\n",
>>> reg);
>>> +               return -EINVAL;
>>> +       }
>>> +
>>> +       reg = readl(dp->regs + VER_L) & 0xff;
>>> +       dp->fw_version = reg;
>>> +       reg = readl(dp->regs + VER_H) & 0xff;
>>> +       dp->fw_version |= reg << 8;
>>> +       reg = readl(dp->regs + VER_LIB_L_ADDR) & 0xff;
>>> +       dp->fw_version |= reg << 16;
>>> +       reg = readl(dp->regs + VER_LIB_H_ADDR) & 0xff;
>>> +       dp->fw_version |= reg << 24;
>>> +
>>
>> Are there any sanity checks you can do on the fw_version before returning?
>
> This is first version of firmware, so do not nedd check the number, But I
> think adding a dev_dbg log here is better.
>

Ok, sounds good.

>>
>>> +       dp->fw_loaded = 1;
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +int cdn_dp_reg_write_bit(struct cdn_dp_device *dp, u16 addr, u8
>>> start_bit,
>>> +                        u8 bits_no, u32 val)
>>> +{
>>> +       u8 field[8];
>>> +
>>> +       field[0] = (addr >> 8) & 0xff;
>>> +       field[1] = addr & 0xff;
>>> +       field[2] = start_bit;
>>> +       field[3] = bits_no;
>>> +       field[4] = (val >> 24) & 0xff;
>>> +       field[5] = (val >> 16) & 0xff;
>>> +       field[6] = (val >> 8) & 0xff;
>>> +       field[7] = val & 0xff;
>>> +
>>> +       return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
>>> DPTX_WRITE_FIELD,
>>> +                           sizeof(field), field);
>>> +}
>>> +
>>> +int cdn_dp_active(struct cdn_dp_device *dp, bool enable)
>>> +{
>>> +       u8 active = enable ? 1 : 0;
>>> +       u8 resp;
>>> +       int ret;
>>> +
>>> +       /* set firmware status, 1: avtive; 0: standby */
>>
>> s/avtive/active/
>>
>>> +       ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_GENERAL,
>>> +                                 GENERAL_MAIN_CONTROL, 1, &active);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       ret = cdn_dp_mailbox_response(dp, MB_MODULE_ID_GENERAL,
>>> +                                     GENERAL_MAIN_CONTROL, &resp, 1);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       return resp ? 0 : -EINVAL;
>>> +}
>>> +
>>> +int cdn_dp_set_host_cap(struct cdn_dp_device *dp)
>>> +{
>>> +       u8 msg[8];
>>> +       int ret;
>>> +
>>> +       msg[0] = DP_LINK_BW_5_4;
>>
>> Why is this always 5.4GHz? Does the hw support any other rates?
>
> It is setting the max rate to 5.4G, not all support rate.
>

Ah, so it will automatically downgrade the link speed in the training
process if the sink doesn't support it?

>>
>>> +       msg[1] = dp->cap_lanes;
>>> +       msg[2] = VOLTAGE_LEVEL_2;
>>> +       msg[3] = PRE_EMPHASIS_LEVEL_3;
>>> +       msg[4] = PRBS7 | D10_2 | TRAINING_PTN1 | TRAINING_PTN2;
>>
>> You don't need to use training pattern 3 for hbr modes?
>
> This is the fault of pattern name, it should be:
>
> msg[4] = TPS1 | TPS2 | TPS3 | TPS4;
>
>
>
>>
>>> +       msg[5] = FAST_LT_NOT_SUPPORT;
>>> +       msg[6] = LANE_MAPPING_NORMAL;
>>> +       msg[7] = ENHANCED;
>>> +
>>> +       ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
>>> +                                 DPTX_SET_HOST_CAPABILITIES,
>>> +                                 ARRAY_SIZE(msg), msg);
>>
>> ARRAY_SIZE counts the number of elements, not the size, so I think
>> you're better off using sizeof instead. This applies to the rest of
>> the dp_mailbox_send instances as well.
>>
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       ret = cdn_dp_reg_write(dp, DP_AUX_SWAP_INVERSION_CONTROL,
>>> +                              AUX_HOST_INVERT);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +int cdn_dp_event_config(struct cdn_dp_device *dp)
>>> +{
>>> +       u8 msg[5] = {0, 0, 0, 0, 0};
>>
>> memset(msg, 0, sizeof(msg));
>>
>>> +
>>> +       msg[0] = DPTX_EVENT_ENABLE_HPD | DPTX_EVENT_ENABLE_TRAINING;
>>> +
>>> +       return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
>>> DPTX_ENABLE_EVENT,
>>> +                                   ARRAY_SIZE(msg), msg);
>>> +}
>>> +
>>> +int cdn_dp_get_event(struct cdn_dp_device *dp)
>>
>> Should return u32
>>
>>> +{
>>> +       return readl(dp->regs + SW_EVENTS0);
>>> +}
>>> +
>>> +int cdn_dp_get_hpd_status(struct cdn_dp_device *dp)
>>> +{
>>> +       u8 status;
>>> +       int ret;
>>> +
>>> +       ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_HPD_STATE,
>>> +                                 0, NULL);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       ret = cdn_dp_mailbox_response(dp, MB_MODULE_ID_DP_TX,
>>> +                                     DPTX_HPD_STATE, &status, 1);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       return status;
>>> +}
>>> +
>>> +int cdn_dp_get_edid_block(void *data, u8 *edid,
>>> +                         unsigned int block, size_t length)
>>> +{
>>> +       struct cdn_dp_device *dp = data;
>>> +       u8 msg[2], reg[EDID_DATA + EDID_BLOCK_SIZE];
>>> +       int ret;
>>> +
>>> +       if (length != EDID_BLOCK_SIZE)
>>> +               return -EINVAL;
>>> +
>>> +       msg[0] = block / 2;
>>> +       msg[1] = block % 2;
>>> +
>>> +       ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_GET_EDID,
>>> +                                 2, msg);
>>
>> sizeof(msg), msg);
>>
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       ret = cdn_dp_mailbox_response(dp, MB_MODULE_ID_DP_TX,
>>> +                                     DPTX_GET_EDID, reg,
>>> +                                     EDID_DATA + EDID_BLOCK_SIZE);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       if (reg[EDID_LENGTH_BYTE] != EDID_BLOCK_SIZE ||
>>> +           reg[EDID_SEGMENT_BUMBER] != block / 2) {
>>> +               dev_err(dp->dev, "edid block size err\n");
>>> +               return -EINVAL;
>>> +       }
>>> +
>>> +       memcpy(edid, &reg[EDID_DATA], EDID_BLOCK_SIZE);
>>
>> You can avoid the intermediate reg[] buffer if you restructure the
>> _response/_receive mailbox function. I'd suggest splitting it into a
>> _validate_response() function that reads the header and makes sure the
>> lengths match. The second part would be a _read_response() which
>> simply reads the specified number of bytes from the mailbox. For
>> convenience, you could make cdn_dp_mailbox_receive() a helper which
>> calls both validate and read for times when you're reading the same
>> number as you're validating.
>>
>> So in this function, you'd validate the response has EDID_DATA +
>> EDID_BLOCK_SIZE bytes, then read the EDID_DATA prefix into a local,
>> then read EDID_BLOCK_DATA directly into edid.
>>
>> I think you'll also need a _drain function to clear out the mailbox in
>> case of error (otherwise the next read will fail).
>>
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +int cdn_dp_training_start(struct cdn_dp_device *dp)
>>> +{
>>> +       u8 msg, event[2];
>>> +       unsigned long timeout;
>>> +       int ret;
>>> +
>>> +       msg = LINK_TRAINING_RUN;
>>> +
>>> +       /* start training */
>>> +       ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
>>> DPTX_TRAINING_CONTROL,
>>> +                                 1, &msg);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       /* the whole training should finish in 500ms */
>>> +       timeout = jiffies + msecs_to_jiffies(500);
>>
>> Pull 500 out into a #define
>>
>>> +       while (1) {
>>
>> while(time_before(jiffies, timeout))
>>
>>> +               msleep(20);
>>
>> Pull 20 out into a #define
>>
>>> +               ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
>>> +                                         DPTX_READ_EVENT, 0, NULL);
>>> +               if (ret)
>>> +                       return ret;
>>> +
>>> +               ret = cdn_dp_mailbox_response(dp, MB_MODULE_ID_DP_TX,
>>> +                                             DPTX_READ_EVENT, event, 2);
>>> +               if (ret)
>>> +                       return ret;
>>> +
>>> +               if (event[1] & EQ_PHASE_FINISHED)
>>> +                       break;
>>
>> return 0
>>
>>> +
>>> +               if (time_after(jiffies, timeout))
>>> +                       return -ETIMEDOUT;
>>> +       }
>>> +
>>> +       return 0;
>>
>> return -ETIMEDOUT
>>
>>> +}
>>> +
>>> +int cdn_dp_get_lt_status(struct cdn_dp_device *dp)
>>
>> s/lt/training/
>>
>>> +{
>>> +       u8 status[10];
>>> +       int ret;
>>> +
>>> +       ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
>>> DPTX_READ_LINK_STAT,
>>> +                                 0, NULL);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       ret = cdn_dp_mailbox_response(dp, MB_MODULE_ID_DP_TX,
>>> +                                     DPTX_READ_LINK_STAT, status, 10);
>>
>> s/10/sizeof(status)/
>>
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       dp->link.rate = status[0];
>>> +       dp->link.num_lanes = status[1];
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active)
>>> +{
>>> +       u8 msg;
>>> +
>>> +       msg = !!active;
>>> +
>>> +       return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
>>> DPTX_SET_VIDEO,
>>> +                                  1, &msg);
>>
>> s/1/sizeof(msg)/
>>
>>> +}
>>> +
>>> +static int cdn_dp_get_msa_misc(struct video_info *video,
>>> +                              struct drm_display_mode *mode)
>>> +{
>>> +       u8 val0, val1;
>>
>> u8 val[2];
>>
>>> +       u32 msa_misc;
>>> +
>>> +       switch (video->color_fmt) {
>>> +       case PXL_RGB:
>>> +       case Y_ONLY:
>>> +               val0 = 0;
>>> +               break;
>>
>> Perhaps this would be better as listed under the default case.
>>
>>> +       case YCBCR_4_4_4:
>>> +               /* set default color space conversion to BT601 */
>>> +               val0 = 6 + BT_601 * 8;
>>> +               break;
>>> +       case YCBCR_4_2_2:
>>> +               /* set default color space conversion to BT601 */
>>> +               val0 = 5 + BT_601 * 8;
>>> +               break;
>>> +       case YCBCR_4_2_0:
>>> +               val0 = 5;
>>> +               break;
>>> +       };
>>> +
>>> +       switch (video->color_depth) {
>>> +       case 6:
>>> +               val1 = 0;
>>> +               break;
>>> +       case 8:
>>> +               val1 = 1;
>>> +               break;
>>> +       case 10:
>>> +               val1 = 2;
>>> +               break;
>>> +       case 12:
>>> +               val1 = 3;
>>> +               break;
>>> +       case 16:
>>> +               val1 = 4;
>>> +               break;
>>> +       };
>>> +
>>> +       msa_misc = 2 * val0 + 32 * val1 +
>>> +                  ((video->color_fmt == Y_ONLY) ? (1 << 14) : 0);
>>
>> Perhaps a stupid question, but is this documented anywhere? If not,
>> could you add a comment describing what you're doing?
>>
>>> +
>>> +       return msa_misc;
>>> +}
>>> +
>>> +int cdn_dp_config_video(struct cdn_dp_device *dp)
>>> +{
>>> +       struct video_info *video = &dp->video_info;
>>> +       struct drm_display_mode *mode = &dp->mode;
>>> +       u64 symbol;
>>> +       u32 val, link_rate;
>>> +       u8 bit_per_pix;
>>> +       u8 tu_size_reg = TU_SIZE;
>>> +       int ret;
>>> +
>>> +       bit_per_pix = (video->color_fmt == YCBCR_4_2_2) ?
>>> +                     (video->color_depth * 2) : (video->color_depth *
>>> 3);
>>> +
>>> +       link_rate = drm_dp_bw_code_to_link_rate(dp->link.rate);
>>> +
>>> +       val = VIF_BYPASS_INTERLACE;
>>> +
>>
>> remove extra line
>>
>>> +       ret = cdn_dp_reg_write(dp, BND_HSYNC2VSYNC, val);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       ret = cdn_dp_reg_write(dp, HSYNC2VSYNC_POL_CTRL, 0);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       /* get a best tu_size and symbol */
>>> +       do {
>>> +               tu_size_reg += 2;
>>> +               symbol = tu_size_reg * mode->clock * bit_per_pix;
>>> +               symbol /= dp->link.num_lanes * link_rate * 8;
>>> +       } while ((symbol == 1) || (tu_size_reg - symbol < 4));
>>> +
>>> +       val = symbol + (tu_size_reg << 8);
>>> +
>>
>> remove extra line
>>
>>> +       ret = cdn_dp_reg_write(dp, DP_FRAMER_TU, val);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       switch (video->color_depth) {
>>> +       case 6:
>>> +               val = BCS_6;
>>> +               break;
>>> +       case 8:
>>> +               val = BCS_8;
>>> +               break;
>>> +       case 10:
>>> +               val = BCS_10;
>>> +               break;
>>> +       case 12:
>>> +               val = BCS_12;
>>> +               break;
>>> +       case 16:
>>> +               val = BCS_16;
>>> +               break;
>>> +       };
>>> +
>>> +       val += video->color_fmt << 8;
>>> +       ret = cdn_dp_reg_write(dp, DP_FRAMER_PXL_REPR, val);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       val = video->h_sync_polarity ? DP_FRAMER_SP_HSP : 0;
>>> +       val |= video->v_sync_polarity ? DP_FRAMER_SP_VSP : 0;
>>> +       ret = cdn_dp_reg_write(dp, DP_FRAMER_SP, val);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       val = (mode->hsync_start - mode->hdisplay) << 16;
>>> +       val |= mode->htotal - mode->hsync_end;
>>> +       ret = cdn_dp_reg_write(dp, DP_FRONT_BACK_PORCH, val);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       val = mode->hdisplay * bit_per_pix / 8;
>>> +       ret = cdn_dp_reg_write(dp, DP_BYTE_COUNT, val);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       val = mode->htotal | ((mode->htotal - mode->hsync_start) << 16);
>>> +       ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_0, val);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       val = mode->hsync_end - mode->hsync_start;
>>> +       val |= (mode->hdisplay << 16) | (video->h_sync_polarity << 15);
>>> +       ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_1, val);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       val = mode->vtotal;
>>> +       val |= ((mode->vtotal - mode->vsync_start) << 16);
>>> +
>>
>> remove extra line
>>
>>> +       ret = cdn_dp_reg_write(dp, MSA_VERTICAL_0, val);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       val = mode->vsync_end - mode->vsync_start;
>>> +       val |= mode->vdisplay << 16;
>>> +       val |= (video->v_sync_polarity << 15);
>>
>> Make this consistent with how you assigned val for MSA_HORIZONTAL_1
>> (ie: split both or combine both)
>>
>>> +       ret = cdn_dp_reg_write(dp, MSA_VERTICAL_1, val);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       val = cdn_dp_get_msa_misc(video, mode);
>>> +       ret = cdn_dp_reg_write(dp, MSA_MISC, val);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       ret = cdn_dp_reg_write(dp, STREAM_CONFIG, 1);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       val = mode->hsync_end - mode->hsync_start;
>>> +       val |= (mode->hdisplay << 16);
>>> +       ret = cdn_dp_reg_write(dp, DP_HORIZONTAL, val);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       val = mode->vtotal;
>>> +       val -= (mode->vtotal - mode->vdisplay);
>>> +       val |= (mode->vtotal - mode->vsync_start) << 16;
>>> +
>>
>> extra line
>>
>>> +       ret = cdn_dp_reg_write(dp, DP_VERTICAL_0, val);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       val = mode->vtotal;
>>> +       ret = cdn_dp_reg_write(dp, DP_VERTICAL_1, val);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       val =  0;
>>> +       return cdn_dp_reg_write_bit(dp, DP_VB_ID, 2, 1, val);
>>> +}
>>> +
>>> +int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info
>>> *audio)
>>> +{
>>> +       int ret = cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL,
>>> AUDIO_PACK_EN(0));
>>> +
>>
>> remove this empty line
>>
>>> +       if (ret)
>>> +               return ret;
>>
>> insert empy line
>>
>>> +       writel(0x1F0707, dp->regs + SPDIF_CTRL_ADDR);
>>
>> pull this magic value into a #define
>>
>>> +       writel(0, dp->regs + AUDIO_SRC_CNTL);
>>> +       writel(0, dp->regs + AUDIO_SRC_CNFG);
>>> +       writel(1, dp->regs + AUDIO_SRC_CNTL);
>>> +       writel(0, dp->regs + AUDIO_SRC_CNTL);
>>> +
>>> +       writel(0, dp->regs + SMPL2PKT_CNTL);
>>> +       writel(1, dp->regs + SMPL2PKT_CNTL);
>>> +       writel(0, dp->regs + SMPL2PKT_CNTL);
>>> +
>>> +       writel(1, dp->regs + FIFO_CNTL);
>>> +       writel(0, dp->regs + FIFO_CNTL);
>>
>> Can you document why you need to toggle these bits as opposed to just
>> setting them to 0?
>>
>>> +
>>> +       if (audio->format == AFMT_SPDIF)
>>> +               clk_disable_unprepare(dp->spdif_clk);
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable)
>>> +{
>>> +       return cdn_dp_reg_write_bit(dp, DP_VB_ID, 4, 1, enable);
>>> +}
>>> +
>>> +int cdn_dp_audio_config_set(struct cdn_dp_device *dp, struct audio_info
>>> *audio)
>>> +{
>>> +       int lanes_param, i2s_port_en_val, val, i;
>>
>> int lanes_param = 3, i2s_port_en_val = 0xf, i;
>>
>>> +       int ret;


From seanpaul at chromium.org  Thu Jul 14 07:46:57 2016
From: seanpaul at chromium.org (Sean Paul)
Date: Thu, 14 Jul 2016 10:46:57 -0400
Subject: [PATCH v4 1/4] drm/rockchip: vop: export line flag function
In-Reply-To: <1468469744-12592-1-git-send-email-ykk@rock-chips.com>
References: <1468469704-12440-1-git-send-email-ykk@rock-chips.com>
 <1468469744-12592-1-git-send-email-ykk@rock-chips.com>
Message-ID: <20160714144657.GA13857@seanpaul0.roam.corp.google.com>

On Thu, Jul 14, 2016 at 12:15:44PM +0800, Yakir Yang wrote:
> VOP have integrated a hardware counter which indicate the exact display
> line that vop is scanning. And if we're interested in a specific line,
> we can set the line number to vop line_flag register, and then vop would
> generate a line_flag interrupt for it.
> 
> For example eDP PSR function is interested in the vertical blanking
> period, then driver could set the line number to zero.
> 
> This patch have exported a symbol that allow other driver to listen the
> line flag event with given timeout limit:
> -  rockchip_drm_wait_line_flag()
> 
> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
> ---
> Changes in v4:
> - Avoid the weird behavior in rockchip_drm_wait_line_flag(). (Sean)
> - Make line_flag_num_x to an array. (Sean)
> - Remove the unused vop_cfg_done() in vop_line_flag_irq_enable(). (Stephane, reviewed in Google gerrit)
>     [https://chromium-review.googlesource.com/#/c/349084/33/drivers/gpu/drm/rockchip/rockchip_drm_vop.c at 466]
> 
> Changes in v3:
> - Export the 'rockchip_drm_wait_line_flag' symbol, and document it.
> - Add 'line_flag_num_0' for RK3288/RK3036
> - Remove the notify for waiting line_flag event (Daniel)
> 
> Changes in v2:
> - Introduce in v2, split VOP line flag changes out
> 
>  drivers/gpu/drm/rockchip/rockchip_drm_drv.h |   3 +
>  drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 118 ++++++++++++++++++++++++++++
>  drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   2 +
>  drivers/gpu/drm/rockchip/rockchip_vop_reg.c |   4 +
>  4 files changed, 127 insertions(+)
> 
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> index ea39329..239b830 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> @@ -70,4 +70,7 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
>  				   struct device *dev);
>  void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
>  				    struct device *dev);
> +int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
> +				unsigned int mstimeout);
> +
>  #endif /* _ROCKCHIP_DRM_DRV_H_ */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> index c8a62a8..69d32f1 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> @@ -121,6 +121,8 @@ struct vop {
>  	/* protected by dev->event_lock */
>  	struct drm_pending_vblank_event *event;
>  
> +	struct completion line_flag_completion;
> +
>  	const struct vop_data *data;
>  
>  	uint32_t *regsbak;
> @@ -431,6 +433,71 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop)
>  	spin_unlock_irqrestore(&vop->irq_lock, flags);
>  }
>  
> +/*
> + * (1) each frame starts at the start of the Vsync pulse which is signaled by
> + *     the "FRAME_SYNC" interrupt.
> + * (2) the active data region of each frame ends at dsp_vact_end
> + * (3) we should program this same number (dsp_vact_end) into dsp_line_frag_num,
> + *      to get "LINE_FLAG" interrupt at the end of the active on screen data.
> + *
> + * VOP_INTR_CTRL0.dsp_line_frag_num = VOP_DSP_VACT_ST_END.dsp_vact_end
> + * Interrupts
> + * LINE_FLAG -------------------------------+
> + * FRAME_SYNC ----+                         |
> + *                |                         |
> + *                v                         v
> + *                | Vsync | Vbp |  Vactive  | Vfp |
> + *                        ^     ^           ^     ^
> + *                        |     |           |     |
> + *                        |     |           |     |
> + * dsp_vs_end ------------+     |           |     |   VOP_DSP_VTOTAL_VS_END
> + * dsp_vact_start --------------+           |     |   VOP_DSP_VACT_ST_END
> + * dsp_vact_end ----------------------------+     |   VOP_DSP_VACT_ST_END
> + * dsp_total -------------------------------------+   VOP_DSP_VTOTAL_VS_END
> + */
> +static bool vop_line_flag_irq_is_enabled(struct vop *vop)
> +{
> +	uint32_t line_flag_irq;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&vop->irq_lock, flags);
> +
> +	line_flag_irq = VOP_INTR_GET_TYPE(vop, enable, LINE_FLAG_INTR);
> +
> +	spin_unlock_irqrestore(&vop->irq_lock, flags);
> +
> +	return !!line_flag_irq;
> +}
> +
> +static void vop_line_flag_irq_enable(struct vop *vop, int line_num)
> +{
> +	unsigned long flags;
> +
> +	if (WARN_ON(!vop->is_enabled))
> +		return;
> +
> +	spin_lock_irqsave(&vop->irq_lock, flags);
> +
> +	VOP_CTRL_SET(vop, line_flag_num[0], line_num);
> +	VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1);
> +
> +	spin_unlock_irqrestore(&vop->irq_lock, flags);
> +}
> +
> +static void vop_line_flag_irq_disable(struct vop *vop)
> +{
> +	unsigned long flags;
> +
> +	if (WARN_ON(!vop->is_enabled))
> +		return;
> +
> +	spin_lock_irqsave(&vop->irq_lock, flags);
> +
> +	VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 0);
> +
> +	spin_unlock_irqrestore(&vop->irq_lock, flags);
> +}
> +
>  static void vop_enable(struct drm_crtc *crtc)
>  {
>  	struct vop *vop = to_vop(crtc);
> @@ -1157,6 +1224,13 @@ static irqreturn_t vop_isr(int irq, void *data)
>  		ret = IRQ_HANDLED;
>  	}
>  
> +	if (active_irqs & LINE_FLAG_INTR) {
> +		if (!completion_done(&vop->line_flag_completion))
> +			complete(&vop->line_flag_completion);

You still have the completion_done/complete here.

If you just call complete() without checking if it's done, you'll remove the
race I mentioned in my first review.

Sean

> +		active_irqs &= ~LINE_FLAG_INTR;
> +		ret = IRQ_HANDLED;
> +	}
> +
>  	if (active_irqs & FS_INTR) {
>  		drm_crtc_handle_vblank(crtc);
>  		vop_handle_vblank(vop);
> @@ -1255,6 +1329,7 @@ static int vop_create_crtc(struct vop *vop)
>  
>  	init_completion(&vop->dsp_hold_completion);
>  	init_completion(&vop->wait_update_complete);
> +	init_completion(&vop->line_flag_completion);
>  	crtc->port = port;
>  	rockchip_register_crtc_funcs(crtc, &private_crtc_funcs);
>  
> @@ -1411,6 +1486,49 @@ static void vop_win_init(struct vop *vop)
>  	}
>  }
>  
> +/**
> + * rockchip_drm_wait_line_flag - acqiure the give line flag event
> + * @crtc: CRTC to enable line flag
> + * @line_num: interested line number
> + * @mstimeout: millisecond for timeout
> + *
> + * Driver would hold here until the interested line flag interrupt have
> + * happened or timeout to wait.
> + *
> + * Returns:
> + * Zero on success, negative errno on failure.
> + */
> +int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
> +				unsigned int mstimeout)
> +{
> +	struct vop *vop = to_vop(crtc);
> +	unsigned long jiffies_left;
> +
> +	if (!crtc || !vop->is_enabled)
> +		return -ENODEV;
> +
> +	if (line_num > crtc->mode.vtotal || mstimeout <= 0)
> +		return -EINVAL;
> +
> +	if (vop_line_flag_irq_is_enabled(vop))
> +		return -EBUSY;
> +
> +	reinit_completion(&vop->line_flag_completion);
> +	vop_line_flag_irq_enable(vop, line_num);
> +
> +	jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion,
> +						   msecs_to_jiffies(mstimeout));
> +	vop_line_flag_irq_disable(vop);
> +
> +	if (jiffies_left == 0) {
> +		dev_err(vop->dev, "Timeout waiting for IRQ\n");
> +		return -ETIMEDOUT;
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(rockchip_drm_wait_line_flag);
> +
>  static int vop_bind(struct device *dev, struct device *master, void *data)
>  {
>  	struct platform_device *pdev = to_platform_device(dev);
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> index ff4f52e..1dbc526 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> @@ -61,6 +61,8 @@ struct vop_ctrl {
>  	struct vop_reg hpost_st_end;
>  	struct vop_reg vpost_st_end;
>  
> +	struct vop_reg line_flag_num[2];
> +
>  	struct vop_reg cfg_done;
>  };
>  
> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> index 6f42e56..eea8427 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> @@ -122,6 +122,7 @@ static const struct vop_ctrl rk3036_ctrl_data = {
>  	.hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0),
>  	.vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
>  	.vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0),
> +	.line_flag_num[0] = VOP_REG(RK3036_INT_STATUS, 0xfff, 12),
>  	.cfg_done = VOP_REG(RK3036_REG_CFG_DONE, 0x1, 0),
>  };
>  
> @@ -221,6 +222,7 @@ static const struct vop_ctrl rk3288_ctrl_data = {
>  	.vact_st_end = VOP_REG(RK3288_DSP_VACT_ST_END, 0x1fff1fff, 0),
>  	.hpost_st_end = VOP_REG(RK3288_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
>  	.vpost_st_end = VOP_REG(RK3288_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
> +	.line_flag_num[0] = VOP_REG(RK3288_INTR_CTRL0, 0x1fff, 12),
>  	.cfg_done = VOP_REG(RK3288_REG_CFG_DONE, 0x1, 0),
>  };
>  
> @@ -299,6 +301,8 @@ static const struct vop_ctrl rk3399_ctrl_data = {
>  	.vact_st_end = VOP_REG(RK3399_DSP_VACT_ST_END, 0x1fff1fff, 0),
>  	.hpost_st_end = VOP_REG(RK3399_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
>  	.vpost_st_end = VOP_REG(RK3399_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
> +	.line_flag_num[0] = VOP_REG(RK3399_LINE_FLAG, 0xffff, 0),
> +	.line_flag_num[1] = VOP_REG(RK3399_LINE_FLAG, 0xffff, 16),
>  	.cfg_done = VOP_REG_MASK(RK3399_REG_CFG_DONE, 0x1, 0),
>  };
>  
> -- 
> 1.9.1
> 
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel


From seanpaul at chromium.org  Thu Jul 14 08:14:33 2016
From: seanpaul at chromium.org (Sean Paul)
Date: Thu, 14 Jul 2016 11:14:33 -0400
Subject: [PATCH v4 2/4] drm/rockchip: add an common abstracted PSR driver
In-Reply-To: <1468469749-12636-1-git-send-email-ykk@rock-chips.com>
References: <1468469704-12440-1-git-send-email-ykk@rock-chips.com>
 <1468469749-12636-1-git-send-email-ykk@rock-chips.com>
Message-ID: <20160714151433.GB13857@seanpaul0.roam.corp.google.com>

On Thu, Jul 14, 2016 at 12:15:49PM +0800, Yakir Yang wrote:
> The PSR driver have exported four symbols for specific device driver:
> - rockchip_drm_psr_register()
> - rockchip_drm_psr_unregister()
> - rockchip_drm_psr_enable()
> - rockchip_drm_psr_disable()
> - rockchip_drm_psr_flush()
> 
> Encoder driver should call the register/unregister interfaces to hook
> itself into common PSR driver, encoder have implement the 'psr_set'
> callback which use the set PSR state in hardware side.
> 
> Crtc driver would call the enable/disable interfaces when vblank is
> enable/disable, after that the common PSR driver would call the encoder
> registered callback to set the PSR state.
> 
> Fb driver would call the flush interface in 'fb->dirty' callback, this
> helper function would force all PSR enabled encoders to exit from PSR
> for 3 seconds.
> 
> Signed-off-by: Yakir Yang <ykk at rock-chips.com>


I still don't think it's a good idea to pull this out into a separate PSR
driver, but I suppose we can integrate it at a later time if it becomes
cumbersome.

Reviewed-by: Sean Paul <seanpaul at chromium.org>


> ---
> Changes in v4:
> - Tuck the global "psr_list" & "psr_list_mutex" in struct rockchip_drm_private. (Sean)
> - Move the access of "psr->state" under "psr->state_mutex"'s protect. (Sean)
> - Let "psr->state = PSR_FLUSH" under "psr->state_mutex"'s protect. (Sean)
> - Collect psr_enable() and psr_disable() into psr_set_state()
> - s/5\ second/PSR_FLUSH_TIMEOUT/ (Sean)
> - Flush the psr callback in vop_crtc_disable(). (St?phane, reviewed in Google gerrit)
>     [https://chromium-review.googlesource.com/#/c/349084/6/drivers/gpu/drm/rockchip/rockchip_drm_vop.c at 475]
> - Add the missing file head with license. (St?phane, reviewed in Google gerrit)
>     [https://chromium-review.googlesource.com/#/c/357563/1/drivers/gpu/drm/rockchip/rockchip_drm_psr.h at 3]
> 
> Changes in v3:
> - split the psr flow into an common abstracted PSR driver
> - implement the 'fb->dirty' callback function (Daniel)
> - avoid to use notify to acqiure for vact event (Daniel)
> - remove psr_active() callback which introduce in v2
> 
> Changes in v2: None
> 
>  drivers/gpu/drm/rockchip/Makefile           |   2 +-
>  drivers/gpu/drm/rockchip/rockchip_drm_drv.c |   4 +
>  drivers/gpu/drm/rockchip/rockchip_drm_drv.h |   3 +
>  drivers/gpu/drm/rockchip/rockchip_drm_fb.c  |  12 ++
>  drivers/gpu/drm/rockchip/rockchip_drm_psr.c | 223 ++++++++++++++++++++++++++++
>  drivers/gpu/drm/rockchip/rockchip_drm_psr.h |  26 ++++
>  drivers/gpu/drm/rockchip/rockchip_drm_vop.c |  29 ++++
>  7 files changed, 298 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.c
>  create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.h
> 
> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
> index 05d0713..9746365 100644
> --- a/drivers/gpu/drm/rockchip/Makefile
> +++ b/drivers/gpu/drm/rockchip/Makefile
> @@ -3,7 +3,7 @@
>  # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
>  
>  rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
> -		rockchip_drm_gem.o rockchip_drm_vop.o
> +		rockchip_drm_gem.o rockchip_drm_psr.o rockchip_drm_vop.o
>  rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
>  
>  obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> index d665fb0..26c12b3 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> @@ -156,6 +156,9 @@ static int rockchip_drm_bind(struct device *dev)
>  
>  	drm_dev->dev_private = private;
>  
> +	INIT_LIST_HEAD(&private->psr_list);
> +	mutex_init(&private->psr_list_mutex);
> +
>  	drm_mode_config_init(drm_dev);
>  
>  	rockchip_drm_mode_config_init(drm_dev);
> @@ -218,6 +221,7 @@ static int rockchip_drm_bind(struct device *dev)
>  
>  	if (is_support_iommu)
>  		arm_iommu_release_mapping(mapping);
> +
>  	return 0;
>  err_fbdev_fini:
>  	rockchip_drm_fbdev_fini(drm_dev);
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> index 239b830..9c34c9e 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> @@ -61,6 +61,9 @@ struct rockchip_drm_private {
>  	struct drm_gem_object *fbdev_bo;
>  	const struct rockchip_crtc_funcs *crtc_funcs[ROCKCHIP_MAX_CRTC];
>  	struct drm_atomic_state *state;
> +
> +	struct list_head psr_list;
> +	struct mutex psr_list_mutex;
>  };
>  
>  int rockchip_register_crtc_funcs(struct drm_crtc *crtc,
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> index 20f12bc..36afd9c 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> @@ -21,6 +21,7 @@
>  
>  #include "rockchip_drm_drv.h"
>  #include "rockchip_drm_gem.h"
> +#include "rockchip_drm_psr.h"
>  
>  #define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
>  
> @@ -66,9 +67,20 @@ static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
>  				     rockchip_fb->obj[0], handle);
>  }
>  
> +static int rockchip_drm_fb_dirty(struct drm_framebuffer *fb,
> +				 struct drm_file *file,
> +				 unsigned int flags, unsigned int color,
> +				 struct drm_clip_rect *clips,
> +				 unsigned int num_clips)
> +{
> +	rockchip_drm_psr_flush(fb->dev);
> +	return 0;
> +}
> +
>  static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
>  	.destroy	= rockchip_drm_fb_destroy,
>  	.create_handle	= rockchip_drm_fb_create_handle,
> +	.dirty		= rockchip_drm_fb_dirty,
>  };
>  
>  static struct rockchip_drm_fb *
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> new file mode 100644
> index 0000000..e79ea18
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
> @@ -0,0 +1,223 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author: Yakir Yang <ykk at rock-chips.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * 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 <drm/drmP.h>
> +#include <drm/drm_crtc_helper.h>
> +
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_psr.h"
> +
> +#define PSR_FLUSH_TIMEOUT	msecs_to_jiffies(3000) /* 3 seconds */
> +
> +static LIST_HEAD(psr_list);
> +static DEFINE_MUTEX(psr_list_mutex);
> +
> +enum psr_state {
> +	PSR_FLUSH,
> +	PSR_ENABLE,
> +	PSR_DISABLE,
> +};
> +
> +struct psr_drv {
> +	struct list_head list;
> +	enum psr_state state;
> +	struct mutex state_mutex;
> +
> +	struct timer_list flush_timer;
> +
> +	struct drm_encoder *encoder;
> +	void (*set)(struct drm_encoder *encoder, bool enable);
> +};
> +
> +static struct psr_drv *find_psr_by_crtc(struct drm_crtc *crtc)
> +{
> +	struct rockchip_drm_private *drm_drv = crtc->dev->dev_private;
> +	struct psr_drv *psr;
> +
> +	mutex_lock(&drm_drv->psr_list_mutex);
> +	list_for_each_entry(psr, &drm_drv->psr_list, list) {
> +		if (psr->encoder->crtc == crtc) {
> +			mutex_unlock(&drm_drv->psr_list_mutex);
> +			return psr;
> +		}
> +	}
> +	mutex_unlock(&drm_drv->psr_list_mutex);
> +
> +	return ERR_PTR(-ENODEV);
> +}
> +
> +static void psr_set_state(struct psr_drv *psr, enum psr_state state)
> +{
> +	mutex_lock(&psr->state_mutex);
> +
> +	if (psr->state == state) {
> +		mutex_unlock(&psr->state_mutex);
> +		return;
> +	}
> +
> +	psr->state = state;
> +	switch (state) {
> +	case PSR_ENABLE:
> +		psr->set(psr->encoder, true);
> +		break;
> +
> +	case PSR_DISABLE:
> +	case PSR_FLUSH:
> +		psr->set(psr->encoder, false);
> +		break;
> +	};
> +
> +	mutex_unlock(&psr->state_mutex);
> +}
> +
> +static void psr_flush_handler(unsigned long data)
> +{
> +	struct psr_drv *psr = (struct psr_drv *)data;
> +
> +	if (!psr || psr->state != PSR_FLUSH)
> +		return;
> +
> +	psr_set_state(psr, PSR_ENABLE);
> +}
> +
> +/**
> + * rockchip_drm_psr_enable - enable the encoder PSR which bind to given CRTC
> + * @crtc: CRTC to obtain the PSR encoder
> + *
> + * Returns:
> + * Zero on success, negative errno on failure.
> + */
> +int rockchip_drm_psr_enable(struct drm_crtc *crtc)
> +{
> +	struct psr_drv *psr = find_psr_by_crtc(crtc);
> +
> +	if (IS_ERR(psr))
> +		return PTR_ERR(psr);
> +
> +	psr_set_state(psr, PSR_ENABLE);
> +	return 0;
> +}
> +EXPORT_SYMBOL(rockchip_drm_psr_enable);
> +
> +/**
> + * rockchip_drm_psr_disable - disable the encoder PSR which bind to given CRTC
> + * @crtc: CRTC to obtain the PSR encoder
> + *
> + * Returns:
> + * Zero on success, negative errno on failure.
> + */
> +int rockchip_drm_psr_disable(struct drm_crtc *crtc)
> +{
> +	struct psr_drv *psr = find_psr_by_crtc(crtc);
> +
> +	if (IS_ERR(psr))
> +		return PTR_ERR(psr);
> +
> +	psr_set_state(psr, PSR_DISABLE);
> +	return 0;
> +}
> +EXPORT_SYMBOL(rockchip_drm_psr_disable);
> +
> +/**
> + * rockchip_drm_psr_flush - force to flush all registered PSR encoders
> + * @dev: drm device
> + *
> + * Disable the PSR function for all registered encoders, and then enable the
> + * PSR function back after PSR_FLUSH_TIMEOUT. If encoder PSR state have been
> + * changed during flush time, then keep the state no change after flush
> + * timeout.
> + *
> + * Returns:
> + * Zero on success, negative errno on failure.
> + */
> +void rockchip_drm_psr_flush(struct drm_device *dev)
> +{
> +	struct rockchip_drm_private *drm_drv = dev->dev_private;
> +	struct psr_drv *psr;
> +
> +	mutex_lock(&drm_drv->psr_list_mutex);
> +	list_for_each_entry(psr, &drm_drv->psr_list, list) {
> +		if (psr->state == PSR_DISABLE)
> +			continue;
> +
> +		mod_timer(&psr->flush_timer,
> +			  round_jiffies_up(jiffies + PSR_FLUSH_TIMEOUT));
> +
> +		psr_set_state(psr, PSR_FLUSH);
> +	}
> +	mutex_unlock(&drm_drv->psr_list_mutex);
> +}
> +EXPORT_SYMBOL(rockchip_drm_psr_flush);
> +
> +/**
> + * rockchip_drm_psr_register - register encoder to psr driver
> + * @encoder: encoder that obtain the PSR function
> + * @psr_set: call back to set PSR state
> + *
> + * Returns:
> + * Zero on success, negative errno on failure.
> + */
> +int rockchip_drm_psr_register(struct drm_encoder *encoder,
> +			void (*psr_set)(struct drm_encoder *, bool enable))
> +{
> +	struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
> +	struct psr_drv *psr;
> +
> +	if (!encoder || !psr_set)
> +		return -EINVAL;
> +
> +	psr = kzalloc(sizeof(struct psr_drv), GFP_KERNEL);
> +	if (!psr)
> +		return -ENOMEM;
> +
> +	setup_timer(&psr->flush_timer, psr_flush_handler, (unsigned long)psr);
> +
> +	mutex_init(&psr->state_mutex);
> +
> +	psr->state = PSR_DISABLE;
> +	psr->encoder = encoder;
> +	psr->set = psr_set;
> +
> +	mutex_lock(&drm_drv->psr_list_mutex);
> +	list_add_tail(&psr->list, &drm_drv->psr_list);
> +	mutex_unlock(&drm_drv->psr_list_mutex);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(rockchip_drm_psr_register);
> +
> +/**
> + * rockchip_drm_psr_unregister - unregister encoder to psr driver
> + * @encoder: encoder that obtain the PSR function
> + * @psr_set: call back to set PSR state
> + *
> + * Returns:
> + * Zero on success, negative errno on failure.
> + */
> +void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
> +{
> +	struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
> +	struct psr_drv *psr;
> +
> +	mutex_lock(&drm_drv->psr_list_mutex);
> +	list_for_each_entry(psr, &drm_drv->psr_list, list) {
> +		if (psr->encoder == encoder) {
> +			del_timer(&psr->flush_timer);
> +			list_del(&psr->list);
> +			kfree(psr);
> +		}
> +	}
> +	mutex_unlock(&drm_drv->psr_list_mutex);
> +}
> +EXPORT_SYMBOL(rockchip_drm_psr_unregister);
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.h b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
> new file mode 100644
> index 0000000..c35b688
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
> @@ -0,0 +1,26 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author: Yakir Yang <ykk at rock-chips.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * 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.
> + */
> +
> +#ifndef __ROCKCHIP_DRM_PSR___
> +#define __ROCKCHIP_DRM_PSR___
> +
> +void rockchip_drm_psr_flush(struct drm_device *dev);
> +int rockchip_drm_psr_enable(struct drm_crtc *crtc);
> +int rockchip_drm_psr_disable(struct drm_crtc *crtc);
> +
> +int rockchip_drm_psr_register(struct drm_encoder *encoder,
> +			void (*psr_set)(struct drm_encoder *, bool enable));
> +void rockchip_drm_psr_unregister(struct drm_encoder *encoder);
> +
> +#endif /* __ROCKCHIP_DRM_PSR__ */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> index 69d32f1..e702fb3 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> @@ -34,6 +34,7 @@
>  #include "rockchip_drm_drv.h"
>  #include "rockchip_drm_gem.h"
>  #include "rockchip_drm_fb.h"
> +#include "rockchip_drm_psr.h"
>  #include "rockchip_drm_vop.h"
>  
>  #define __REG_SET_RELAXED(x, off, mask, shift, v, write_mask) \
> @@ -87,6 +88,8 @@
>  #define to_vop_win(x) container_of(x, struct vop_win, base)
>  #define to_vop_plane_state(x) container_of(x, struct vop_plane_state, base)
>  
> +#define VOP_PSR_SET_DELAY_TIME		msecs_to_jiffies(10)
> +
>  struct vop_plane_state {
>  	struct drm_plane_state base;
>  	int format;
> @@ -121,6 +124,9 @@ struct vop {
>  	/* protected by dev->event_lock */
>  	struct drm_pending_vblank_event *event;
>  
> +	bool psr_enabled;
> +	struct delayed_work psr_work;
> +
>  	struct completion line_flag_completion;
>  
>  	const struct vop_data *data;
> @@ -629,6 +635,9 @@ static void vop_crtc_disable(struct drm_crtc *crtc)
>  
>  		crtc->state->event = NULL;
>  	}
> +
> +	vop->psr_enabled = false;
> +	INIT_DELAYED_WORK(&vop->psr_work, vop_psr_work);
>  }
>  
>  static void vop_plane_destroy(struct drm_plane *plane)
> @@ -923,6 +932,16 @@ static const struct drm_plane_funcs vop_plane_funcs = {
>  	.atomic_destroy_state = vop_atomic_plane_destroy_state,
>  };
>  
> +static void vop_psr_work(struct work_struct *work)
> +{
> +	struct vop *vop = container_of(work, typeof(*vop), psr_work.work);
> +
> +	if (vop->psr_enabled)
> +		rockchip_drm_psr_enable(&vop->crtc);
> +	else
> +		rockchip_drm_psr_disable(&vop->crtc);
> +}
> +
>  static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
>  {
>  	struct vop *vop = to_vop(crtc);
> @@ -937,6 +956,9 @@ static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
>  
>  	spin_unlock_irqrestore(&vop->irq_lock, flags);
>  
> +	vop->psr_enabled = false;
> +	schedule_delayed_work(&vop->psr_work, VOP_PSR_SET_DELAY_TIME);
> +
>  	return 0;
>  }
>  
> @@ -953,6 +975,9 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc)
>  	VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0);
>  
>  	spin_unlock_irqrestore(&vop->irq_lock, flags);
> +
> +	vop->psr_enabled = true;
> +	schedule_delayed_work(&vop->psr_work, VOP_PSR_SET_DELAY_TIME);
>  }
>  
>  static void vop_crtc_wait_for_update(struct drm_crtc *crtc)
> @@ -1597,6 +1622,10 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
>  		return ret;
>  
>  	pm_runtime_enable(&pdev->dev);
> +
> +	vop->psr_enabled = false;
> +	INIT_DELAYED_WORK(&vop->psr_work, vop_psr_work);
> +
>  	return 0;
>  }
>  
> -- 
> 1.9.1
> 
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel


From seanpaul at chromium.org  Thu Jul 14 08:23:24 2016
From: seanpaul at chromium.org (Sean Paul)
Date: Thu, 14 Jul 2016 11:23:24 -0400
Subject: [PATCH v4 3/4] drm/bridge: analogix_dp: add the PSR function
 support
In-Reply-To: <1468469753-12679-1-git-send-email-ykk@rock-chips.com>
References: <1468469704-12440-1-git-send-email-ykk@rock-chips.com>
 <1468469753-12679-1-git-send-email-ykk@rock-chips.com>
Message-ID: <20160714152324.GC13857@seanpaul0.roam.corp.google.com>

On Thu, Jul 14, 2016 at 12:15:53PM +0800, Yakir Yang wrote:
> The full name of PSR is Panel Self Refresh, panel device could refresh
> itself with the hardware framebuffer in panel, this would make lots of
> sense to save the power consumption.
> 
> This patch have exported two symbols for platform driver to implement
> the PSR function in hardware side:
> - analogix_dp_active_psr()
> - analogix_dp_inactive_psr()
> 
> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
> ---
> Changes in v4:
> - Downgrade the PSR version print message to debug level. (Sean)
> - Return 'void' instead of 'int' in analogix_dp_enable_sink_psr(). (Sean)
> - Delete the unused read dpcd operations in analogix_dp_enable_sink_psr(). (Sean)
> - Delete the arbitrary usleep_range in analogix_dp_enable_psr_crc. (Sean).
> - Clean up the hardcoded values in analogix_dp_send_psr_spd(). (Sean)
> - Rename "active/inactive" to "enable/disable". (Sean, Dominik)
> - Keep set the PSR_VID_CRC_FLUSH gate in analogix_dp_enable_psr_crc().
> 
> Changes in v3:
> - split analogix_dp_enable_psr(), make it more clearly
>     analogix_dp_detect_sink_psr()
>     analogix_dp_enable_sink_psr()
> - remove some nosie register setting comments
> 
> Changes in v2:
> - introduce in v2, splite the common Analogix DP changes out
> 
>  drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 60 ++++++++++++++++++++++
>  drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |  4 ++
>  drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  | 49 ++++++++++++++++++
>  drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h  | 28 ++++++++++
>  include/drm/bridge/analogix_dp.h                   |  3 ++
>  5 files changed, 144 insertions(+)
> 
> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> index 32715da..1fec91a 100644
> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> @@ -97,6 +97,62 @@ static int analogix_dp_detect_hpd(struct analogix_dp_device *dp)
>  	return 0;
>  }
>  
> +int analogix_dp_enable_psr(struct device *dev)
> +{
> +	struct analogix_dp_device *dp = dev_get_drvdata(dev);
> +
> +	if (!dp->psr_support)
> +		return -EINVAL;
> +
> +	analogix_dp_send_psr_spd(dp, EDP_VSC_PSR_STATE_ACTIVE |
> +				 EDP_VSC_PSR_CRC_VALUES_VALID);
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(analogix_dp_enable_psr);
> +
> +int analogix_dp_disable_psr(struct device *dev)
> +{
> +	struct analogix_dp_device *dp = dev_get_drvdata(dev);
> +
> +	if (!dp->psr_support)
> +		return -EINVAL;
> +
> +	analogix_dp_send_psr_spd(dp, 0);
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(analogix_dp_disable_psr);
> +
> +static bool analogix_dp_detect_sink_psr(struct analogix_dp_device *dp)
> +{
> +	unsigned char psr_version;
> +
> +	analogix_dp_read_byte_from_dpcd(dp, DP_PSR_SUPPORT, &psr_version);
> +	dev_dbg(dp->dev, "Panel PSR version : %x\n", psr_version);
> +
> +	return (psr_version & DP_PSR_IS_SUPPORTED) ? true : false;
> +}
> +
> +static void analogix_dp_enable_sink_psr(struct analogix_dp_device *dp)
> +{
> +	unsigned char psr_en;
> +
> +	/* Disable psr function */
> +	analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
> +	psr_en &= ~DP_PSR_ENABLE;
> +	analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
> +
> +	/* Main-Link transmitter remains active during PSR active states */
> +	psr_en = DP_PSR_MAIN_LINK_ACTIVE | DP_PSR_CRC_VERIFICATION;
> +	analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
> +
> +	/* Enable psr function */
> +	psr_en = DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE |
> +		 DP_PSR_CRC_VERIFICATION;
> +	analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
> +
> +	analogix_dp_enable_psr_crc(dp);
> +}
> +
>  static unsigned char analogix_dp_calc_edid_check_sum(unsigned char *edid_data)
>  {
>  	int i;
> @@ -921,6 +977,10 @@ static void analogix_dp_commit(struct analogix_dp_device *dp)
>  
>  	/* Enable video */
>  	analogix_dp_start_video(dp);
> +
> +	dp->psr_support = analogix_dp_detect_sink_psr(dp);
> +	if (dp->psr_support)
> +		analogix_dp_enable_sink_psr(dp);
>  }
>  
>  int analogix_dp_get_modes(struct drm_connector *connector)
> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
> index b456380..6ca5dde 100644
> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
> @@ -177,6 +177,7 @@ struct analogix_dp_device {
>  	int			hpd_gpio;
>  	bool                    force_hpd;
>  	unsigned char           edid[EDID_BLOCK_LENGTH * 2];
> +	bool			psr_support;
>  
>  	struct analogix_dp_plat_data *plat_data;
>  };
> @@ -278,4 +279,7 @@ int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp);
>  void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp);
>  void analogix_dp_enable_scrambling(struct analogix_dp_device *dp);
>  void analogix_dp_disable_scrambling(struct analogix_dp_device *dp);
> +void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp);
> +void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, int db1);
> +
>  #endif /* _ANALOGIX_DP_CORE_H */
> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
> index 48030f0..26446ae 100644
> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
> @@ -26,6 +26,9 @@
>  #define COMMON_INT_MASK_4	(HOTPLUG_CHG | HPD_LOST | PLUG)
>  #define INT_STA_MASK		INT_HPD
>  
> +static u32 psr_spd_hbx[] = { 0x00, 0x07, 0x02, 0x08 };
> +static u32 psr_spd_pbx[] = { 0x00, 0x16, 0xCE, 0x5D };

These need to be const. Can you also please add a comment explaining where you
got the values from?

> +
>  void analogix_dp_enable_video_mute(struct analogix_dp_device *dp, bool enable)
>  {
>  	u32 reg;
> @@ -1322,3 +1325,49 @@ void analogix_dp_disable_scrambling(struct analogix_dp_device *dp)
>  	reg |= SCRAMBLING_DISABLE;
>  	writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
>  }
> +
> +void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp)
> +{
> +	writel(PSR_VID_CRC_FLUSH | PSR_VID_CRC_ENABLE,
> +	       dp->reg_base + ANALOGIX_DP_CRC_CON);
> +}
> +
> +void analogix_dp_send_psr_spd(struct analogix_dp_device *dp, int db1)
> +{
> +	unsigned int val;
> +	unsigned int i;
> +
> +	/* don't send info frame */
> +	val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
> +	val &= ~IF_EN;
> +	writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
> +
> +	/* configure single frame update mode */
> +	writel(PSR_FRAME_UP_TYPE_BURST | PSR_CRC_SEL_HARDWARE,
> +	       dp->reg_base + ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL);
> +
> +	/* configure VSC HB0~HB3 and PB0~PB3 */
> +	for (i = 0; i < 4; i++)
> +		writel(psr_spd_hbx[i], dp->reg_base + ANALOGIX_DP_SPD_HB + i*4);
> +	for (i = 0; i < 4; i++)
> +		writel(psr_spd_pbx[i], dp->reg_base + ANALOGIX_DP_SPD_PB + i*4);
> +
> +	/* configure DB0 / DB1 values */
> +	writel(0x0, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB0);
> +	writel(db1, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB1);
> +
> +	/* set reuse spd inforframe */
> +	val = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
> +	val |= REUSE_SPD_EN;
> +	writel(val, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
> +
> +	/* mark info frame update */
> +	val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
> +	val = (val | IF_UP) & ~IF_EN;
> +	writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
> +
> +	/* send info frame */
> +	val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
> +	val |= IF_EN;
> +	writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
> +}
> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
> index cdcc6c5..fd232b2 100644
> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
> @@ -22,6 +22,8 @@
>  #define ANALOGIX_DP_VIDEO_CTL_8			0x3C
>  #define ANALOGIX_DP_VIDEO_CTL_10		0x44
>  
> +#define ANALOGIX_DP_SPDIF_AUDIO_CTL_0		0xD8
> +
>  #define ANALOGIX_DP_PLL_REG_1			0xfc
>  #define ANALOGIX_DP_PLL_REG_2			0x9e4
>  #define ANALOGIX_DP_PLL_REG_3			0x9e8
> @@ -30,6 +32,15 @@
>  
>  #define ANALOGIX_DP_PD				0x12c
>  
> +#define ANALOGIX_DP_IF_TYPE			0x244
> +#define ANALOGIX_DP_IF_PKT_DB1			0x254
> +#define ANALOGIX_DP_IF_PKT_DB2			0x258
> +#define ANALOGIX_DP_SPD_HB			0x2F8
> +#define ANALOGIX_DP_SPD_PB			0x308
> +#define ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL	0x318
> +#define ANALOGIX_DP_VSC_SHADOW_DB0		0x31C
> +#define ANALOGIX_DP_VSC_SHADOW_DB1		0x320
> +
>  #define ANALOGIX_DP_LANE_MAP			0x35C
>  
>  #define ANALOGIX_DP_ANALOG_CTL_1		0x370
> @@ -103,6 +114,8 @@
>  
>  #define ANALOGIX_DP_SOC_GENERAL_CTL		0x800
>  
> +#define ANALOGIX_DP_CRC_CON			0x890
> +
>  /* ANALOGIX_DP_TX_SW_RESET */
>  #define RESET_DP_TX				(0x1 << 0)
>  
> @@ -151,6 +164,7 @@
>  #define VID_CHK_UPDATE_TYPE_SHIFT		(4)
>  #define VID_CHK_UPDATE_TYPE_1			(0x1 << 4)
>  #define VID_CHK_UPDATE_TYPE_0			(0x0 << 4)
> +#define REUSE_SPD_EN				(0x1 << 3)
>  
>  /* ANALOGIX_DP_VIDEO_CTL_8 */
>  #define VID_HRES_TH(x)				(((x) & 0xf) << 4)
> @@ -167,6 +181,12 @@
>  #define REF_CLK_27M				(0x0 << 0)
>  #define REF_CLK_MASK				(0x1 << 0)
>  
> +/* ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL */
> +#define PSR_FRAME_UP_TYPE_BURST			(0x1 << 0)
> +#define PSR_FRAME_UP_TYPE_SINGLE		(0x0 << 0)
> +#define PSR_CRC_SEL_HARDWARE			(0x1 << 1)
> +#define PSR_CRC_SEL_MANUALLY			(0x0 << 1)
> +
>  /* ANALOGIX_DP_LANE_MAP */
>  #define LANE3_MAP_LOGIC_LANE_0			(0x0 << 6)
>  #define LANE3_MAP_LOGIC_LANE_1			(0x1 << 6)
> @@ -376,4 +396,12 @@
>  #define VIDEO_MODE_SLAVE_MODE			(0x1 << 0)
>  #define VIDEO_MODE_MASTER_MODE			(0x0 << 0)
>  
> +/* ANALOGIX_DP_PKT_SEND_CTL */
> +#define IF_UP					(0x1 << 4)
> +#define IF_EN					(0x1 << 0)
> +
> +/* ANALOGIX_DP_CRC_CON */
> +#define PSR_VID_CRC_FLUSH			(0x1 << 2)
> +#define PSR_VID_CRC_ENABLE			(0x1 << 0)
> +
>  #endif /* _ANALOGIX_DP_REG_H */
> diff --git a/include/drm/bridge/analogix_dp.h b/include/drm/bridge/analogix_dp.h
> index 261b86d..9cd8838 100644
> --- a/include/drm/bridge/analogix_dp.h
> +++ b/include/drm/bridge/analogix_dp.h
> @@ -38,6 +38,9 @@ struct analogix_dp_plat_data {
>  			 struct drm_connector *);
>  };
>  
> +int analogix_dp_enable_psr(struct device *dev);
> +int analogix_dp_disable_psr(struct device *dev);
> +
>  int analogix_dp_resume(struct device *dev);
>  int analogix_dp_suspend(struct device *dev);
>  
> -- 
> 1.9.1
> 
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel


From seanpaul at chromium.org  Thu Jul 14 08:26:41 2016
From: seanpaul at chromium.org (Sean Paul)
Date: Thu, 14 Jul 2016 11:26:41 -0400
Subject: [PATCH v4 4/4] drm/rockchip: analogix_dp: implement PSR function
In-Reply-To: <1468469758-12722-1-git-send-email-ykk@rock-chips.com>
References: <1468469704-12440-1-git-send-email-ykk@rock-chips.com>
 <1468469758-12722-1-git-send-email-ykk@rock-chips.com>
Message-ID: <20160714152640.GD13857@seanpaul0.roam.corp.google.com>

On Thu, Jul 14, 2016 at 12:15:58PM +0800, Yakir Yang wrote:
> Alway enable the PSR function for Rockchip analogix_dp driver. If panel
> don't support PSR, then the core analogix_dp would ignore this setting.
> 
> Signed-off-by: Yakir Yang <ykk at rock-chips.com>

Reviewed-by: Sean Paul <seanpaul at chromium.org>

> ---
> Changes in v4:
> - Return 'void' instead of 'int' in analogix_dp_psr_set(). (Sean)
> - Pull the 10ms delay time out into a #define. (Sean)
> - Improved the code of analogix_dp_psr_work(). (Sean)
> - Indented with spaces for new numbers in rockchip_dp_device struct. (St?phane, reviewed at Google gerrit)
>     [https://chromium-review.googlesource.com/#/c/349085/33/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c at 83]
> 
> Changes in v3:
> - split the common psr logic into a seperate driver, make this to a
>   simple sub-psr device driver.
> 
> Changes in v2:
> - remove vblank notify out (Daniel)
> - create a psr_active() callback in vop data struct.
> 
>  drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 57 +++++++++++++++++++++++++
>  1 file changed, 57 insertions(+)
> 
> diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
> index e81e19a..aa916f4 100644
> --- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
> +++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
> @@ -32,6 +32,7 @@
>  #include <drm/bridge/analogix_dp.h>
>  
>  #include "rockchip_drm_drv.h"
> +#include "rockchip_drm_psr.h"
>  #include "rockchip_drm_vop.h"
>  
>  #define RK3288_GRF_SOC_CON6		0x25c
> @@ -41,6 +42,9 @@
>  
>  #define HIWORD_UPDATE(val, mask)	(val | (mask) << 16)
>  
> +#define PSR_SET_DELAY_TIME		msecs_to_jiffies(10)
> +#define PSR_WAIT_LINE_FLAG_TIMEOUT_MS	100
> +
>  #define to_dp(nm)	container_of(nm, struct rockchip_dp_device, nm)
>  
>  /**
> @@ -68,11 +72,55 @@ struct rockchip_dp_device {
>  	struct regmap            *grf;
>  	struct reset_control     *rst;
>  
> +	struct delayed_work      psr_work;
> +	unsigned int             psr_state;
> +
>  	const struct rockchip_dp_chip_data *data;
>  
>  	struct analogix_dp_plat_data plat_data;
>  };
>  
> +static void analogix_dp_psr_set(struct drm_encoder *encoder, bool enabled)
> +{
> +	struct rockchip_dp_device *dp = to_dp(encoder);
> +
> +	dev_dbg(dp->dev, "%s PSR...\n", enabled ? "Entry" : "Exit");
> +
> +	if (enabled)
> +		dp->psr_state = EDP_VSC_PSR_STATE_ACTIVE;
> +	else
> +		dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE;
> +
> +	schedule_delayed_work(&dp->psr_work, PSR_SET_DELAY_TIME);
> +}
> +
> +static void analogix_dp_psr_work(struct work_struct *work)
> +{
> +	struct rockchip_dp_device *dp =
> +				container_of(work, typeof(*dp), psr_work.work);
> +	struct drm_crtc *crtc = dp->encoder.crtc;
> +	int psr_state = dp->psr_state;
> +	int vact_end;
> +	int ret;
> +
> +	if (!crtc)
> +		return;
> +
> +	vact_end = crtc->mode.vtotal - crtc->mode.vsync_start + crtc->mode.vdisplay;
> +
> +	ret = rockchip_drm_wait_line_flag(dp->encoder.crtc, vact_end,
> +					  PSR_WAIT_LINE_FLAG_TIMEOUT_MS);
> +	if (ret) {
> +		dev_err(dp->dev, "line flag interrupt did not arrive\n");
> +		return;
> +	}
> +
> +	if (psr_state == EDP_VSC_PSR_STATE_ACTIVE)
> +		analogix_dp_enable_psr(dp->dev);
> +	else
> +		analogix_dp_disable_psr(dp->dev);
> +}
> +
>  static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
>  {
>  	reset_control_assert(dp->rst);
> @@ -340,12 +388,21 @@ static int rockchip_dp_bind(struct device *dev, struct device *master,
>  	dp->plat_data.power_off = rockchip_dp_powerdown;
>  	dp->plat_data.get_modes = rockchip_dp_get_modes;
>  
> +	dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE;
> +	INIT_DELAYED_WORK(&dp->psr_work, analogix_dp_psr_work);
> +
> +	rockchip_drm_psr_register(&dp->encoder, analogix_dp_psr_set);
> +
>  	return analogix_dp_bind(dev, dp->drm_dev, &dp->plat_data);
>  }
>  
>  static void rockchip_dp_unbind(struct device *dev, struct device *master,
>  			       void *data)
>  {
> +	struct rockchip_dp_device *dp = dev_get_drvdata(dev);
> +
> +	rockchip_drm_psr_unregister(&dp->encoder);
> +
>  	return analogix_dp_unbind(dev, master, data);
>  }
>  
> -- 
> 1.9.1
> 
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel


From john at metanate.com  Thu Jul 14 08:29:15 2016
From: john at metanate.com (John Keeping)
Date: Thu, 14 Jul 2016 16:29:15 +0100
Subject: [PATCH] drm/rockchip: allocate correct crtc state structure on reset
Message-ID: <20160714152915.31235-1-john@metanate.com>

Because we are using a custom crtc_state structure, we must override the
reset helper to allocate the correct amount of memory.

Cc: stable at vger.kernel.org
Fixes: 4e257d9eee23 ("drm/rockchip: get rid of rockchip_drm_crtc_mode_config")
Signed-off-by: John Keeping <john at metanate.com>
---
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 1c4d5b5a70a2..21e1247098be 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -1048,6 +1048,17 @@ static void vop_crtc_destroy(struct drm_crtc *crtc)
 	drm_crtc_cleanup(crtc);
 }
 
+static void vop_crtc_reset(struct drm_crtc *crtc)
+{
+	if (crtc->state)
+		__drm_atomic_helper_crtc_destroy_state(crtc, crtc->state);
+	kfree(crtc->state);
+
+	crtc->state = kzalloc(sizeof(struct rockchip_crtc_state), GFP_KERNEL);
+	if (crtc->state)
+		crtc->state->crtc = crtc;
+}
+
 static struct drm_crtc_state *vop_crtc_duplicate_state(struct drm_crtc *crtc)
 {
 	struct rockchip_crtc_state *rockchip_state;
@@ -1073,7 +1084,7 @@ static const struct drm_crtc_funcs vop_crtc_funcs = {
 	.set_config = drm_atomic_helper_set_config,
 	.page_flip = drm_atomic_helper_page_flip,
 	.destroy = vop_crtc_destroy,
-	.reset = drm_atomic_helper_crtc_reset,
+	.reset = vop_crtc_reset,
 	.atomic_duplicate_state = vop_crtc_duplicate_state,
 	.atomic_destroy_state = vop_crtc_destroy_state,
 };
-- 
2.9.0.465.g8850cbc.dirty



From seanpaul at chromium.org  Thu Jul 14 08:35:10 2016
From: seanpaul at chromium.org (Sean Paul)
Date: Thu, 14 Jul 2016 08:35:10 -0700
Subject: [PATCH] drm/rockchip: allocate correct crtc state structure on
 reset
In-Reply-To: <20160714152915.31235-1-john@metanate.com>
References: <20160714152915.31235-1-john@metanate.com>
Message-ID: <CAOw6vbKRTpk=7f+gyAAkdS_r4bw1hyNeYqa1=qXB5_tvOuoBiw@mail.gmail.com>

On Thu, Jul 14, 2016 at 8:29 AM, John Keeping <john at metanate.com> wrote:
> Because we are using a custom crtc_state structure, we must override the
> reset helper to allocate the correct amount of memory.
>
> Cc: stable at vger.kernel.org
> Fixes: 4e257d9eee23 ("drm/rockchip: get rid of rockchip_drm_crtc_mode_config")
> Signed-off-by: John Keeping <john at metanate.com>

Reviewed-by: Sean Paul <seanpaul at chromium.org>

> ---
>  drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 13 ++++++++++++-
>  1 file changed, 12 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> index 1c4d5b5a70a2..21e1247098be 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> @@ -1048,6 +1048,17 @@ static void vop_crtc_destroy(struct drm_crtc *crtc)
>         drm_crtc_cleanup(crtc);
>  }
>
> +static void vop_crtc_reset(struct drm_crtc *crtc)
> +{
> +       if (crtc->state)
> +               __drm_atomic_helper_crtc_destroy_state(crtc, crtc->state);
> +       kfree(crtc->state);
> +
> +       crtc->state = kzalloc(sizeof(struct rockchip_crtc_state), GFP_KERNEL);
> +       if (crtc->state)
> +               crtc->state->crtc = crtc;
> +}
> +
>  static struct drm_crtc_state *vop_crtc_duplicate_state(struct drm_crtc *crtc)
>  {
>         struct rockchip_crtc_state *rockchip_state;
> @@ -1073,7 +1084,7 @@ static const struct drm_crtc_funcs vop_crtc_funcs = {
>         .set_config = drm_atomic_helper_set_config,
>         .page_flip = drm_atomic_helper_page_flip,
>         .destroy = vop_crtc_destroy,
> -       .reset = drm_atomic_helper_crtc_reset,
> +       .reset = vop_crtc_reset,
>         .atomic_duplicate_state = vop_crtc_duplicate_state,
>         .atomic_destroy_state = vop_crtc_destroy_state,
>  };
> --
> 2.9.0.465.g8850cbc.dirty
>
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel


From seanpaul at chromium.org  Thu Jul 14 08:36:32 2016
From: seanpaul at chromium.org (Sean Paul)
Date: Thu, 14 Jul 2016 08:36:32 -0700
Subject: [PATCH 6/6] drm/rockchip: vop: correct rk3036 register define
In-Reply-To: <1468467221-6819-1-git-send-email-mark.yao@rock-chips.com>
References: <1468467090-6108-1-git-send-email-mark.yao@rock-chips.com>
 <1468467221-6819-1-git-send-email-mark.yao@rock-chips.com>
Message-ID: <CAOw6vb+rtH=tUyBY8VdDFRRjAAAMVdFM8by+H14L+C_L4O6g9A@mail.gmail.com>

On Wed, Jul 13, 2016 at 8:33 PM, Mark Yao <mark.yao at rock-chips.com> wrote:
> Signed-off-by: Mark Yao <mark.yao at rock-chips.com>
> Reported-by: Tomasz Figa <tfiga at chromium.org>

Reviewed-by: Sean Paul <seanpaul at chromium.org>

> ---
>  drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> index a348a7a..919992c 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> @@ -190,7 +190,7 @@ static const struct vop_data rk3288_vop = {
>         .win_size = ARRAY_SIZE(rk3288_vop_win_data),
>  };
>
> -static const struct vop_scl_regs rk3066_win_scl = {
> +static const struct vop_scl_regs rk3036_win_scl = {
>         .scale_yrgb_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0),
>         .scale_yrgb_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 16),
>         .scale_cbcr_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0),
> @@ -198,7 +198,7 @@ static const struct vop_scl_regs rk3066_win_scl = {
>  };
>
>  static const struct vop_win_phy rk3036_win0_data = {
> -       .scl = &rk3066_win_scl,
> +       .scl = &rk3036_win_scl,
>         .data_formats = formats_win_full,
>         .nformats = ARRAY_SIZE(formats_win_full),
>         .enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 0),
> --
> 1.9.1
>
>
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel


From shawn.lin at rock-chips.com  Thu Jul 14 18:00:13 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Fri, 15 Jul 2016 09:00:13 +0800
Subject: [PATCH] arm64: dts: rockchip: Add basic support for orion-r68
In-Reply-To: <1468499302-16508-1-git-send-email-matthias.bgg@gmail.com>
References: <1468499302-16508-1-git-send-email-matthias.bgg@gmail.com>
Message-ID: <b6aa3694-1126-3152-5e1f-72368813fdf6@rock-chips.com>

Hi Matthias,

Just some comment for mmc stuff. :)

? 2016/7/14 20:28, Matthias Brugger ??:
> This patch adds basic support for the Tronsmart orion r86 set-top-box.
>
> Signed-off-by: Matthias Brugger <mbrugger at suse.com>
> ---
> Changes since v1:
> - change memory node to 2GB
> - change compatible and file name
> - add uart4
> - add second led
>
>  Documentation/devicetree/bindings/arm/rockchip.txt |   4 +
>  arch/arm64/boot/dts/rockchip/Makefile              |   1 +
>  .../boot/dts/rockchip/rk3368-orion-r68-meta.dts    | 382 +++++++++++++++++++++
>  3 files changed, 387 insertions(+)
>  create mode 100644 arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts
>
> diff --git a/Documentation/devicetree/bindings/arm/rockchip.txt b/Documentation/devicetree/bindings/arm/rockchip.txt
> index 715d960..1d5c65e0 100644
> --- a/Documentation/devicetree/bindings/arm/rockchip.txt
> +++ b/Documentation/devicetree/bindings/arm/rockchip.txt
> @@ -110,3 +110,7 @@ Rockchip platforms device tree bindings
>  - Rockchip RK3399 evb:
>      Required root node properties:
>        - compatible = "rockchip,rk3399-evb", "rockchip,rk3399";
> +
> +- Tronsmart Orion R68 Meta
> +    Required root node properties:
> +      - compatible = "tronsmart,orion-r68-meta", "rockchip,rk3368";
> diff --git a/arch/arm64/boot/dts/rockchip/Makefile b/arch/arm64/boot/dts/rockchip/Makefile
> index 7037a16..87669f6 100644
> --- a/arch/arm64/boot/dts/rockchip/Makefile
> +++ b/arch/arm64/boot/dts/rockchip/Makefile
> @@ -1,5 +1,6 @@
>  dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-evb-act8846.dtb
>  dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-geekbox.dtb
> +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-orion-r68-meta.dtb
>  dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-r88.dtb
>  dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-evb.dtb
>
> diff --git a/arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts b/arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts
> new file mode 100644
> index 0000000..8c8671f
> --- /dev/null
> +++ b/arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts
> @@ -0,0 +1,382 @@
> +/*
> + * Copyright (c) 2016 Matthias Brugger <mbrugger at suse.com>
> + *
> + * This file is dual-licensed: you can use it either under the terms
> + * of the GPL or the X11 license, at your option. Note that this dual
> + * licensing only applies to this file, and not this project as a
> + * whole.
> + *
> + *  a) This file is free software; you can redistribute it and/or
> + *     modify it under the terms of the GNU General Public License as
> + *     published by the Free Software Foundation; either version 2 of the
> + *     License, or (at your option) any later version.
> + *
> + *     This file 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.
> + *
> + * Or, alternatively,
> + *
> + *  b) Permission is hereby granted, free of charge, to any person
> + *     obtaining a copy of this software and associated documentation
> + *     files (the "Software"), to deal in the Software without
> + *     restriction, including without limitation the rights to use,
> + *     copy, modify, merge, publish, distribute, sublicense, and/or
> + *     sell copies of the Software, and to permit persons to whom the
> + *     Software is furnished to do so, subject to the following
> + *     conditions:
> + *
> + *     The above copyright notice and this permission notice shall be
> + *     included in all copies or substantial portions of the Software.
> + *
> + *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
> + *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
> + *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
> + *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + *     OTHER DEALINGS IN THE SOFTWARE.
> + */
> +
> +/dts-v1/;
> +#include "rk3368.dtsi"
> +
> +/ {
> +	model = "Rockchip Orion R68";
> +	compatible = "tronsmart,orion-r68-meta", "rockchip,rk3368";
> +
> +	chosen {
> +		stdout-path = "serial2:115200n8";
> +	};
> +
> +	memory {
> +		device_type = "memory";
> +		reg = <0x0 0x0 0x0 0x80000000>;
> +	};
> +
> +	emmc_pwrseq: emmc-pwrseq {
> +		compatible = "mmc-pwrseq-emmc";
> +		pinctrl-0 = <&emmc_reset>;
> +		pinctrl-names = "default";
> +		reset-gpios = <&gpio2 3 GPIO_ACTIVE_HIGH>;
> +	};
> +
> +	ext_gmac: external-gmac-clock {
> +		compatible = "fixed-clock";
> +		#clock-cells = <0>;
> +		clock-frequency = <125000000>;
> +		clock-output-names = "ext_gmac";
> +	};
> +
> +	keys: gpio-keys {
> +		compatible = "gpio-keys";
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +		pinctrl-names = "default";
> +		pinctrl-0 = <&pwr_key>;
> +
> +		button at 0 {
> +			gpio-key,wakeup = <1>;
> +			gpios = <&gpio0 5 GPIO_ACTIVE_HIGH>;
> +			label = "GPIO Power";
> +			linux,code = <116>;
> +		};
> +	};
> +
> +	leds: gpio-leds {
> +		compatible = "gpio-leds";
> +
> +		red {
> +			gpios = <&gpio3 29 GPIO_ACTIVE_HIGH>;
> +			label = "orion:red:led";
> +			pinctrl-names = "default";
> +			pinctrl-0 = <&led_ctl>;
> +			default-state = "on";
> +		};
> +
> +		blue {
> +			gpios = <&gpio0 12 GPIO_ACTIVE_HIGH>;
> +			label = "orion:blue:led";
> +			pinctrl-names = "default";
> +			pinctrl-0 = <&stby_pwren>;
> +			default-state = "off";
> +		};
> +	};
> +
> +	vcc_18: vcc18-regulator {
> +		compatible = "regulator-fixed";
> +		regulator-name = "vcc_18";
> +		regulator-min-microvolt = <1800000>;
> +		regulator-max-microvolt = <1800000>;
> +		regulator-always-on;
> +		regulator-boot-on;
> +		vin-supply = <&vcc_sys>;
> +	};
> +
> +	/* supplies both host and otg */
> +	vcc_host: vcc-host-regulator {
> +		compatible = "regulator-fixed";
> +		gpio = <&gpio0 4 GPIO_ACTIVE_LOW>;
> +		pinctrl-names = "default";
> +		pinctrl-0 = <&host_vbus_drv>;
> +		regulator-name = "vcc_host";
> +		regulator-always-on;
> +		regulator-boot-on;
> +		vin-supply = <&vcc_sys>;
> +	};
> +
> +	vccio_sd: vcc-io-sd-regulator {
> +		regulator-name= "vccio_sd";
> +		gpio = <&gpio0 9 GPIO_ACTIVE_LOW>;
> +		regulator-min-microvolt = <3300000>;
> +		regulator-max-microvolt = <3300000>;

so, I guess your r68 board won't support sd 3.0?

> +	};
> +
> +	vcc_sd: vcc-sd-regulator {
> +		compatible = "regulator-fixed";
> +		regulator-name = "vcc_sd";
> +		gpio = <&gpio3 11 GPIO_ACTIVE_LOW>;
> +		regulator-min-microvolt = <3300000>;
> +		regulator-max-microvolt = <3300000>;
> +		regulator-always-on;
> +		regulator-boot-on;
> +		vin-supply = <&vcc_io>;
> +	};
> +
> +	vcc_io: vcc-io-regulator {
> +		compatible = "regulator-fixed";
> +		regulator-name = "vcc_io";
> +		regulator-min-microvolt = <3300000>;
> +		regulator-max-microvolt = <3300000>;
> +		regulator-always-on;
> +		regulator-boot-on;
> +		vin-supply = <&vcc_sys>;
> +	};
> +
> +	vcc_lan: vcc-lan-regulator {
> +		compatible = "regulator-fixed";
> +		regulator-name = "vcc_lan";
> +		regulator-min-microvolt = <3300000>;
> +		regulator-max-microvolt = <3300000>;
> +		regulator-always-on;
> +		regulator-boot-on;
> +		vin-supply = <&vcc_io>;
> +	};
> +
> +	vcc_sys: vcc-sys-regulator {
> +		compatible = "regulator-fixed";
> +		regulator-name = "vcc_sys";
> +		regulator-min-microvolt = <5000000>;
> +		regulator-max-microvolt = <5000000>;
> +		regulator-always-on;
> +		regulator-boot-on;
> +	};
> +
> +	vccio_wl: vccio-wl-regulator {
> +		compatible = "regulator-fixed";
> +		regulator-name = "vccio_wl";
> +		regulator-min-microvolt = <3300000>;
> +		regulator-max-microvolt = <3300000>;
> +		regulator-always-on;
> +		regulator-boot-on;
> +		vin-supply = <&vcc_io>;
> +	};
> +
> +	vdd_10: vdd-10-regulator {
> +		compatible = "regulator-fixed";
> +		regulator-name = "vdd_10";
> +		regulator-min-microvolt = <1000000>;
> +		regulator-max-microvolt = <1000000>;
> +		regulator-always-on;
> +		regulator-boot-on;
> +		vin-supply = <&vcc_sys>;
> +	};
> +};
> +
> +&emmc {
> +	bus-width = <8>;
> +	cap-mmc-highspeed;
> +	disable-wp;
> +	mmc-pwrseq = <&emmc_pwrseq>;
> +	non-removable;
> +	num-slots = <1>;
> +	pinctrl-names = "default";
> +	pinctrl-0 = <&emmc_clk &emmc_cmd &emmc_bus8>;
> +	status = "okay";
> +};
> +
> +&gmac {
> +	assigned-clocks = <&cru SCLK_MAC>;
> +	assigned-clock-parents = <&ext_gmac>;
> +	phy-supply = <&vcc_lan>;
> +	phy-mode = "rgmii";
> +	clock_in_out = "input";
> +	snps,reset-gpio = <&gpio3 12 0>;
> +	snps,reset-active-low;
> +	snps,reset-delays-us = <0 10000 1000000>;
> +	pinctrl-names = "default";
> +	pinctrl-0 = <&rgmii_pins>;
> +	tx_delay = <0x30>;
> +	rx_delay = <0x10>;
> +	status = "ok";
> +};
> +
> +&i2c0 {
> +	status = "okay";
> +
> +	vdd_cpu: syr827 at 40 {
> +		compatible = "silergy,syr827";
> +		reg = <0x40>;
> +		fcs,suspend-voltage-selector = <1>;
> +		regulator-name = "vdd_cpu";
> +		regulator-enable-ramp-delay = <300>;
> +		regulator-min-microvolt = <712500>;
> +		regulator-max-microvolt = <1500000>;
> +		regulator-ramp-delay = <8000>;
> +		regulator-always-on;
> +		regulator-boot-on;
> +		vin-supply = <&vcc_sys>;
> +	};
> +
> +	hym8563: hym8563 at 51 {
> +		compatible = "haoyu,hym8563";
> +		reg = <0x51>;
> +		#clock-cells = <0>;
> +		clock-frequency = <32768>;
> +		clock-output-names = "xin32k";
> +		/* rtc_int is not connected */
> +	};
> +};
> +
> +&pinctrl {
> +	pcfg_pull_none_drv_8ma: pcfg-pull-none-drv-8ma {
> +		bias-disable;
> +		drive-strength = <8>;
> +	};
> +
> +	pcfg_pull_up_drv_8ma: pcfg-pull-up-drv-8ma {
> +		bias-pull-up;
> +		drive-strength = <8>;
> +	};
> +
> +	emmc {
> +		emmc_bus8: emmc-bus8 {
> +			rockchip,pins = <1 18 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
> +					<1 19 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
> +					<1 20 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
> +					<1 21 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
> +					<1 22 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
> +					<1 23 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
> +					<1 24 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
> +					<1 25 RK_FUNC_2 &pcfg_pull_up_drv_8ma>;
> +		};
> +
> +		emmc-clk {
> +			rockchip,pins = <2 4 RK_FUNC_2 &pcfg_pull_none_drv_8ma>;
> +		};
> +
> +		emmc-cmd {
> +			rockchip,pins = <1 26 RK_FUNC_2 &pcfg_pull_up_drv_8ma>;
> +		};
> +
> +		emmc_reset: emmc-reset {
> +			rockchip,pins = <2 3 RK_FUNC_GPIO &pcfg_pull_none>;
> +		};
> +	};
> +
> +	keys {
> +		pwr_key: pwr-key {
> +			rockchip,pins = <0 5 RK_FUNC_GPIO &pcfg_pull_down>;
> +		};
> +	};
> +
> +	leds {
> +		stby_pwren: stby-pwren {
> +			rockchip,pins = <0 12 RK_FUNC_GPIO &pcfg_pull_none>;
> +		};
> +
> +		led_ctl: led-ctl {
> +			rockchip,pins = <3 29 RK_FUNC_GPIO &pcfg_pull_none>;
> +		};
> +	};
> +
> +	sdmmc {
> +		sdmmc_clk: sdmmc-clk {
> +			rockchip,pins = <2 9 RK_FUNC_1 &pcfg_pull_none_drv_8ma>;
> +		};
> +
> +		sdmmc_cmd: sdmmc-cmd {
> +			rockchip,pins = <2 10 RK_FUNC_1 &pcfg_pull_up_drv_8ma>;
> +		};
> +
> +		sdmmc_cd: sdmmc-cd {
> +			rockchip,pins = <2 11 RK_FUNC_1 &pcfg_pull_up_drv_8ma>;
> +		};
> +
> +		sdmmc_bus1: sdmmc-bus1 {
> +			rockchip,pins = <2 5 RK_FUNC_1 &pcfg_pull_up_drv_8ma>;
> +		};
> +
> +		sdmmc_bus4: sdmmc-bus4 {
> +			rockchip,pins = <2 5 RK_FUNC_1 &pcfg_pull_up_drv_8ma>,
> +					<2 6 RK_FUNC_1 &pcfg_pull_up_drv_8ma>,
> +					<2 7 RK_FUNC_1 &pcfg_pull_up_drv_8ma>,
> +					<2 8 RK_FUNC_1 &pcfg_pull_up_drv_8ma>;
> +		};
> +	};
> +
> +	usb {
> +		host_vbus_drv: host-vbus-drv {
> +			rockchip,pins = <0 4 RK_FUNC_GPIO &pcfg_pull_none>;
> +		};
> +	};
> +};
> +
> +&saradc {
> +	vref-supply = <&vcc_18>;
> +	status = "okay";
> +};
> +
> +&sdmmc {
> +	clock-frequency = <50000000>;
> +	clock-freq-min-max = <400000 50000000>;
> +	cap-sd-highspeed;
> +	supports-sd;

This's not a upstream property.

> +	broken-cd;

sdmmc controller of rk3368 isn't broken. Could you remove it?


> +	card-detect-delay = <200>;
> +
> +	pinctrl-names = "default";
> +	pinctrl-0 = <&sdmmc_clk &sdmmc_cmd &sdmmc_cd &sdmmc_bus4>;
> +	bus-width = <4>;
> +	num-slots = <1>;
> +	ignore-pm-notify;

ignore-pm-notify is from AOSP but not upstream mmc. You should remove
it.

> +	keep-power-in-suspend;
> +
> +	vmmc-supply = <&vcc_sd>;
> +	vqmmc-supply = <&vccio_sd>;
> +	status = "okay";
> +};
> +
> +&uart2 {
> +	status = "okay";
> +};
> +
> +&uart4 {
> +	status = "okay";
> +	pinctrl-names = "default";
> +	pinctrl-0 = <&uart4_xfer>;
> +};
> +
> +&usb_host0_ehci {
> +	status = "okay";
> +};
> +
> +&usb_otg {
> +	status = "okay";
> +};
> +
> +&wdt {
> +	status = "okay";
> +};
>


-- 
Best Regards
Shawn Lin



From shawn.lin at rock-chips.com  Thu Jul 14 18:10:05 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Fri, 15 Jul 2016 09:10:05 +0800
Subject: [PATCH] arm64: dts: rockchip: remove broken-cd from sdio0
Message-ID: <1468545005-21382-1-git-send-email-shawn.lin@rock-chips.com>

commit 1ade61c141e2 ("arm64: dts: rockchip: remove broken-cd
from emmc and sdio") was intended to remove the abuse of
broken-cd property from mmc. But somehow it forgot to remove
this property from sdio0 node. Let's remove it now.

Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
---

 arch/arm64/boot/dts/rockchip/rk3368-r88.dts | 1 -
 1 file changed, 1 deletion(-)

diff --git a/arch/arm64/boot/dts/rockchip/rk3368-r88.dts b/arch/arm64/boot/dts/rockchip/rk3368-r88.dts
index 82a32e5..eed1ef6 100644
--- a/arch/arm64/boot/dts/rockchip/rk3368-r88.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3368-r88.dts
@@ -248,7 +248,6 @@
 &sdio0 {
 	assigned-clocks = <&cru SCLK_SDIO0>;
 	assigned-clock-parents = <&cru PLL_CPLL>;
-	broken-cd;
 	bus-width = <4>;
 	cap-sd-highspeed;
 	cap-sdio-irq;
-- 
2.3.7




From briannorris at chromium.org  Thu Jul 14 18:24:33 2016
From: briannorris at chromium.org (Brian Norris)
Date: Thu, 14 Jul 2016 18:24:33 -0700
Subject: [PATCH] arm64: dts: rockchip: add spiX aliases for rk3399
Message-ID: <1468545873-134339-1-git-send-email-briannorris@chromium.org>

It's nicer to see this:

    # ls -l /sys/bus/spi/devices/
    total 0
    lrwxrwxrwx 1 root root 0 Dec 31  1969 spi0.0 -> ../../../devices/platform/ff1c0000.spi/spi_master/spi0/spi0.0
    lrwxrwxrwx 1 root root 0 Dec 31  1969 spi1.0 -> ../../../devices/platform/ff1d0000.spi/spi_master/spi1/spi1.0
    lrwxrwxrwx 1 root root 0 Dec 31  1969 spi2.0 -> ../../../devices/platform/ff1e0000.spi/spi_master/spi2/spi2.0
    lrwxrwxrwx 1 root root 0 Dec 31  1969 spi5.0 -> ../../../devices/platform/ff200000.spi/spi_master/spi5/spi5.0

than this:

    # ls -l /sys/bus/spi/devices/
    total 0
    lrwxrwxrwx 1 root root 0 Dec 31  1969 spi32763.0 -> ../../../devices/platform/ff200000.spi/spi_master/spi32763/spi32763.0
    lrwxrwxrwx 1 root root 0 Dec 31  1969 spi32764.0 -> ../../../devices/platform/ff1e0000.spi/spi_master/spi32764/spi32764.0
    lrwxrwxrwx 1 root root 0 Dec 31  1969 spi32765.0 -> ../../../devices/platform/ff1d0000.spi/spi_master/spi32765/spi32765.0
    lrwxrwxrwx 1 root root 0 Dec 31  1969 spi32766.0 -> ../../../devices/platform/ff1c0000.spi/spi_master/spi32766/spi32766.0

With the latter, it's much clearer which bus devices are on.

Signed-off-by: Brian Norris <briannorris at chromium.org>
---
 arch/arm64/boot/dts/rockchip/rk3399.dtsi | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index 4c84229789ef..b04abbfd4898 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -70,6 +70,12 @@
 		serial2 = &uart2;
 		serial3 = &uart3;
 		serial4 = &uart4;
+		spi0 = &spi0;
+		spi1 = &spi1;
+		spi2 = &spi2;
+		spi3 = &spi3;
+		spi4 = &spi4;
+		spi5 = &spi5;
 	};
 
 	cpus {
-- 
2.8.0.rc3.226.g39d4020



From briannorris at chromium.org  Thu Jul 14 18:30:59 2016
From: briannorris at chromium.org (Brian Norris)
Date: Thu, 14 Jul 2016 18:30:59 -0700
Subject: [PATCH] spi: rockchip: limit transfers to (64K - 1) bytes
Message-ID: <1468546259-136659-1-git-send-email-briannorris@chromium.org>

The Rockchip SPI controller's length register only supports 16-bits,
yielding a maximum length of 64KiB (the CTRLR1 register holds "length -
1"). Trying to transfer more than that (e.g., with a large SPI flash
read) will cause the driver to hang.

Now, it seems that while theoretically we should be able to program
CTRLR1 with 0xffff, and get a 64KiB transfer, but that also seems to
cause the core to choke, so stick with a maximum of 64K - 1 bytes --
i.e., 0xffff.

Signed-off-by: Brian Norris <briannorris at chromium.org>
---
 drivers/spi/spi-rockchip.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c
index d840324bcc9f..0f89c2169c24 100644
--- a/drivers/spi/spi-rockchip.c
+++ b/drivers/spi/spi-rockchip.c
@@ -142,6 +142,12 @@
 /* sclk_out: spi master internal logic in rk3x can support 50Mhz */
 #define MAX_SCLK_OUT		50000000
 
+/*
+ * SPI_CTRLR1 is 16-bits, so we should support lengths of 0xffff + 1. However,
+ * the controller seems to hang when given 0x10000, so stick with this for now.
+ */
+#define ROCKCHIP_SPI_MAX_TRANLEN		0xffff
+
 enum rockchip_ssi_type {
 	SSI_MOTO_SPI = 0,
 	SSI_TI_SSP,
@@ -573,6 +579,11 @@ static void rockchip_spi_config(struct rockchip_spi *rs)
 	dev_dbg(rs->dev, "cr0 0x%x, div %d\n", cr0, div);
 }
 
+static size_t rockchip_spi_max_transfer_size(struct spi_device *spi)
+{
+	return ROCKCHIP_SPI_MAX_TRANLEN;
+}
+
 static int rockchip_spi_transfer_one(
 		struct spi_master *master,
 		struct spi_device *spi,
@@ -589,6 +600,11 @@ static int rockchip_spi_transfer_one(
 		return -EINVAL;
 	}
 
+	if (xfer->len > ROCKCHIP_SPI_MAX_TRANLEN) {
+		dev_err(rs->dev, "Transfer is too long (%d)\n", xfer->len);
+		return -EINVAL;
+	}
+
 	rs->speed = xfer->speed_hz;
 	rs->bpw = xfer->bits_per_word;
 	rs->n_bytes = rs->bpw >> 3;
@@ -730,6 +746,7 @@ static int rockchip_spi_probe(struct platform_device *pdev)
 	master->prepare_message = rockchip_spi_prepare_message;
 	master->unprepare_message = rockchip_spi_unprepare_message;
 	master->transfer_one = rockchip_spi_transfer_one;
+	master->max_transfer_size = rockchip_spi_max_transfer_size;
 	master->handle_err = rockchip_spi_handle_err;
 
 	rs->dma_tx.ch = dma_request_chan(rs->dev, "tx");
-- 
2.8.0.rc3.226.g39d4020



From ykk at rock-chips.com  Thu Jul 14 18:43:07 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 15 Jul 2016 09:43:07 +0800
Subject: [PATCH v4 1/4] drm/rockchip: vop: export line flag function
In-Reply-To: <20160714144657.GA13857@seanpaul0.roam.corp.google.com>
References: <1468469704-12440-1-git-send-email-ykk@rock-chips.com>
 <1468469744-12592-1-git-send-email-ykk@rock-chips.com>
 <20160714144657.GA13857@seanpaul0.roam.corp.google.com>
Message-ID: <57883FAB.7040203@rock-chips.com>

Sean,

On 07/14/2016 10:46 PM, Sean Paul wrote:
> On Thu, Jul 14, 2016 at 12:15:44PM +0800, Yakir Yang wrote:
>> VOP have integrated a hardware counter which indicate the exact display
>> line that vop is scanning. And if we're interested in a specific line,
>> we can set the line number to vop line_flag register, and then vop would
>> generate a line_flag interrupt for it.
>>
>> For example eDP PSR function is interested in the vertical blanking
>> period, then driver could set the line number to zero.
>>
>> This patch have exported a symbol that allow other driver to listen the
>> line flag event with given timeout limit:
>> -  rockchip_drm_wait_line_flag()
>>
>> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
>> ---
>> Changes in v4:
>> - Avoid the weird behavior in rockchip_drm_wait_line_flag(). (Sean)
>> - Make line_flag_num_x to an array. (Sean)
>> - Remove the unused vop_cfg_done() in vop_line_flag_irq_enable(). (Stephane, reviewed in Google gerrit)
>>      [https://chromium-review.googlesource.com/#/c/349084/33/drivers/gpu/drm/rockchip/rockchip_drm_vop.c at 466]
>>
>> Changes in v3:
>> - Export the 'rockchip_drm_wait_line_flag' symbol, and document it.
>> - Add 'line_flag_num_0' for RK3288/RK3036
>> - Remove the notify for waiting line_flag event (Daniel)
>>
>> Changes in v2:
>> - Introduce in v2, split VOP line flag changes out
>>
>>   drivers/gpu/drm/rockchip/rockchip_drm_drv.h |   3 +
>>   drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 118 ++++++++++++++++++++++++++++
>>   drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   2 +
>>   drivers/gpu/drm/rockchip/rockchip_vop_reg.c |   4 +
>>   4 files changed, 127 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>> index ea39329..239b830 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>> @@ -70,4 +70,7 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
>>   				   struct device *dev);
>>   void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
>>   				    struct device *dev);
>> +int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
>> +				unsigned int mstimeout);
>> +
>>   #endif /* _ROCKCHIP_DRM_DRV_H_ */
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> index c8a62a8..69d32f1 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> @@ -121,6 +121,8 @@ struct vop {
>>   	/* protected by dev->event_lock */
>>   	struct drm_pending_vblank_event *event;
>>   
>> +	struct completion line_flag_completion;
>> +
>>   	const struct vop_data *data;
>>   
>>   	uint32_t *regsbak;
>> @@ -431,6 +433,71 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop)
>>   	spin_unlock_irqrestore(&vop->irq_lock, flags);
>>   }
>>   
>> +/*
>> + * (1) each frame starts at the start of the Vsync pulse which is signaled by
>> + *     the "FRAME_SYNC" interrupt.
>> + * (2) the active data region of each frame ends at dsp_vact_end
>> + * (3) we should program this same number (dsp_vact_end) into dsp_line_frag_num,
>> + *      to get "LINE_FLAG" interrupt at the end of the active on screen data.
>> + *
>> + * VOP_INTR_CTRL0.dsp_line_frag_num = VOP_DSP_VACT_ST_END.dsp_vact_end
>> + * Interrupts
>> + * LINE_FLAG -------------------------------+
>> + * FRAME_SYNC ----+                         |
>> + *                |                         |
>> + *                v                         v
>> + *                | Vsync | Vbp |  Vactive  | Vfp |
>> + *                        ^     ^           ^     ^
>> + *                        |     |           |     |
>> + *                        |     |           |     |
>> + * dsp_vs_end ------------+     |           |     |   VOP_DSP_VTOTAL_VS_END
>> + * dsp_vact_start --------------+           |     |   VOP_DSP_VACT_ST_END
>> + * dsp_vact_end ----------------------------+     |   VOP_DSP_VACT_ST_END
>> + * dsp_total -------------------------------------+   VOP_DSP_VTOTAL_VS_END
>> + */
>> +static bool vop_line_flag_irq_is_enabled(struct vop *vop)
>> +{
>> +	uint32_t line_flag_irq;
>> +	unsigned long flags;
>> +
>> +	spin_lock_irqsave(&vop->irq_lock, flags);
>> +
>> +	line_flag_irq = VOP_INTR_GET_TYPE(vop, enable, LINE_FLAG_INTR);
>> +
>> +	spin_unlock_irqrestore(&vop->irq_lock, flags);
>> +
>> +	return !!line_flag_irq;
>> +}
>> +
>> +static void vop_line_flag_irq_enable(struct vop *vop, int line_num)
>> +{
>> +	unsigned long flags;
>> +
>> +	if (WARN_ON(!vop->is_enabled))
>> +		return;
>> +
>> +	spin_lock_irqsave(&vop->irq_lock, flags);
>> +
>> +	VOP_CTRL_SET(vop, line_flag_num[0], line_num);
>> +	VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1);
>> +
>> +	spin_unlock_irqrestore(&vop->irq_lock, flags);
>> +}
>> +
>> +static void vop_line_flag_irq_disable(struct vop *vop)
>> +{
>> +	unsigned long flags;
>> +
>> +	if (WARN_ON(!vop->is_enabled))
>> +		return;
>> +
>> +	spin_lock_irqsave(&vop->irq_lock, flags);
>> +
>> +	VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 0);
>> +
>> +	spin_unlock_irqrestore(&vop->irq_lock, flags);
>> +}
>> +
>>   static void vop_enable(struct drm_crtc *crtc)
>>   {
>>   	struct vop *vop = to_vop(crtc);
>> @@ -1157,6 +1224,13 @@ static irqreturn_t vop_isr(int irq, void *data)
>>   		ret = IRQ_HANDLED;
>>   	}
>>   
>> +	if (active_irqs & LINE_FLAG_INTR) {
>> +		if (!completion_done(&vop->line_flag_completion))
>> +			complete(&vop->line_flag_completion);
> You still have the completion_done/complete here.
>
> If you just call complete() without checking if it's done, you'll remove the
> race I mentioned in my first review.

Ah, forget to update this one, will send a new v4.1 patch to fix it.

Thanks a lot,
- Yakir

> Sean
>
>> +		active_irqs &= ~LINE_FLAG_INTR;
>> +		ret = IRQ_HANDLED;
>> +	}
>> +
>>   	if (active_irqs & FS_INTR) {
>>   		drm_crtc_handle_vblank(crtc);
>>   		vop_handle_vblank(vop);
>> @@ -1255,6 +1329,7 @@ static int vop_create_crtc(struct vop *vop)
>>   
>>   	init_completion(&vop->dsp_hold_completion);
>>   	init_completion(&vop->wait_update_complete);
>> +	init_completion(&vop->line_flag_completion);
>>   	crtc->port = port;
>>   	rockchip_register_crtc_funcs(crtc, &private_crtc_funcs);
>>   
>> @@ -1411,6 +1486,49 @@ static void vop_win_init(struct vop *vop)
>>   	}
>>   }
>>   
>> +/**
>> + * rockchip_drm_wait_line_flag - acqiure the give line flag event
>> + * @crtc: CRTC to enable line flag
>> + * @line_num: interested line number
>> + * @mstimeout: millisecond for timeout
>> + *
>> + * Driver would hold here until the interested line flag interrupt have
>> + * happened or timeout to wait.
>> + *
>> + * Returns:
>> + * Zero on success, negative errno on failure.
>> + */
>> +int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
>> +				unsigned int mstimeout)
>> +{
>> +	struct vop *vop = to_vop(crtc);
>> +	unsigned long jiffies_left;
>> +
>> +	if (!crtc || !vop->is_enabled)
>> +		return -ENODEV;
>> +
>> +	if (line_num > crtc->mode.vtotal || mstimeout <= 0)
>> +		return -EINVAL;
>> +
>> +	if (vop_line_flag_irq_is_enabled(vop))
>> +		return -EBUSY;
>> +
>> +	reinit_completion(&vop->line_flag_completion);
>> +	vop_line_flag_irq_enable(vop, line_num);
>> +
>> +	jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion,
>> +						   msecs_to_jiffies(mstimeout));
>> +	vop_line_flag_irq_disable(vop);
>> +
>> +	if (jiffies_left == 0) {
>> +		dev_err(vop->dev, "Timeout waiting for IRQ\n");
>> +		return -ETIMEDOUT;
>> +	}
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL(rockchip_drm_wait_line_flag);
>> +
>>   static int vop_bind(struct device *dev, struct device *master, void *data)
>>   {
>>   	struct platform_device *pdev = to_platform_device(dev);
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>> index ff4f52e..1dbc526 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>> @@ -61,6 +61,8 @@ struct vop_ctrl {
>>   	struct vop_reg hpost_st_end;
>>   	struct vop_reg vpost_st_end;
>>   
>> +	struct vop_reg line_flag_num[2];
>> +
>>   	struct vop_reg cfg_done;
>>   };
>>   
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>> index 6f42e56..eea8427 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>> +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>> @@ -122,6 +122,7 @@ static const struct vop_ctrl rk3036_ctrl_data = {
>>   	.hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0),
>>   	.vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
>>   	.vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0),
>> +	.line_flag_num[0] = VOP_REG(RK3036_INT_STATUS, 0xfff, 12),
>>   	.cfg_done = VOP_REG(RK3036_REG_CFG_DONE, 0x1, 0),
>>   };
>>   
>> @@ -221,6 +222,7 @@ static const struct vop_ctrl rk3288_ctrl_data = {
>>   	.vact_st_end = VOP_REG(RK3288_DSP_VACT_ST_END, 0x1fff1fff, 0),
>>   	.hpost_st_end = VOP_REG(RK3288_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
>>   	.vpost_st_end = VOP_REG(RK3288_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
>> +	.line_flag_num[0] = VOP_REG(RK3288_INTR_CTRL0, 0x1fff, 12),
>>   	.cfg_done = VOP_REG(RK3288_REG_CFG_DONE, 0x1, 0),
>>   };
>>   
>> @@ -299,6 +301,8 @@ static const struct vop_ctrl rk3399_ctrl_data = {
>>   	.vact_st_end = VOP_REG(RK3399_DSP_VACT_ST_END, 0x1fff1fff, 0),
>>   	.hpost_st_end = VOP_REG(RK3399_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
>>   	.vpost_st_end = VOP_REG(RK3399_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
>> +	.line_flag_num[0] = VOP_REG(RK3399_LINE_FLAG, 0xffff, 0),
>> +	.line_flag_num[1] = VOP_REG(RK3399_LINE_FLAG, 0xffff, 16),
>>   	.cfg_done = VOP_REG_MASK(RK3399_REG_CFG_DONE, 0x1, 0),
>>   };
>>   
>> -- 
>> 1.9.1
>>
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel at lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>
>




From ykk at rock-chips.com  Thu Jul 14 18:43:37 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 15 Jul 2016 09:43:37 +0800
Subject: [PATCH v4 2/4] drm/rockchip: add an common abstracted PSR driver
In-Reply-To: <20160714151433.GB13857@seanpaul0.roam.corp.google.com>
References: <1468469704-12440-1-git-send-email-ykk@rock-chips.com>
 <1468469749-12636-1-git-send-email-ykk@rock-chips.com>
 <20160714151433.GB13857@seanpaul0.roam.corp.google.com>
Message-ID: <57883FC9.4050305@rock-chips.com>

Sean,

On 07/14/2016 11:14 PM, Sean Paul wrote:
> On Thu, Jul 14, 2016 at 12:15:49PM +0800, Yakir Yang wrote:
>> The PSR driver have exported four symbols for specific device driver:
>> - rockchip_drm_psr_register()
>> - rockchip_drm_psr_unregister()
>> - rockchip_drm_psr_enable()
>> - rockchip_drm_psr_disable()
>> - rockchip_drm_psr_flush()
>>
>> Encoder driver should call the register/unregister interfaces to hook
>> itself into common PSR driver, encoder have implement the 'psr_set'
>> callback which use the set PSR state in hardware side.
>>
>> Crtc driver would call the enable/disable interfaces when vblank is
>> enable/disable, after that the common PSR driver would call the encoder
>> registered callback to set the PSR state.
>>
>> Fb driver would call the flush interface in 'fb->dirty' callback, this
>> helper function would force all PSR enabled encoders to exit from PSR
>> for 3 seconds.
>>
>> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
>
> I still don't think it's a good idea to pull this out into a separate PSR
> driver, but I suppose we can integrate it at a later time if it becomes
> cumbersome.
>
> Reviewed-by: Sean Paul <seanpaul at chromium.org>

Thanks :-D

- Yakir

>
>> ---
>> Changes in v4:
>> - Tuck the global "psr_list" & "psr_list_mutex" in struct rockchip_drm_private. (Sean)
>> - Move the access of "psr->state" under "psr->state_mutex"'s protect. (Sean)
>> - Let "psr->state = PSR_FLUSH" under "psr->state_mutex"'s protect. (Sean)
>> - Collect psr_enable() and psr_disable() into psr_set_state()
>> - s/5\ second/PSR_FLUSH_TIMEOUT/ (Sean)
>> - Flush the psr callback in vop_crtc_disable(). (St?phane, reviewed in Google gerrit)
>>      [https://chromium-review.googlesource.com/#/c/349084/6/drivers/gpu/drm/rockchip/rockchip_drm_vop.c at 475]
>> - Add the missing file head with license. (St?phane, reviewed in Google gerrit)
>>      [https://chromium-review.googlesource.com/#/c/357563/1/drivers/gpu/drm/rockchip/rockchip_drm_psr.h at 3]
>>
>> Changes in v3:
>> - split the psr flow into an common abstracted PSR driver
>> - implement the 'fb->dirty' callback function (Daniel)
>> - avoid to use notify to acqiure for vact event (Daniel)
>> - remove psr_active() callback which introduce in v2
>>
>> Changes in v2: None
>>
>>   drivers/gpu/drm/rockchip/Makefile           |   2 +-
>>   drivers/gpu/drm/rockchip/rockchip_drm_drv.c |   4 +
>>   drivers/gpu/drm/rockchip/rockchip_drm_drv.h |   3 +
>>   drivers/gpu/drm/rockchip/rockchip_drm_fb.c  |  12 ++
>>   drivers/gpu/drm/rockchip/rockchip_drm_psr.c | 223 ++++++++++++++++++++++++++++
>>   drivers/gpu/drm/rockchip/rockchip_drm_psr.h |  26 ++++
>>   drivers/gpu/drm/rockchip/rockchip_drm_vop.c |  29 ++++
>>   7 files changed, 298 insertions(+), 1 deletion(-)
>>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.c
>>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.h
>>
>> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
>> index 05d0713..9746365 100644
>> --- a/drivers/gpu/drm/rockchip/Makefile
>> +++ b/drivers/gpu/drm/rockchip/Makefile
>> @@ -3,7 +3,7 @@
>>   # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
>>   
>>   rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
>> -		rockchip_drm_gem.o rockchip_drm_vop.o
>> +		rockchip_drm_gem.o rockchip_drm_psr.o rockchip_drm_vop.o
>>   rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
>>   
>>   obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
>> index d665fb0..26c12b3 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
>> @@ -156,6 +156,9 @@ static int rockchip_drm_bind(struct device *dev)
>>   
>>   	drm_dev->dev_private = private;
>>   
>> +	INIT_LIST_HEAD(&private->psr_list);
>> +	mutex_init(&private->psr_list_mutex);
>> +
>>   	drm_mode_config_init(drm_dev);
>>   
>>   	rockchip_drm_mode_config_init(drm_dev);
>> @@ -218,6 +221,7 @@ static int rockchip_drm_bind(struct device *dev)
>>   
>>   	if (is_support_iommu)
>>   		arm_iommu_release_mapping(mapping);
>> +
>>   	return 0;
>>   err_fbdev_fini:
>>   	rockchip_drm_fbdev_fini(drm_dev);
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>> index 239b830..9c34c9e 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>> @@ -61,6 +61,9 @@ struct rockchip_drm_private {
>>   	struct drm_gem_object *fbdev_bo;
>>   	const struct rockchip_crtc_funcs *crtc_funcs[ROCKCHIP_MAX_CRTC];
>>   	struct drm_atomic_state *state;
>> +
>> +	struct list_head psr_list;
>> +	struct mutex psr_list_mutex;
>>   };
>>   
>>   int rockchip_register_crtc_funcs(struct drm_crtc *crtc,
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>> index 20f12bc..36afd9c 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>> @@ -21,6 +21,7 @@
>>   
>>   #include "rockchip_drm_drv.h"
>>   #include "rockchip_drm_gem.h"
>> +#include "rockchip_drm_psr.h"
>>   
>>   #define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
>>   
>> @@ -66,9 +67,20 @@ static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
>>   				     rockchip_fb->obj[0], handle);
>>   }
>>   
>> +static int rockchip_drm_fb_dirty(struct drm_framebuffer *fb,
>> +				 struct drm_file *file,
>> +				 unsigned int flags, unsigned int color,
>> +				 struct drm_clip_rect *clips,
>> +				 unsigned int num_clips)
>> +{
>> +	rockchip_drm_psr_flush(fb->dev);
>> +	return 0;
>> +}
>> +
>>   static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
>>   	.destroy	= rockchip_drm_fb_destroy,
>>   	.create_handle	= rockchip_drm_fb_create_handle,
>> +	.dirty		= rockchip_drm_fb_dirty,
>>   };
>>   
>>   static struct rockchip_drm_fb *
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
>> new file mode 100644
>> index 0000000..e79ea18
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
>> @@ -0,0 +1,223 @@
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + * Author: Yakir Yang <ykk at rock-chips.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * 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 <drm/drmP.h>
>> +#include <drm/drm_crtc_helper.h>
>> +
>> +#include "rockchip_drm_drv.h"
>> +#include "rockchip_drm_psr.h"
>> +
>> +#define PSR_FLUSH_TIMEOUT	msecs_to_jiffies(3000) /* 3 seconds */
>> +
>> +static LIST_HEAD(psr_list);
>> +static DEFINE_MUTEX(psr_list_mutex);
>> +
>> +enum psr_state {
>> +	PSR_FLUSH,
>> +	PSR_ENABLE,
>> +	PSR_DISABLE,
>> +};
>> +
>> +struct psr_drv {
>> +	struct list_head list;
>> +	enum psr_state state;
>> +	struct mutex state_mutex;
>> +
>> +	struct timer_list flush_timer;
>> +
>> +	struct drm_encoder *encoder;
>> +	void (*set)(struct drm_encoder *encoder, bool enable);
>> +};
>> +
>> +static struct psr_drv *find_psr_by_crtc(struct drm_crtc *crtc)
>> +{
>> +	struct rockchip_drm_private *drm_drv = crtc->dev->dev_private;
>> +	struct psr_drv *psr;
>> +
>> +	mutex_lock(&drm_drv->psr_list_mutex);
>> +	list_for_each_entry(psr, &drm_drv->psr_list, list) {
>> +		if (psr->encoder->crtc == crtc) {
>> +			mutex_unlock(&drm_drv->psr_list_mutex);
>> +			return psr;
>> +		}
>> +	}
>> +	mutex_unlock(&drm_drv->psr_list_mutex);
>> +
>> +	return ERR_PTR(-ENODEV);
>> +}
>> +
>> +static void psr_set_state(struct psr_drv *psr, enum psr_state state)
>> +{
>> +	mutex_lock(&psr->state_mutex);
>> +
>> +	if (psr->state == state) {
>> +		mutex_unlock(&psr->state_mutex);
>> +		return;
>> +	}
>> +
>> +	psr->state = state;
>> +	switch (state) {
>> +	case PSR_ENABLE:
>> +		psr->set(psr->encoder, true);
>> +		break;
>> +
>> +	case PSR_DISABLE:
>> +	case PSR_FLUSH:
>> +		psr->set(psr->encoder, false);
>> +		break;
>> +	};
>> +
>> +	mutex_unlock(&psr->state_mutex);
>> +}
>> +
>> +static void psr_flush_handler(unsigned long data)
>> +{
>> +	struct psr_drv *psr = (struct psr_drv *)data;
>> +
>> +	if (!psr || psr->state != PSR_FLUSH)
>> +		return;
>> +
>> +	psr_set_state(psr, PSR_ENABLE);
>> +}
>> +
>> +/**
>> + * rockchip_drm_psr_enable - enable the encoder PSR which bind to given CRTC
>> + * @crtc: CRTC to obtain the PSR encoder
>> + *
>> + * Returns:
>> + * Zero on success, negative errno on failure.
>> + */
>> +int rockchip_drm_psr_enable(struct drm_crtc *crtc)
>> +{
>> +	struct psr_drv *psr = find_psr_by_crtc(crtc);
>> +
>> +	if (IS_ERR(psr))
>> +		return PTR_ERR(psr);
>> +
>> +	psr_set_state(psr, PSR_ENABLE);
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL(rockchip_drm_psr_enable);
>> +
>> +/**
>> + * rockchip_drm_psr_disable - disable the encoder PSR which bind to given CRTC
>> + * @crtc: CRTC to obtain the PSR encoder
>> + *
>> + * Returns:
>> + * Zero on success, negative errno on failure.
>> + */
>> +int rockchip_drm_psr_disable(struct drm_crtc *crtc)
>> +{
>> +	struct psr_drv *psr = find_psr_by_crtc(crtc);
>> +
>> +	if (IS_ERR(psr))
>> +		return PTR_ERR(psr);
>> +
>> +	psr_set_state(psr, PSR_DISABLE);
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL(rockchip_drm_psr_disable);
>> +
>> +/**
>> + * rockchip_drm_psr_flush - force to flush all registered PSR encoders
>> + * @dev: drm device
>> + *
>> + * Disable the PSR function for all registered encoders, and then enable the
>> + * PSR function back after PSR_FLUSH_TIMEOUT. If encoder PSR state have been
>> + * changed during flush time, then keep the state no change after flush
>> + * timeout.
>> + *
>> + * Returns:
>> + * Zero on success, negative errno on failure.
>> + */
>> +void rockchip_drm_psr_flush(struct drm_device *dev)
>> +{
>> +	struct rockchip_drm_private *drm_drv = dev->dev_private;
>> +	struct psr_drv *psr;
>> +
>> +	mutex_lock(&drm_drv->psr_list_mutex);
>> +	list_for_each_entry(psr, &drm_drv->psr_list, list) {
>> +		if (psr->state == PSR_DISABLE)
>> +			continue;
>> +
>> +		mod_timer(&psr->flush_timer,
>> +			  round_jiffies_up(jiffies + PSR_FLUSH_TIMEOUT));
>> +
>> +		psr_set_state(psr, PSR_FLUSH);
>> +	}
>> +	mutex_unlock(&drm_drv->psr_list_mutex);
>> +}
>> +EXPORT_SYMBOL(rockchip_drm_psr_flush);
>> +
>> +/**
>> + * rockchip_drm_psr_register - register encoder to psr driver
>> + * @encoder: encoder that obtain the PSR function
>> + * @psr_set: call back to set PSR state
>> + *
>> + * Returns:
>> + * Zero on success, negative errno on failure.
>> + */
>> +int rockchip_drm_psr_register(struct drm_encoder *encoder,
>> +			void (*psr_set)(struct drm_encoder *, bool enable))
>> +{
>> +	struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
>> +	struct psr_drv *psr;
>> +
>> +	if (!encoder || !psr_set)
>> +		return -EINVAL;
>> +
>> +	psr = kzalloc(sizeof(struct psr_drv), GFP_KERNEL);
>> +	if (!psr)
>> +		return -ENOMEM;
>> +
>> +	setup_timer(&psr->flush_timer, psr_flush_handler, (unsigned long)psr);
>> +
>> +	mutex_init(&psr->state_mutex);
>> +
>> +	psr->state = PSR_DISABLE;
>> +	psr->encoder = encoder;
>> +	psr->set = psr_set;
>> +
>> +	mutex_lock(&drm_drv->psr_list_mutex);
>> +	list_add_tail(&psr->list, &drm_drv->psr_list);
>> +	mutex_unlock(&drm_drv->psr_list_mutex);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL(rockchip_drm_psr_register);
>> +
>> +/**
>> + * rockchip_drm_psr_unregister - unregister encoder to psr driver
>> + * @encoder: encoder that obtain the PSR function
>> + * @psr_set: call back to set PSR state
>> + *
>> + * Returns:
>> + * Zero on success, negative errno on failure.
>> + */
>> +void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
>> +{
>> +	struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
>> +	struct psr_drv *psr;
>> +
>> +	mutex_lock(&drm_drv->psr_list_mutex);
>> +	list_for_each_entry(psr, &drm_drv->psr_list, list) {
>> +		if (psr->encoder == encoder) {
>> +			del_timer(&psr->flush_timer);
>> +			list_del(&psr->list);
>> +			kfree(psr);
>> +		}
>> +	}
>> +	mutex_unlock(&drm_drv->psr_list_mutex);
>> +}
>> +EXPORT_SYMBOL(rockchip_drm_psr_unregister);
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.h b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
>> new file mode 100644
>> index 0000000..c35b688
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
>> @@ -0,0 +1,26 @@
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + * Author: Yakir Yang <ykk at rock-chips.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * 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.
>> + */
>> +
>> +#ifndef __ROCKCHIP_DRM_PSR___
>> +#define __ROCKCHIP_DRM_PSR___
>> +
>> +void rockchip_drm_psr_flush(struct drm_device *dev);
>> +int rockchip_drm_psr_enable(struct drm_crtc *crtc);
>> +int rockchip_drm_psr_disable(struct drm_crtc *crtc);
>> +
>> +int rockchip_drm_psr_register(struct drm_encoder *encoder,
>> +			void (*psr_set)(struct drm_encoder *, bool enable));
>> +void rockchip_drm_psr_unregister(struct drm_encoder *encoder);
>> +
>> +#endif /* __ROCKCHIP_DRM_PSR__ */
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> index 69d32f1..e702fb3 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> @@ -34,6 +34,7 @@
>>   #include "rockchip_drm_drv.h"
>>   #include "rockchip_drm_gem.h"
>>   #include "rockchip_drm_fb.h"
>> +#include "rockchip_drm_psr.h"
>>   #include "rockchip_drm_vop.h"
>>   
>>   #define __REG_SET_RELAXED(x, off, mask, shift, v, write_mask) \
>> @@ -87,6 +88,8 @@
>>   #define to_vop_win(x) container_of(x, struct vop_win, base)
>>   #define to_vop_plane_state(x) container_of(x, struct vop_plane_state, base)
>>   
>> +#define VOP_PSR_SET_DELAY_TIME		msecs_to_jiffies(10)
>> +
>>   struct vop_plane_state {
>>   	struct drm_plane_state base;
>>   	int format;
>> @@ -121,6 +124,9 @@ struct vop {
>>   	/* protected by dev->event_lock */
>>   	struct drm_pending_vblank_event *event;
>>   
>> +	bool psr_enabled;
>> +	struct delayed_work psr_work;
>> +
>>   	struct completion line_flag_completion;
>>   
>>   	const struct vop_data *data;
>> @@ -629,6 +635,9 @@ static void vop_crtc_disable(struct drm_crtc *crtc)
>>   
>>   		crtc->state->event = NULL;
>>   	}
>> +
>> +	vop->psr_enabled = false;
>> +	INIT_DELAYED_WORK(&vop->psr_work, vop_psr_work);
>>   }
>>   
>>   static void vop_plane_destroy(struct drm_plane *plane)
>> @@ -923,6 +932,16 @@ static const struct drm_plane_funcs vop_plane_funcs = {
>>   	.atomic_destroy_state = vop_atomic_plane_destroy_state,
>>   };
>>   
>> +static void vop_psr_work(struct work_struct *work)
>> +{
>> +	struct vop *vop = container_of(work, typeof(*vop), psr_work.work);
>> +
>> +	if (vop->psr_enabled)
>> +		rockchip_drm_psr_enable(&vop->crtc);
>> +	else
>> +		rockchip_drm_psr_disable(&vop->crtc);
>> +}
>> +
>>   static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
>>   {
>>   	struct vop *vop = to_vop(crtc);
>> @@ -937,6 +956,9 @@ static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
>>   
>>   	spin_unlock_irqrestore(&vop->irq_lock, flags);
>>   
>> +	vop->psr_enabled = false;
>> +	schedule_delayed_work(&vop->psr_work, VOP_PSR_SET_DELAY_TIME);
>> +
>>   	return 0;
>>   }
>>   
>> @@ -953,6 +975,9 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc)
>>   	VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0);
>>   
>>   	spin_unlock_irqrestore(&vop->irq_lock, flags);
>> +
>> +	vop->psr_enabled = true;
>> +	schedule_delayed_work(&vop->psr_work, VOP_PSR_SET_DELAY_TIME);
>>   }
>>   
>>   static void vop_crtc_wait_for_update(struct drm_crtc *crtc)
>> @@ -1597,6 +1622,10 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
>>   		return ret;
>>   
>>   	pm_runtime_enable(&pdev->dev);
>> +
>> +	vop->psr_enabled = false;
>> +	INIT_DELAYED_WORK(&vop->psr_work, vop_psr_work);
>> +
>>   	return 0;
>>   }
>>   
>> -- 
>> 1.9.1
>>
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel at lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>
>




From shawn.lin at rock-chips.com  Thu Jul 14 18:56:59 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Fri, 15 Jul 2016 09:56:59 +0800
Subject: [PATCH] spi: rockchip: limit transfers to (64K - 1) bytes
In-Reply-To: <1468546259-136659-1-git-send-email-briannorris@chromium.org>
References: <1468546259-136659-1-git-send-email-briannorris@chromium.org>
Message-ID: <131afa39-4287-0fb8-61f4-146283dc7cf0@rock-chips.com>

+ huibing

? 2016/7/15 9:30, Brian Norris ??:
> The Rockchip SPI controller's length register only supports 16-bits,
> yielding a maximum length of 64KiB (the CTRLR1 register holds "length -
> 1"). Trying to transfer more than that (e.g., with a large SPI flash
> read) will cause the driver to hang.

Brian, are you using dma or pio?

 From huibing's feedback for rk3399, you can see...

root at rk3399:/ # echo read 0 1 0x10001 > /sys/slt/SPI.0/SPI.0 

[148360.004332] f0041200:       DMAMOV CCR 0x804200
[148360.004772] f0041206:       DMAMOV SAR 0xff1d0800
[148360.005161] f004120c:       DMAMOV DAR 0xc9f60000
[148360.005549] f0041212:       DMALP_0 255
[148360.005866] f0041214:       DMALP_1 255
[148360.006178] f0041216:       DMAWFPB 13
[148360.006483] f0041218:       DMALDPB 13
[148360.006788] f004121a:       DMASTA
[148360.007061] f004121b:       DMAFLUSHP 13
[148360.007380] f004121d:       DMALPENDA_1 bjmpto_7
[148360.007763] f004121f:       DMALPENDA_0 bjmpto_b
[148360.008144] f0041221:       DMAWFPB 13
[148360.008448] f0041223:       DMALDPB 13
[148360.008752] f0041225:       DMASTA
[148360.009026] f0041226:       DMAFLUSHP 13
[148360.009344] f0041228:       DMASEV 1
[148360.009633] f004122a:       DMAEND
[148360.147993] slt_spi_test spi32766.0: SPI transfer timed out
[148360.148653] spi read 65537*1 cost 144493us speed:453KB/S
root at rk3399:/ #
root at rk3399:/ # echo read 0 1 0x10000 > /sys/slt/SPI.0/SPI.0 

[148365.172429] f0041200:       DMAMOV CCR 0x804200
[148365.172863] f0041206:       DMAMOV SAR 0xff1d0800
[148365.173252] f004120c:       DMAMOV DAR 0xc9f90000
[148365.173641] f0041212:       DMALP_0 255
[148365.173958] f0041214:       DMALP_1 255
[148365.174270] f0041216:       DMAWFPB 13
[148365.174574] f0041218:       DMALDPB 13
[148365.174878] f004121a:       DMASTA
[148365.175152] f004121b:       DMAFLUSHP 13
[148365.175471] f004121d:       DMALPENDA_1 bjmpto_7
[148365.175852] f004121f:       DMALPENDA_0 bjmpto_b
[148365.176234] f0041221:       DMASEV 1
[148365.176522] f0041223:       DMAEND
[148365.207421] spi read 65536*1 cost 35144us speed:1864KB/S



>
> Now, it seems that while theoretically we should be able to program
> CTRLR1 with 0xffff, and get a 64KiB transfer, but that also seems to
> cause the core to choke, so stick with a maximum of 64K - 1 bytes --
> i.e., 0xffff.
>
> Signed-off-by: Brian Norris <briannorris at chromium.org>
> ---
>  drivers/spi/spi-rockchip.c | 17 +++++++++++++++++
>  1 file changed, 17 insertions(+)
>
> diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c
> index d840324bcc9f..0f89c2169c24 100644
> --- a/drivers/spi/spi-rockchip.c
> +++ b/drivers/spi/spi-rockchip.c
> @@ -142,6 +142,12 @@
>  /* sclk_out: spi master internal logic in rk3x can support 50Mhz */
>  #define MAX_SCLK_OUT		50000000
>
> +/*
> + * SPI_CTRLR1 is 16-bits, so we should support lengths of 0xffff + 1. However,
> + * the controller seems to hang when given 0x10000, so stick with this for now.
> + */
> +#define ROCKCHIP_SPI_MAX_TRANLEN		0xffff
> +
>  enum rockchip_ssi_type {
>  	SSI_MOTO_SPI = 0,
>  	SSI_TI_SSP,
> @@ -573,6 +579,11 @@ static void rockchip_spi_config(struct rockchip_spi *rs)
>  	dev_dbg(rs->dev, "cr0 0x%x, div %d\n", cr0, div);
>  }
>
> +static size_t rockchip_spi_max_transfer_size(struct spi_device *spi)
> +{
> +	return ROCKCHIP_SPI_MAX_TRANLEN;
> +}
> +
>  static int rockchip_spi_transfer_one(
>  		struct spi_master *master,
>  		struct spi_device *spi,
> @@ -589,6 +600,11 @@ static int rockchip_spi_transfer_one(
>  		return -EINVAL;
>  	}
>
> +	if (xfer->len > ROCKCHIP_SPI_MAX_TRANLEN) {
> +		dev_err(rs->dev, "Transfer is too long (%d)\n", xfer->len);
> +		return -EINVAL;
> +	}
> +
>  	rs->speed = xfer->speed_hz;
>  	rs->bpw = xfer->bits_per_word;
>  	rs->n_bytes = rs->bpw >> 3;
> @@ -730,6 +746,7 @@ static int rockchip_spi_probe(struct platform_device *pdev)
>  	master->prepare_message = rockchip_spi_prepare_message;
>  	master->unprepare_message = rockchip_spi_unprepare_message;
>  	master->transfer_one = rockchip_spi_transfer_one;
> +	master->max_transfer_size = rockchip_spi_max_transfer_size;
>  	master->handle_err = rockchip_spi_handle_err;
>
>  	rs->dma_tx.ch = dma_request_chan(rs->dev, "tx");
>


-- 
Best Regards
Shawn Lin



From briannorris at chromium.org  Thu Jul 14 19:31:52 2016
From: briannorris at chromium.org (Brian Norris)
Date: Thu, 14 Jul 2016 19:31:52 -0700
Subject: [PATCH] spi: rockchip: limit transfers to (64K - 1) bytes
In-Reply-To: <131afa39-4287-0fb8-61f4-146283dc7cf0@rock-chips.com>
References: <1468546259-136659-1-git-send-email-briannorris@chromium.org>
 <131afa39-4287-0fb8-61f4-146283dc7cf0@rock-chips.com>
Message-ID: <20160715023152.GA145248@google.com>

Hi Shawn,

On Fri, Jul 15, 2016 at 09:56:59AM +0800, Shawn Lin wrote:
> ? 2016/7/15 9:30, Brian Norris ??:
> >The Rockchip SPI controller's length register only supports 16-bits,
> >yielding a maximum length of 64KiB (the CTRLR1 register holds "length -
> >1"). Trying to transfer more than that (e.g., with a large SPI flash
> >read) will cause the driver to hang.
> 
> Brian, are you using dma or pio?

I forgot to mention, I'm using PIO. (The rk3399.dts in linux-next
doesn't even have the 'dma{s,-names}' properties listed for spi[0-5].)

> From huibing's feedback for rk3399, you can see...
> 
> root at rk3399:/ # echo read 0 1 0x10001 > /sys/slt/SPI.0/SPI.0

I don't know what that file is, but presumably it's some kind of bare
SPI test interface you have in your private tree?

> [148360.004332] f0041200:       DMAMOV CCR 0x804200
> [148360.004772] f0041206:       DMAMOV SAR 0xff1d0800
> [148360.005161] f004120c:       DMAMOV DAR 0xc9f60000
> [148360.005549] f0041212:       DMALP_0 255
> [148360.005866] f0041214:       DMALP_1 255
> [148360.006178] f0041216:       DMAWFPB 13
> [148360.006483] f0041218:       DMALDPB 13
> [148360.006788] f004121a:       DMASTA
> [148360.007061] f004121b:       DMAFLUSHP 13
> [148360.007380] f004121d:       DMALPENDA_1 bjmpto_7
> [148360.007763] f004121f:       DMALPENDA_0 bjmpto_b
> [148360.008144] f0041221:       DMAWFPB 13
> [148360.008448] f0041223:       DMALDPB 13
> [148360.008752] f0041225:       DMASTA
> [148360.009026] f0041226:       DMAFLUSHP 13
> [148360.009344] f0041228:       DMASEV 1
> [148360.009633] f004122a:       DMAEND
> [148360.147993] slt_spi_test spi32766.0: SPI transfer timed out
> [148360.148653] spi read 65537*1 cost 144493us speed:453KB/S

And this means a 64KiB + 1 transfer using DMA timed out?

> root at rk3399:/ #
> root at rk3399:/ # echo read 0 1 0x10000 > /sys/slt/SPI.0/SPI.0
> 
> [148365.172429] f0041200:       DMAMOV CCR 0x804200
> [148365.172863] f0041206:       DMAMOV SAR 0xff1d0800
> [148365.173252] f004120c:       DMAMOV DAR 0xc9f90000
> [148365.173641] f0041212:       DMALP_0 255
> [148365.173958] f0041214:       DMALP_1 255
> [148365.174270] f0041216:       DMAWFPB 13
> [148365.174574] f0041218:       DMALDPB 13
> [148365.174878] f004121a:       DMASTA
> [148365.175152] f004121b:       DMAFLUSHP 13
> [148365.175471] f004121d:       DMALPENDA_1 bjmpto_7
> [148365.175852] f004121f:       DMALPENDA_0 bjmpto_b
> [148365.176234] f0041221:       DMASEV 1
> [148365.176522] f0041223:       DMAEND
> [148365.207421] spi read 65536*1 cost 35144us speed:1864KB/S

And a 64KiB transfer using DMA completed successfully?

So maybe there's a driver bug in the PIO path that gives us an
off-by-one error. I poked around a bit and couldn't figure out anything,
so I sent this. Technically, this patch is still valid (even if not
optimal) for the DMA case too...

If you can't figure out what the difference is, and you really don't
want to unnecessarily limit the DMA case, then a last resort hack could
be to say:

static size_t rockchip_spi_max_transfer_size(struct spi_device *device)
{
	if (using dma) // this is obviously pseudocode
		return SZ_64K;
	else
		return SZ_64K - 1;
}

or something like that.

Brian


From shawn.lin at rock-chips.com  Thu Jul 14 19:42:55 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Fri, 15 Jul 2016 10:42:55 +0800
Subject: [PATCH] spi: rockchip: limit transfers to (64K - 1) bytes
In-Reply-To: <20160715023152.GA145248@google.com>
References: <1468546259-136659-1-git-send-email-briannorris@chromium.org>
 <131afa39-4287-0fb8-61f4-146283dc7cf0@rock-chips.com>
 <20160715023152.GA145248@google.com>
Message-ID: <b855036c-86b3-3515-86ae-24de820564ff@rock-chips.com>

? 2016/7/15 10:31, Brian Norris ??:
> Hi Shawn,
>
> On Fri, Jul 15, 2016 at 09:56:59AM +0800, Shawn Lin wrote:
>> ? 2016/7/15 9:30, Brian Norris ??:
>>> The Rockchip SPI controller's length register only supports 16-bits,
>>> yielding a maximum length of 64KiB (the CTRLR1 register holds "length -
>>> 1"). Trying to transfer more than that (e.g., with a large SPI flash
>>> read) will cause the driver to hang.
>>
>> Brian, are you using dma or pio?
>
> I forgot to mention, I'm using PIO. (The rk3399.dts in linux-next
> doesn't even have the 'dma{s,-names}' properties listed for spi[0-5].)

okay.

>
>> From huibing's feedback for rk3399, you can see...
>>
>> root at rk3399:/ # echo read 0 1 0x10001 > /sys/slt/SPI.0/SPI.0
>
> I don't know what that file is, but presumably it's some kind of bare
> SPI test interface you have in your private tree?

yes.

>
>> [148360.004332] f0041200:       DMAMOV CCR 0x804200
>> [148360.004772] f0041206:       DMAMOV SAR 0xff1d0800
>> [148360.005161] f004120c:       DMAMOV DAR 0xc9f60000
>> [148360.005549] f0041212:       DMALP_0 255
>> [148360.005866] f0041214:       DMALP_1 255
>> [148360.006178] f0041216:       DMAWFPB 13
>> [148360.006483] f0041218:       DMALDPB 13
>> [148360.006788] f004121a:       DMASTA
>> [148360.007061] f004121b:       DMAFLUSHP 13
>> [148360.007380] f004121d:       DMALPENDA_1 bjmpto_7
>> [148360.007763] f004121f:       DMALPENDA_0 bjmpto_b
>> [148360.008144] f0041221:       DMAWFPB 13
>> [148360.008448] f0041223:       DMALDPB 13
>> [148360.008752] f0041225:       DMASTA
>> [148360.009026] f0041226:       DMAFLUSHP 13
>> [148360.009344] f0041228:       DMASEV 1
>> [148360.009633] f004122a:       DMAEND
>> [148360.147993] slt_spi_test spi32766.0: SPI transfer timed out
>> [148360.148653] spi read 65537*1 cost 144493us speed:453KB/S
>
> And this means a 64KiB + 1 transfer using DMA timed out?

Right.

>
>> root at rk3399:/ #
>> root at rk3399:/ # echo read 0 1 0x10000 > /sys/slt/SPI.0/SPI.0
>>
>> [148365.172429] f0041200:       DMAMOV CCR 0x804200
>> [148365.172863] f0041206:       DMAMOV SAR 0xff1d0800
>> [148365.173252] f004120c:       DMAMOV DAR 0xc9f90000
>> [148365.173641] f0041212:       DMALP_0 255
>> [148365.173958] f0041214:       DMALP_1 255
>> [148365.174270] f0041216:       DMAWFPB 13
>> [148365.174574] f0041218:       DMALDPB 13
>> [148365.174878] f004121a:       DMASTA
>> [148365.175152] f004121b:       DMAFLUSHP 13
>> [148365.175471] f004121d:       DMALPENDA_1 bjmpto_7
>> [148365.175852] f004121f:       DMALPENDA_0 bjmpto_b
>> [148365.176234] f0041221:       DMASEV 1
>> [148365.176522] f0041223:       DMAEND
>> [148365.207421] spi read 65536*1 cost 35144us speed:1864KB/S
>
> And a 64KiB transfer using DMA completed successfully?

yup.

>
> So maybe there's a driver bug in the PIO path that gives us an
> off-by-one error. I poked around a bit and couldn't figure out anything,
> so I sent this. Technically, this patch is still valid (even if not
> optimal) for the DMA case too...

Huibing is using pio to test this case, and we still achieve it
successfully? That means there may be some minor diff for the pio
path between your chromium repo and my local branch.

>
> If you can't figure out what the difference is, and you really don't
> want to unnecessarily limit the DMA case, then a last resort hack could
> be to say:
>

Could you get spi-rockchip.c of rockchip local kernel 4.4 from Caesar
to see if you could transfer 64KB with the one we're using?


> static size_t rockchip_spi_max_transfer_size(struct spi_device *device)
> {
> 	if (using dma) // this is obviously pseudocode
> 		return SZ_64K;
> 	else
> 		return SZ_64K - 1;
> }
>
> or something like that.
>
> Brian
>
>
>


-- 
Best Regards
Shawn Lin



From mark.yao at rock-chips.com  Thu Jul 14 19:46:38 2016
From: mark.yao at rock-chips.com (Mark yao)
Date: Fri, 15 Jul 2016 10:46:38 +0800
Subject: [PATCH] drm/rockchip: allocate correct crtc state structure on
 reset
In-Reply-To: <20160714152915.31235-1-john@metanate.com>
References: <20160714152915.31235-1-john@metanate.com>
Message-ID: <57884E8E.3000504@rock-chips.com>

On 2016?07?14? 23:29, John Keeping wrote:
> Because we are using a custom crtc_state structure, we must override the
> reset helper to allocate the correct amount of memory.
>
> Cc: stable at vger.kernel.org
> Fixes: 4e257d9eee23 ("drm/rockchip: get rid of rockchip_drm_crtc_mode_config")
> Signed-off-by: John Keeping <john at metanate.com>
> ---
>   drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 13 ++++++++++++-
>   1 file changed, 12 insertions(+), 1 deletion(-)
Thanks for the fixes, Applied to my drm-fixes


> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> index 1c4d5b5a70a2..21e1247098be 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> @@ -1048,6 +1048,17 @@ static void vop_crtc_destroy(struct drm_crtc *crtc)
>   	drm_crtc_cleanup(crtc);
>   }
>   
> +static void vop_crtc_reset(struct drm_crtc *crtc)
> +{
> +	if (crtc->state)
> +		__drm_atomic_helper_crtc_destroy_state(crtc, crtc->state);
> +	kfree(crtc->state);
> +
> +	crtc->state = kzalloc(sizeof(struct rockchip_crtc_state), GFP_KERNEL);
> +	if (crtc->state)
> +		crtc->state->crtc = crtc;
> +}
> +
>   static struct drm_crtc_state *vop_crtc_duplicate_state(struct drm_crtc *crtc)
>   {
>   	struct rockchip_crtc_state *rockchip_state;
> @@ -1073,7 +1084,7 @@ static const struct drm_crtc_funcs vop_crtc_funcs = {
>   	.set_config = drm_atomic_helper_set_config,
>   	.page_flip = drm_atomic_helper_page_flip,
>   	.destroy = vop_crtc_destroy,
> -	.reset = drm_atomic_helper_crtc_reset,
> +	.reset = vop_crtc_reset,
>   	.atomic_duplicate_state = vop_crtc_duplicate_state,
>   	.atomic_destroy_state = vop_crtc_destroy_state,
>   };


-- 
?ark Yao




From briannorris at chromium.org  Thu Jul 14 20:09:00 2016
From: briannorris at chromium.org (Brian Norris)
Date: Thu, 14 Jul 2016 20:09:00 -0700
Subject: [PATCH] spi: rockchip: limit transfers to (64K - 1) bytes
In-Reply-To: <b855036c-86b3-3515-86ae-24de820564ff@rock-chips.com>
References: <1468546259-136659-1-git-send-email-briannorris@chromium.org>
 <131afa39-4287-0fb8-61f4-146283dc7cf0@rock-chips.com>
 <20160715023152.GA145248@google.com>
 <b855036c-86b3-3515-86ae-24de820564ff@rock-chips.com>
Message-ID: <20160715030859.GA3360@google.com>

On Fri, Jul 15, 2016 at 10:42:55AM +0800, Shawn Lin wrote:
> ? 2016/7/15 10:31, Brian Norris ??:
> >So maybe there's a driver bug in the PIO path that gives us an
> >off-by-one error. I poked around a bit and couldn't figure out anything,
> >so I sent this. Technically, this patch is still valid (even if not
> >optimal) for the DMA case too...
> 
> Huibing is using pio to test this case, and we still achieve it
> successfully?

Are you saying Huibing has tested PIO with 64KB transfers, or is that a
question? If the former, it'd be great if he can publish the differences
between his kernel and upstream to produce this.

> That means there may be some minor diff for the pio
> path between your chromium repo and my local branch.

This is an upstream kernel mailing list. I'd expect we can agree to test
on an upstream kernel...

> >want to unnecessarily limit the DMA case, then a last resort hack could
> >be to say:
> >
> 
> Could you get spi-rockchip.c of rockchip local kernel 4.4 from Caesar
> to see if you could transfer 64KB with the one we're using?

I just compared with some (possibly old) copy of your vendor-kernel
driver, and there are a few moderate differences. I didn't test it yet.
I'd think the onus is on you to publish your changes though, if you
think you've fixed things in your own private tree...but I'm willing to
test any changes you recommend.

Brian


From ykk at rock-chips.com  Thu Jul 14 22:45:59 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 15 Jul 2016 13:45:59 +0800
Subject: [PATCH v4 4/4] drm/rockchip: analogix_dp: implement PSR function
In-Reply-To: <20160714152640.GD13857@seanpaul0.roam.corp.google.com>
References: <1468469704-12440-1-git-send-email-ykk@rock-chips.com>
 <1468469758-12722-1-git-send-email-ykk@rock-chips.com>
 <20160714152640.GD13857@seanpaul0.roam.corp.google.com>
Message-ID: <57887897.2090303@rock-chips.com>

Sean,

On 07/14/2016 11:26 PM, Sean Paul wrote:
> On Thu, Jul 14, 2016 at 12:15:58PM +0800, Yakir Yang wrote:
>> Alway enable the PSR function for Rockchip analogix_dp driver. If panel
>> don't support PSR, then the core analogix_dp would ignore this setting.
>>
>> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
> Reviewed-by: Sean Paul <seanpaul at chromium.org>

Thanks :-D

- Yakir

>> ---
>> Changes in v4:
>> - Return 'void' instead of 'int' in analogix_dp_psr_set(). (Sean)
>> - Pull the 10ms delay time out into a #define. (Sean)
>> - Improved the code of analogix_dp_psr_work(). (Sean)
>> - Indented with spaces for new numbers in rockchip_dp_device struct. (St?phane, reviewed at Google gerrit)
>>      [https://chromium-review.googlesource.com/#/c/349085/33/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c at 83]
>>
>> Changes in v3:
>> - split the common psr logic into a seperate driver, make this to a
>>    simple sub-psr device driver.
>>
>> Changes in v2:
>> - remove vblank notify out (Daniel)
>> - create a psr_active() callback in vop data struct.
>>
>>   drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 57 +++++++++++++++++++++++++
>>   1 file changed, 57 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
>> index e81e19a..aa916f4 100644
>> --- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
>> +++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
>> @@ -32,6 +32,7 @@
>>   #include <drm/bridge/analogix_dp.h>
>>   
>>   #include "rockchip_drm_drv.h"
>> +#include "rockchip_drm_psr.h"
>>   #include "rockchip_drm_vop.h"
>>   
>>   #define RK3288_GRF_SOC_CON6		0x25c
>> @@ -41,6 +42,9 @@
>>   
>>   #define HIWORD_UPDATE(val, mask)	(val | (mask) << 16)
>>   
>> +#define PSR_SET_DELAY_TIME		msecs_to_jiffies(10)
>> +#define PSR_WAIT_LINE_FLAG_TIMEOUT_MS	100
>> +
>>   #define to_dp(nm)	container_of(nm, struct rockchip_dp_device, nm)
>>   
>>   /**
>> @@ -68,11 +72,55 @@ struct rockchip_dp_device {
>>   	struct regmap            *grf;
>>   	struct reset_control     *rst;
>>   
>> +	struct delayed_work      psr_work;
>> +	unsigned int             psr_state;
>> +
>>   	const struct rockchip_dp_chip_data *data;
>>   
>>   	struct analogix_dp_plat_data plat_data;
>>   };
>>   
>> +static void analogix_dp_psr_set(struct drm_encoder *encoder, bool enabled)
>> +{
>> +	struct rockchip_dp_device *dp = to_dp(encoder);
>> +
>> +	dev_dbg(dp->dev, "%s PSR...\n", enabled ? "Entry" : "Exit");
>> +
>> +	if (enabled)
>> +		dp->psr_state = EDP_VSC_PSR_STATE_ACTIVE;
>> +	else
>> +		dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE;
>> +
>> +	schedule_delayed_work(&dp->psr_work, PSR_SET_DELAY_TIME);
>> +}
>> +
>> +static void analogix_dp_psr_work(struct work_struct *work)
>> +{
>> +	struct rockchip_dp_device *dp =
>> +				container_of(work, typeof(*dp), psr_work.work);
>> +	struct drm_crtc *crtc = dp->encoder.crtc;
>> +	int psr_state = dp->psr_state;
>> +	int vact_end;
>> +	int ret;
>> +
>> +	if (!crtc)
>> +		return;
>> +
>> +	vact_end = crtc->mode.vtotal - crtc->mode.vsync_start + crtc->mode.vdisplay;
>> +
>> +	ret = rockchip_drm_wait_line_flag(dp->encoder.crtc, vact_end,
>> +					  PSR_WAIT_LINE_FLAG_TIMEOUT_MS);
>> +	if (ret) {
>> +		dev_err(dp->dev, "line flag interrupt did not arrive\n");
>> +		return;
>> +	}
>> +
>> +	if (psr_state == EDP_VSC_PSR_STATE_ACTIVE)
>> +		analogix_dp_enable_psr(dp->dev);
>> +	else
>> +		analogix_dp_disable_psr(dp->dev);
>> +}
>> +
>>   static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
>>   {
>>   	reset_control_assert(dp->rst);
>> @@ -340,12 +388,21 @@ static int rockchip_dp_bind(struct device *dev, struct device *master,
>>   	dp->plat_data.power_off = rockchip_dp_powerdown;
>>   	dp->plat_data.get_modes = rockchip_dp_get_modes;
>>   
>> +	dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE;
>> +	INIT_DELAYED_WORK(&dp->psr_work, analogix_dp_psr_work);
>> +
>> +	rockchip_drm_psr_register(&dp->encoder, analogix_dp_psr_set);
>> +
>>   	return analogix_dp_bind(dev, dp->drm_dev, &dp->plat_data);
>>   }
>>   
>>   static void rockchip_dp_unbind(struct device *dev, struct device *master,
>>   			       void *data)
>>   {
>> +	struct rockchip_dp_device *dp = dev_get_drvdata(dev);
>> +
>> +	rockchip_drm_psr_unregister(&dp->encoder);
>> +
>>   	return analogix_dp_unbind(dev, master, data);
>>   }
>>   
>> -- 
>> 1.9.1
>>
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel at lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>
>




From zyw at rock-chips.com  Thu Jul 14 22:52:17 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Fri, 15 Jul 2016 13:52:17 +0800
Subject: [v5.1 PATCH 5/5] drm/rockchip: cdn-dp: add cdn DP support for rk3399
In-Reply-To: <CAOw6vbKCCoQHO-4TB3qCOuAA09aheXx0H6jRjQQnrkb4KqmtCg@mail.gmail.com>
References: <CAOw6vbKCCoQHO-4TB3qCOuAA09aheXx0H6jRjQQnrkb4KqmtCg@mail.gmail.com>
Message-ID: <1468561937-20527-1-git-send-email-zyw@rock-chips.com>

Add support for cdn DP controller which is embedded in the rk3399
SoCs. The DP is compliant with DisplayPort Specification,
Version 1.3, This IP is compatible with the rockchip type-c PHY IP.
There is a uCPU in DP controller, it need a firmware to work,
please put the firmware file to /lib/firmware/cdn/dptx.bin. The
uCPU in charge of aux communication and link training, the host use
mailbox to communicate with the ucpu.
The dclk pin_pol of vop must not be invert for DP.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>

---

Changes in v5.1:
- modify according to Sean Paul's comments

Changes in v5:
- alphabetical order
- do not use long, use u32 or u64
- return MODE_CLOCK_HIGH when requested > actual
- Optimized Coding Style
- add a formula to get better tu size and symbol value.

Changes in v4:
- use phy framework to control DP phy
- support 2 phys

Changes in v3:
- use EXTCON_DISP_DP and EXTCON_DISP_DP_ALT cable to get dp port state.
- reset spdif before config it
- modify the firmware clk to 100Mhz
- retry load firmware if fw file is requested too early

Changes in v2:
- Alphabetic order
- remove excess error message
- use define clk_rate
- check all return value
- remove dev_set_name(dp->dev, "cdn-dp");
- use schedule_delayed_work
- remove never-called functions
- remove some unnecessary ()

Changes in v1:
- use extcon API
- use hdmi-codec for the DP Asoc
- do not initialize the "ret"
- printk a err log when drm_of_encoder_active_endpoint_id
- modify the dclk pin_pol to a single line

 drivers/gpu/drm/rockchip/Kconfig            |   9 +
 drivers/gpu/drm/rockchip/Makefile           |   1 +
 drivers/gpu/drm/rockchip/cdn-dp-core.c      | 840 ++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/cdn-dp-core.h      |  99 +++
 drivers/gpu/drm/rockchip/cdn-dp-reg.c       | 898 ++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/cdn-dp-reg.h       | 479 +++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c |  11 +-
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   9 +
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c |   2 +
 9 files changed, 2345 insertions(+), 3 deletions(-)
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.c
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.h
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.c
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.h

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index d30bdc3..a04446a 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -25,6 +25,15 @@ config ROCKCHIP_ANALOGIX_DP
 	  for the Analogix Core DP driver. If you want to enable DP
 	  on RK3288 based SoC, you should selet this option.
 
+config ROCKCHIP_CDN_DP
+        tristate "Rockchip cdn DP"
+        depends on DRM_ROCKCHIP
+        help
+	  This selects support for Rockchip SoC specific extensions
+	  for the cdn DP driver. If you want to enable Dp on
+	  RK3399 based SoC, you should select this
+	  option.
+
 config ROCKCHIP_DW_HDMI
         tristate "Rockchip specific extensions for Synopsys DW HDMI"
         depends on DRM_ROCKCHIP
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index 05d0713..abdecd5 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -7,6 +7,7 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
 rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
 
 obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
+obj-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
 obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
 obj-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
 obj-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
new file mode 100644
index 0000000..a425207
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
@@ -0,0 +1,840 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Chris Zhong <zyw at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_of.h>
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/extcon.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/mfd/syscon.h>
+#include <linux/phy/phy.h>
+
+#include <sound/hdmi-codec.h>
+
+#include "cdn-dp-core.h"
+#include "cdn-dp-reg.h"
+#include "rockchip_drm_vop.h"
+
+#define connector_to_dp(c) \
+		container_of(c, struct cdn_dp_device, connector)
+
+#define encoder_to_dp(c) \
+		container_of(c, struct cdn_dp_device, encoder)
+
+/* dp grf register offset */
+#define DP_VOP_SEL		0x6224
+#define DP_SEL_VOP_LIT		BIT(12)
+#define MAX_FW_WAIT_SECS	64
+#define CDN_DP_FIRMWARE		"rockchip/dptx.bin"
+
+struct cdn_dp_data {
+	u8 max_phy;
+};
+
+struct cdn_dp_data rk3399_cdn_dp = {
+	.max_phy = 2,
+};
+
+static const struct of_device_id cdn_dp_dt_ids[] = {
+	{ .compatible = "rockchip,rk3399-cdn-dp",
+		.data = (void *)&rk3399_cdn_dp },
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, cdn_dp_dt_ids);
+
+static int cdn_dp_clk_enable(struct cdn_dp_device *dp)
+{
+	int ret;
+	u32 rate;
+
+	ret = clk_prepare_enable(dp->pclk);
+	if (ret < 0) {
+		dev_err(dp->dev, "cannot enable dp pclk %d\n", ret);
+		goto err_pclk;
+	}
+
+	ret = clk_prepare_enable(dp->core_clk);
+	if (ret < 0) {
+		dev_err(dp->dev, "cannot enable core_clk %d\n", ret);
+		goto err_core_clk;
+	}
+
+	rate = clk_get_rate(dp->core_clk);
+	if (rate < 0) {
+		dev_err(dp->dev, "get clk rate failed: %d\n", rate);
+		goto err_set_rate;
+	}
+
+	cdn_dp_set_fw_clk(dp, rate);
+
+	return 0;
+
+err_set_rate:
+	clk_disable_unprepare(dp->core_clk);
+err_core_clk:
+	clk_disable_unprepare(dp->pclk);
+err_pclk:
+	return ret;
+}
+
+static enum drm_connector_status
+cdn_dp_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct cdn_dp_device *dp = connector_to_dp(connector);
+
+	return dp->hpd_status;
+}
+
+static void cdn_dp_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+static struct drm_connector_funcs cdn_dp_atomic_connector_funcs = {
+	.dpms = drm_atomic_helper_connector_dpms,
+	.detect = cdn_dp_connector_detect,
+	.destroy = cdn_dp_connector_destroy,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int cdn_dp_connector_get_modes(struct drm_connector *connector)
+{
+	struct cdn_dp_device *dp = connector_to_dp(connector);
+	struct edid *edid;
+	int ret = 0;
+
+	edid = drm_do_get_edid(connector, cdn_dp_get_edid_block, dp);
+	if (edid) {
+		dev_dbg(dp->dev, "got edid: width[%d] x height[%d]\n",
+			edid->width_cm, edid->height_cm);
+
+		dp->sink_has_audio = drm_detect_monitor_audio(edid);
+		ret = drm_add_edid_modes(connector, edid);
+		if (ret) {
+			drm_mode_connector_update_edid_property(connector,
+								edid);
+			drm_edid_to_eld(connector, edid);
+		}
+		kfree(edid);
+	} else {
+		dev_warn(dp->dev, "failed to get edid\n");
+	}
+
+	return ret;
+}
+
+static struct drm_encoder *
+	cdn_dp_connector_best_encoder(struct drm_connector *connector)
+{
+	struct cdn_dp_device *dp = connector_to_dp(connector);
+
+	return &dp->encoder;
+}
+
+static int cdn_dp_connector_mode_valid(struct drm_connector *connector,
+				       struct drm_display_mode *mode)
+{
+	struct cdn_dp_device *dp = connector_to_dp(connector);
+	struct drm_display_info *display_info = &dp->connector.display_info;
+	u32 requested = mode->clock * display_info->bpc * 3 / 1000;
+	u32 sink_max, source_max, rate;
+	u8 lanes, id;
+	u32 actual;
+
+	/* find the running port id */
+	for (id = 0; id < dp->max_phy; id++) {
+		if (dp->phy_status[id] == 1)
+			break;
+	}
+
+	sink_max = drm_dp_max_lane_count(dp->dpcd);
+	source_max = dp->cap_lanes[id];
+	lanes = min(sink_max, source_max);
+
+	sink_max = drm_dp_max_link_rate(dp->dpcd);
+	source_max = drm_dp_bw_code_to_link_rate(CDN_DP_MAX_LINK_RATE);
+	rate = min(sink_max, source_max);
+
+	actual = rate * lanes / 100;
+
+	/* efficiency is about 0.8 */
+	actual = actual * 8 / 10;
+
+	if (requested > actual) {
+		dev_dbg(dp->dev, "requested=%d, actual=%d, clock=%d\n",
+			requested, actual, mode->clock);
+		return MODE_CLOCK_HIGH;
+	}
+
+	return MODE_OK;
+}
+
+static struct drm_connector_helper_funcs cdn_dp_connector_helper_funcs = {
+	.get_modes = cdn_dp_connector_get_modes,
+	.best_encoder = cdn_dp_connector_best_encoder,
+	.mode_valid = cdn_dp_connector_mode_valid,
+};
+
+static void cdn_dp_commit(struct drm_encoder *encoder)
+{
+	struct cdn_dp_device *dp = encoder_to_dp(encoder);
+	int ret;
+
+	ret = cdn_dp_training_start(dp);
+	if (ret) {
+		dev_err(dp->dev, "link training failed: %d\n", ret);
+		return;
+	}
+
+	ret = cdn_dp_get_training_status(dp);
+	if (ret) {
+		dev_err(dp->dev, "get link training status failed: %d\n", ret);
+		return;
+	}
+
+	dev_info(dp->dev, "rate:%d, lanes:%d\n",
+		 dp->link.rate, dp->link.num_lanes);
+
+	if (cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE))
+		return;
+
+	if (cdn_dp_config_video(dp)) {
+		dev_err(dp->dev, "unable to config video\n");
+		return;
+	}
+
+	if (cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID))
+		return;
+
+	dp->dpms_mode = DRM_MODE_DPMS_ON;
+}
+
+static void cdn_dp_encoder_mode_set(struct drm_encoder *encoder,
+				    struct drm_display_mode *mode,
+				    struct drm_display_mode *adjusted)
+{
+	struct cdn_dp_device *dp = encoder_to_dp(encoder);
+	struct drm_display_info *display_info = &dp->connector.display_info;
+	struct rockchip_crtc_state *state;
+	struct video_info *video = &dp->video_info;
+	int ret, val;
+
+	switch (display_info->bpc) {
+	case 16:
+	case 12:
+	case 10:
+		video->color_depth = 10;
+		break;
+	case 6:
+		video->color_depth = 6;
+		break;
+	default:
+		video->color_depth = 8;
+		break;
+	}
+
+	video->color_fmt = PXL_RGB;
+
+	video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC);
+	video->h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC);
+
+	ret = drm_of_encoder_active_endpoint_id(dp->dev->of_node, encoder);
+	if (ret < 0) {
+		dev_err(dp->dev, "Could not get vop id, %d", ret);
+		return;
+	}
+
+	state = to_rockchip_crtc_state(encoder->crtc->state);
+	if (ret) {
+		val = DP_SEL_VOP_LIT | (DP_SEL_VOP_LIT << 16);
+		state->output_mode = ROCKCHIP_OUT_MODE_P888;
+	} else {
+		val = DP_SEL_VOP_LIT << 16;
+		state->output_mode = ROCKCHIP_OUT_MODE_AAAA;
+	}
+	ret = regmap_write(dp->grf, DP_VOP_SEL, val);
+	if (ret != 0)
+		dev_err(dp->dev, "Could not write to GRF: %d\n", ret);
+
+	memcpy(&dp->mode, adjusted, sizeof(*mode));
+}
+
+static void cdn_dp_encoder_enable(struct drm_encoder *encoder)
+{
+	struct cdn_dp_device *dp = encoder_to_dp(encoder);
+
+	if (dp->dpms_mode != DRM_MODE_DPMS_ON)
+		cdn_dp_commit(encoder);
+}
+
+static void cdn_dp_encoder_disable(struct drm_encoder *encoder)
+{
+	struct cdn_dp_device *dp = encoder_to_dp(encoder);
+
+	if (dp->dpms_mode != DRM_MODE_DPMS_OFF) {
+		cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE);
+		dp->dpms_mode = DRM_MODE_DPMS_OFF;
+	}
+}
+
+static int
+cdn_dp_encoder_atomic_check(struct drm_encoder *encoder,
+			    struct drm_crtc_state *crtc_state,
+			    struct drm_connector_state *conn_state)
+{
+	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+
+	s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
+	s->output_type = DRM_MODE_CONNECTOR_DisplayPort;
+
+	return 0;
+}
+
+static struct drm_encoder_helper_funcs cdn_dp_encoder_helper_funcs = {
+	.mode_set = cdn_dp_encoder_mode_set,
+	.enable = cdn_dp_encoder_enable,
+	.disable = cdn_dp_encoder_disable,
+	.atomic_check = cdn_dp_encoder_atomic_check,
+};
+
+static struct drm_encoder_funcs cdn_dp_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+static int cdn_dp_firmware_init(struct cdn_dp_device *dp)
+{
+	int ret;
+	const u32 *iram_data, *dram_data;
+	const struct firmware *fw = dp->fw;
+	const struct cdn_firmware_header *hdr;
+
+	hdr = (struct cdn_firmware_header *)fw->data;
+	if (fw->size != le32_to_cpu(hdr->size_bytes)) {
+		dev_err(dp->dev, "firmware is invalid\n");
+		return -EINVAL;
+	}
+
+	iram_data = (const u32 *)(fw->data + hdr->header_size);
+	dram_data = (const u32 *)(fw->data + hdr->header_size + hdr->iram_size);
+
+	ret = cdn_dp_load_firmware(dp, iram_data, hdr->iram_size,
+				   dram_data, hdr->dram_size);
+	if (ret)
+		return ret;
+
+	dp->fw_loaded = 1;
+
+	ret = cdn_dp_active(dp, true);
+	if (ret) {
+		dev_err(dp->dev, "active ucpu failed: %d\n", ret);
+		return ret;
+	}
+
+	return cdn_dp_event_config(dp);
+}
+
+static int cdn_dp_init(struct cdn_dp_device *dp)
+{
+	struct device *dev = dp->dev;
+	struct device_node *np = dev->of_node;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct resource *res;
+	int ret;
+
+	dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+	if (IS_ERR(dp->grf)) {
+		dev_err(dev, "cdn-dp needs rockchip,grf property\n");
+		return PTR_ERR(dp->grf);
+	}
+
+	dp->irq = platform_get_irq(pdev, 0);
+	if (dp->irq < 0) {
+		dev_err(dev, "cdn-dp can not get irq\n");
+		return dp->irq;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	dp->regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(dp->regs)) {
+		dev_err(dev, "ioremap reg failed\n");
+		return PTR_ERR(dp->regs);
+	}
+
+	dp->core_clk = devm_clk_get(dev, "core-clk");
+	if (IS_ERR(dp->core_clk)) {
+		dev_err(dev, "cannot get core_clk_dp\n");
+		return PTR_ERR(dp->core_clk);
+	}
+
+	dp->pclk = devm_clk_get(dev, "pclk");
+	if (IS_ERR(dp->pclk)) {
+		dev_err(dev, "cannot get pclk\n");
+		return PTR_ERR(dp->pclk);
+	}
+
+	dp->spdif_clk = devm_clk_get(dev, "spdif");
+	if (IS_ERR(dp->spdif_clk)) {
+		dev_err(dev, "cannot get spdif_clk\n");
+		return PTR_ERR(dp->spdif_clk);
+	}
+
+	dp->spdif_rst = devm_reset_control_get(dev, "spdif");
+	if (IS_ERR(dp->spdif_rst)) {
+		dev_err(dev, "no spdif reset control found\n");
+		return PTR_ERR(dp->spdif_rst);
+	}
+
+	dp->dpms_mode = DRM_MODE_DPMS_OFF;
+
+	ret = cdn_dp_clk_enable(dp);
+	if (ret < 0)
+		return ret;
+
+	cdn_dp_clock_reset(dp);
+
+	return 0;
+}
+
+static int cdn_dp_audio_hw_params(struct device *dev,
+				  struct hdmi_codec_daifmt *daifmt,
+				  struct hdmi_codec_params *params)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	struct audio_info audio = {
+		.sample_width = params->sample_width,
+		.sample_rate = params->sample_rate,
+		.channels = params->channels,
+	};
+	int ret;
+
+	if (!dp->encoder.crtc)
+		return -ENODEV;
+
+	switch (daifmt->fmt) {
+	case HDMI_I2S:
+		audio.format = AFMT_I2S;
+		break;
+	case HDMI_SPDIF:
+		audio.format = AFMT_SPDIF;
+		break;
+	default:
+		dev_err(dev, "%s: Invalid format %d\n", __func__, daifmt->fmt);
+		return -EINVAL;
+	}
+
+	ret = cdn_dp_audio_config_set(dp, &audio);
+	if (!ret)
+		dp->audio_info = audio;
+
+	return ret;
+}
+
+static void cdn_dp_audio_shutdown(struct device *dev)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	int ret = cdn_dp_audio_stop(dp, &dp->audio_info);
+
+	if (!ret)
+		dp->audio_info.format = AFMT_UNUSED;
+}
+
+static int cdn_dp_audio_digital_mute(struct device *dev, bool enable)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+
+	return cdn_dp_audio_mute(dp, enable);
+}
+
+static int cdn_dp_audio_get_eld(struct device *dev, uint8_t *buf, size_t len)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	struct drm_mode_config *config = &dp->encoder.dev->mode_config;
+	struct drm_connector *connector;
+	int ret = -ENODEV;
+
+	mutex_lock(&config->mutex);
+	list_for_each_entry(connector, &config->connector_list, head) {
+		if (&dp->encoder == connector->encoder) {
+			memcpy(buf, connector->eld,
+			       min(sizeof(connector->eld), len));
+			ret = 0;
+		}
+	}
+	mutex_unlock(&config->mutex);
+
+	return ret;
+}
+
+static const struct hdmi_codec_ops audio_codec_ops = {
+	.hw_params = cdn_dp_audio_hw_params,
+	.audio_shutdown = cdn_dp_audio_shutdown,
+	.digital_mute = cdn_dp_audio_digital_mute,
+	.get_eld = cdn_dp_audio_get_eld,
+};
+
+static int cdn_dp_audio_codec_init(struct cdn_dp_device *dp,
+				   struct device *dev)
+{
+	struct hdmi_codec_pdata codec_data = {
+		.i2s = 1,
+		.spdif = 1,
+		.ops = &audio_codec_ops,
+		.max_i2s_channels = 8,
+	};
+
+	dp->audio_pdev = platform_device_register_data(
+			 dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO,
+			 &codec_data, sizeof(codec_data));
+
+	return PTR_ERR_OR_ZERO(dp->audio_pdev);
+}
+
+static int cdn_dp_get_cap_lanes(struct cdn_dp_device *dp,
+				struct extcon_dev *edev)
+{
+	bool dfp, dptx;
+	u8 lanes;
+
+	dfp = extcon_get_cable_state_(edev, EXTCON_USB_HOST);
+	dptx = extcon_get_cable_state_(edev, EXTCON_DISP_DP);
+
+	if (dfp && dptx)
+		lanes = 2;
+	else if (dptx)
+		lanes = 4;
+	else
+		lanes = 0;
+
+	return lanes;
+}
+
+static int cdn_dp_pd_event(struct notifier_block *nb,
+			   unsigned long event, void *priv)
+{
+	struct cdn_dp_device *dp;
+	struct extcon_dev *edev = priv;
+	u8 i;
+
+	dp = container_of(nb, struct cdn_dp_device, event_nb);
+
+	for (i = 0; i < dp->max_phy; i++) {
+		if (edev == dp->extcon[i]) {
+			dp->port_id = i;
+			break;
+		}
+	}
+
+	schedule_delayed_work(&dp->event_wq, 0);
+
+	return 0;
+}
+
+static void cdn_dp_pd_event_wq(struct work_struct *work)
+{
+	struct cdn_dp_device *dp = container_of(work, struct cdn_dp_device,
+						event_wq.work);
+	u8 id = dp->port_id, i;
+	u8 old_cap_lanes = dp->cap_lanes[id];
+	int ret;
+
+	dp->cap_lanes[id] = cdn_dp_get_cap_lanes(dp, dp->extcon[id]);
+	if (dp->cap_lanes[id] == old_cap_lanes)
+		return;
+
+	/* if phy is on, need reset it when dp lanes count is changed */
+	if (dp->phy_status[id]) {
+		ret = phy_power_off(dp->phy[id]);
+		if (ret) {
+			dev_err(dp->dev, "phy power off failed: %d", ret);
+			goto err_phy_power_off;
+		}
+		dp->phy_status[id] = 0;
+	}
+
+	/* if other phy is running, do not touch the hpd_status, and return */
+	for (i = 0; i < dp->max_phy; i++) {
+		if (dp->phy_status[i] == 1) {
+			dev_warn(dp->dev, "busy, phy[%d] is running", i);
+			goto err_phy_power_off;
+		}
+	}
+
+	if (!dp->cap_lanes[id]) {
+		dp->hpd_status = connector_status_disconnected;
+		drm_helper_hpd_irq_event(dp->drm_dev);
+		return;
+	}
+
+	if (!dp->fw_loaded) {
+		ret = request_firmware(&dp->fw, CDN_DP_FIRMWARE, dp->dev);
+		if (ret == -ENOENT && dp->fw_wait <= MAX_FW_WAIT_SECS) {
+			u32 time = msecs_to_jiffies(dp->fw_wait * HZ);
+
+			/*
+			 * If can not find the file, retry to load the firmware
+			 * in 1 second, if still failed after 1 minute, give up.
+			 */
+			schedule_delayed_work(&dp->event_wq, time);
+			dp->fw_wait *= 2;
+			dev_warn(dp->dev, "retry to request firmware in %d\n",
+				 dp->fw_wait);
+			goto err_phy_power_off;
+		} else if (ret) {
+			dev_err(dp->dev, "failed to request firmware: %d\n",
+				ret);
+			goto err_phy_power_off;
+		}
+	}
+
+	ret = phy_power_on(dp->phy[id]);
+	if (ret) {
+		dev_err(dp->dev, "phy power on failed: %d", ret);
+		if (!dp->fw_loaded)
+			release_firmware(dp->fw);
+		goto err_phy_power_off;
+	}
+
+	dp->phy_status[id] = 1;
+
+	if (!dp->fw_loaded) {
+		ret = cdn_dp_firmware_init(dp);
+		release_firmware(dp->fw);
+		if (ret) {
+			dev_err(dp->dev, "firmware init failed: %d", ret);
+			goto err_firmware;
+		}
+	}
+
+	/* read hpd status failed, or the hpd does not exist. */
+	ret = cdn_dp_get_hpd_status(dp);
+	if (ret <= 0) {
+		dev_err(dp->dev, "get hpd failed: %d", ret);
+		goto err_firmware;
+	}
+
+	ret = cdn_dp_set_host_cap(dp, dp->cap_lanes[id]);
+	if (ret) {
+		dev_err(dp->dev, "set host capabilities failed: %d\n", ret);
+		goto err_firmware;
+	}
+
+	/*
+	 * Native read with retry for link status and receiver capability reads
+	 * for cases where the sink may still be asleep.
+	 *
+	 * Sinks are *supposed* to come up within 1ms from an off state, but
+	 * we're also supposed to retry 3 times per the spec.
+	 */
+	for (i = 0; i < 3; i++) {
+		ret = cdn_dp_dpcd_read(dp, 0x000, dp->dpcd,
+				       DP_RECEIVER_CAP_SIZE);
+		if (!ret) {
+			dp->hpd_status = connector_status_connected;
+			drm_helper_hpd_irq_event(dp->drm_dev);
+			return;
+		}
+		usleep_range(1000, 1050);
+	}
+
+err_firmware:
+	ret = phy_power_off(dp->phy[id]);
+	if (ret)
+		dev_err(dp->dev, "phy power off failed: %d", ret);
+	else
+		dp->phy_status[id] = 0;
+
+err_phy_power_off:
+	dp->cap_lanes[id] = 0;
+}
+
+static int cdn_dp_bind(struct device *dev, struct device *master,
+		       void *data)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	struct drm_encoder *encoder;
+	struct drm_connector *connector;
+	struct drm_device *drm_dev = data;
+	int ret, i;
+
+	ret = cdn_dp_init(dp);
+	if (ret < 0)
+		return ret;
+
+	dp->drm_dev = drm_dev;
+	dp->hpd_status = connector_status_disconnected;
+
+	encoder = &dp->encoder;
+
+	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
+							     dev->of_node);
+	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
+
+	ret = drm_encoder_init(drm_dev, encoder, &cdn_dp_encoder_funcs,
+			       DRM_MODE_ENCODER_TMDS, NULL);
+	if (ret) {
+		DRM_ERROR("failed to initialize encoder with drm\n");
+		return ret;
+	}
+
+	drm_encoder_helper_add(encoder, &cdn_dp_encoder_helper_funcs);
+
+	connector = &dp->connector;
+	connector->polled = DRM_CONNECTOR_POLL_HPD;
+	connector->dpms = DRM_MODE_DPMS_OFF;
+
+	ret = drm_connector_init(drm_dev, connector,
+				 &cdn_dp_atomic_connector_funcs,
+				 DRM_MODE_CONNECTOR_DisplayPort);
+	if (ret) {
+		DRM_ERROR("failed to initialize connector with drm\n");
+		goto err_free_encoder;
+	}
+
+	drm_connector_helper_add(connector, &cdn_dp_connector_helper_funcs);
+
+	ret = drm_mode_connector_attach_encoder(connector, encoder);
+	if (ret) {
+		DRM_ERROR("failed to attach connector and encoder\n");
+		goto err_free_connector;
+	}
+
+	cdn_dp_audio_codec_init(dp, dev);
+
+	dp->event_nb.notifier_call = cdn_dp_pd_event;
+	INIT_DELAYED_WORK(&dp->event_wq, cdn_dp_pd_event_wq);
+
+	for (i = 0; i < dp->max_phy; i++) {
+		if (IS_ERR(dp->extcon[i]))
+			continue;
+
+		ret = extcon_register_notifier(dp->extcon[i], EXTCON_DISP_DP,
+					       &dp->event_nb);
+		if (ret) {
+			dev_err(dev, "regitster EXTCON_DISP_DP notifier err\n");
+			return ret;
+		}
+
+		dp->port_id = i;
+		schedule_delayed_work(&dp->event_wq, 0);
+	}
+
+	return 0;
+
+err_free_connector:
+	drm_connector_cleanup(connector);
+err_free_encoder:
+	drm_encoder_cleanup(encoder);
+	return ret;
+}
+
+static void cdn_dp_unbind(struct device *dev, struct device *master, void *data)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	struct drm_encoder *encoder = &dp->encoder;
+	struct drm_connector *connector = &dp->connector;
+	int i;
+
+	platform_device_unregister(dp->audio_pdev);
+	cdn_dp_encoder_disable(encoder);
+	encoder->funcs->destroy(encoder);
+	connector->funcs->destroy(connector);
+
+	for (i = 0; i < dp->max_phy; i++) {
+		if (!IS_ERR(dp->extcon[i]))
+			extcon_unregister_notifier(dp->extcon[i], EXTCON_USB,
+						   &dp->event_nb);
+	}
+}
+
+static const struct component_ops cdn_dp_component_ops = {
+	.bind = cdn_dp_bind,
+	.unbind = cdn_dp_unbind,
+};
+
+static int cdn_dp_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	const struct of_device_id *match;
+	struct cdn_dp_data *dp_data;
+	struct cdn_dp_device *dp;
+	u8 count = 0;
+	int i;
+
+	dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
+	if (!dp)
+		return -ENOMEM;
+	dp->dev = dev;
+
+	match = of_match_node(cdn_dp_dt_ids, pdev->dev.of_node);
+	dp_data = (struct cdn_dp_data *)match->data;
+
+	dp->max_phy = dp_data->max_phy;
+
+	for (i = 0; i < dp->max_phy; i++) {
+		dp->extcon[i] = extcon_get_edev_by_phandle(dev, i);
+		dp->phy[i] = devm_of_phy_get_by_index(dev, dev->of_node, i);
+
+		if (!IS_ERR(dp->extcon[i]) && !IS_ERR(dp->phy[i]))
+			count++;
+
+		if (PTR_ERR(dp->extcon[i]) == -EPROBE_DEFER ||
+		    PTR_ERR(dp->phy[i]) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+	}
+
+	if (!count) {
+		dev_err(dev, "missing extcon or phy\n");
+		return -EINVAL;
+	}
+
+	dev_set_drvdata(dev, dp);
+
+	return component_add(dev, &cdn_dp_component_ops);
+}
+
+static int cdn_dp_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &cdn_dp_component_ops);
+
+	return 0;
+}
+
+static struct platform_driver cdn_dp_driver = {
+	.probe = cdn_dp_probe,
+	.remove = cdn_dp_remove,
+	.driver = {
+		   .name = "cdn-dp",
+		   .owner = THIS_MODULE,
+		   .of_match_table = of_match_ptr(cdn_dp_dt_ids),
+	},
+};
+
+module_platform_driver(cdn_dp_driver);
+
+MODULE_AUTHOR("Chris Zhong <zyw at rock-chips.com>");
+MODULE_DESCRIPTION("cdn DP Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
new file mode 100644
index 0000000..1c577bf
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 Chris Zhong <zyw at rock-chips.com>
+ * Copyright (C) 2016 ROCKCHIP, Inc.
+ *
+ * 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.
+ */
+
+#ifndef _CDN_DP_CORE_H
+#define _CDN_DP_CORE_H
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_panel.h>
+#include "rockchip_drm_drv.h"
+
+#define MAX_PHY		2
+
+enum audio_format {
+	AFMT_I2S = 0,
+	AFMT_SPDIF = 1,
+	AFMT_UNUSED,
+};
+
+struct audio_info {
+	enum audio_format format;
+	int sample_rate;
+	int channels;
+	int sample_width;
+};
+
+enum vic_pxl_encoding_format {
+	PXL_RGB = 0x1,
+	YCBCR_4_4_4 = 0x2,
+	YCBCR_4_2_2 = 0x4,
+	YCBCR_4_2_0 = 0x8,
+	Y_ONLY = 0x10,
+};
+
+struct video_info {
+	bool h_sync_polarity;
+	bool v_sync_polarity;
+	bool interlaced;
+	int color_depth;
+	enum vic_pxl_encoding_format color_fmt;
+};
+
+struct cdn_firmware_header {
+	u32 size_bytes; /* size of the entire header+image(s) in bytes */
+	u32 header_size; /* size of just the header in bytes */
+	u32 iram_size; /* size of iram */
+	u32 dram_size; /* size of dram */
+};
+
+struct cdn_dp_device {
+	struct device *dev;
+	struct drm_device *drm_dev;
+	struct drm_connector connector;
+	struct drm_encoder encoder;
+	struct drm_display_mode mode;
+	struct platform_device *audio_pdev;
+
+	const struct firmware *fw;	/* cdn dp firmware */
+	unsigned int fw_version;	/* cdn fw version */
+	u32 fw_wait;
+	bool fw_loaded;
+	void __iomem *regs;
+	struct regmap *grf;
+	unsigned int irq;
+	struct clk *core_clk;
+	struct clk *pclk;
+	struct clk *spdif_clk;
+	struct reset_control *spdif_rst;
+	struct audio_info audio_info;
+	struct video_info video_info;
+	struct extcon_dev *extcon[MAX_PHY];
+	struct phy *phy[MAX_PHY];
+	struct notifier_block event_nb;
+	struct delayed_work event_wq;
+	struct drm_dp_link link;
+
+	u8 cap_lanes[MAX_PHY];
+	u8 dpcd[DP_RECEIVER_CAP_SIZE];
+	u8 port_id;
+	u8 max_phy;
+	bool phy_status[MAX_PHY];
+	enum drm_connector_status hpd_status;
+
+	int dpms_mode;
+	bool sink_has_audio;
+};
+#endif  /* _CDN_DP_CORE_H */
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
new file mode 100644
index 0000000..afc8ae9
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
@@ -0,0 +1,898 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Chris Zhong <zyw at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/clk.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/reset.h>
+
+#include "cdn-dp-core.h"
+#include "cdn-dp-reg.h"
+
+#define CDN_DP_SPDIF_CLK		200000000
+#define FW_ALIVE_TIMEOUT_US		1000000
+#define MAILBOX_RETRY_US		1000
+#define MAILBOX_TIMEOUT_US		5000000
+#define LINK_TRAINING_RETRY_MS		20
+#define LINK_TRAINING_TIMEOUT_MS	500
+
+void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, u32 clk)
+{
+	writel(clk / 1000000, dp->regs + SW_CLK_H);
+}
+
+void cdn_dp_clock_reset(struct cdn_dp_device *dp)
+{
+	u32 val;
+
+	val = DPTX_FRMR_DATA_CLK_RSTN_EN |
+	      DPTX_FRMR_DATA_CLK_EN |
+	      DPTX_PHY_DATA_RSTN_EN |
+	      DPTX_PHY_DATA_CLK_EN |
+	      DPTX_PHY_CHAR_RSTN_EN |
+	      DPTX_PHY_CHAR_CLK_EN |
+	      SOURCE_AUX_SYS_CLK_RSTN_EN |
+	      SOURCE_AUX_SYS_CLK_EN |
+	      DPTX_SYS_CLK_RSTN_EN |
+	      DPTX_SYS_CLK_EN |
+	      CFG_DPTX_VIF_CLK_RSTN_EN |
+	      CFG_DPTX_VIF_CLK_EN;
+	writel(val, dp->regs + SOURCE_DPTX_CAR);
+
+	val = SOURCE_PHY_RSTN_EN | SOURCE_PHY_CLK_EN;
+	writel(val, dp->regs + SOURCE_PHY_CAR);
+
+	val = SOURCE_PKT_SYS_RSTN_EN |
+	      SOURCE_PKT_SYS_CLK_EN |
+	      SOURCE_PKT_DATA_RSTN_EN |
+	      SOURCE_PKT_DATA_CLK_EN;
+	writel(val, dp->regs + SOURCE_PKT_CAR);
+
+	val = SPDIF_CDR_CLK_RSTN_EN |
+	      SPDIF_CDR_CLK_EN |
+	      SOURCE_AIF_SYS_RSTN_EN |
+	      SOURCE_AIF_SYS_CLK_EN |
+	      SOURCE_AIF_CLK_RSTN_EN |
+	      SOURCE_AIF_CLK_EN;
+	writel(val, dp->regs + SOURCE_AIF_CAR);
+
+	val = SOURCE_CIPHER_SYSTEM_CLK_RSTN_EN |
+	      SOURCE_CIPHER_SYS_CLK_EN |
+	      SOURCE_CIPHER_CHAR_CLK_RSTN_EN |
+	      SOURCE_CIPHER_CHAR_CLK_EN;
+	writel(val, dp->regs + SOURCE_CIPHER_CAR);
+
+	val = SOURCE_CRYPTO_SYS_CLK_RSTN_EN |
+	      SOURCE_CRYPTO_SYS_CLK_EN;
+	writel(val, dp->regs + SOURCE_CRYPTO_CAR);
+
+	val = ~(MAILBOX_INT_MASK_BIT | PIF_INT_MASK_BIT) & ALL_INT_MASK;
+	writel(val, dp->regs + APB_INT_MASK);
+}
+
+static int cdn_dp_mailbox_read(struct cdn_dp_device *dp)
+{
+	int val, ret;
+
+	if (!dp->fw_loaded)
+		return -EPERM;
+
+	ret = readx_poll_timeout(readl, dp->regs + MAILBOX_EMPTY_ADDR,
+				 val, !val, MAILBOX_RETRY_US,
+				 MAILBOX_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(dp->dev, "failed to read mailbox, keep alive = %x\n",
+			readl(dp->regs + KEEP_ALIVE));
+		return ret;
+	}
+
+	return readl(dp->regs + MAILBOX0_RD_DATA) & 0xff;
+}
+
+static int cdp_dp_mailbox_write(struct cdn_dp_device *dp, u8 val)
+{
+	int ret, full;
+
+	if (!dp->fw_loaded)
+		return -EPERM;
+
+	ret = readx_poll_timeout(readl, dp->regs + MAILBOX_FULL_ADDR,
+				 full, !full, MAILBOX_RETRY_US,
+				 MAILBOX_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(dp->dev, "mailbox is full, keep alive = %x\n",
+			readl(dp->regs + KEEP_ALIVE));
+		return ret;
+	}
+
+	writel(val, dp->regs + MAILBOX0_WR_DATA);
+
+	return 0;
+}
+
+static int cdn_dp_mailbox_validate_receive(struct cdn_dp_device *dp,
+					   u8 module_id, u8 opcode,
+					   u8 req_size)
+{
+	u32 mbox_size, i;
+	u8 header[4];
+	int ret;
+
+	/* read the header of the message */
+	for (i = 0; i < 4; i++)  {
+		ret = cdn_dp_mailbox_read(dp);
+		if (ret < 0)
+			return ret;
+
+		header[i] = ret;
+	}
+
+	mbox_size = (header[2] << 8) | header[3];
+
+	if (opcode != header[0] || module_id != header[1] ||
+	    req_size != mbox_size) {
+		dump_stack();
+		dev_err(dp->dev, "mailbox validate failed");
+
+		/*
+		 * If the message in mailbox is not what we want, we need to
+		 * clear the mailbox by read.
+		 */
+		for (i = 0; i < mbox_size; i++)
+			if (cdn_dp_mailbox_read(dp) < 0)
+				break;
+
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int cdn_dp_mailbox_read_receive(struct cdn_dp_device *dp,
+				       u8 *buff, u8 buff_size)
+{
+	u32 i;
+	int ret;
+
+	for (i = 0; i < buff_size; i++) {
+		ret = cdn_dp_mailbox_read(dp);
+		if (ret < 0)
+			return ret;
+
+		buff[i] = ret;
+	}
+
+	return 0;
+}
+
+static int cdn_dp_mailbox_send(struct cdn_dp_device *dp, u8 module_id,
+			       u8 opcode, u16 size, u8 *message)
+{
+	u8 header[4];
+	int ret, i;
+
+	header[0] = opcode;
+	header[1] = module_id;
+	header[2] = (size >> 8) & 0xff;
+	header[3] = size & 0xff;
+
+	for (i = 0; i < 4; i++) {
+		ret = cdp_dp_mailbox_write(dp, header[i]);
+		if (ret)
+			return ret;
+	}
+
+	for (i = 0; i < size; i++) {
+		ret = cdp_dp_mailbox_write(dp, message[i]);
+		if (ret) {
+			dev_err(dp->dev, "mailbox send failed: %d", ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
+{
+	u8 msg[6];
+
+	msg[0] = (addr >> 8) & 0xff;
+	msg[1] = addr & 0xff;
+	msg[2] = (val >> 24) & 0xff;
+	msg[3] = (val >> 16) & 0xff;
+	msg[4] = (val >> 8) & 0xff;
+	msg[5] = val & 0xff;
+	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_REGISTER,
+				   sizeof(msg), msg);
+}
+
+int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
+{
+	u8 msg[5], reg[5];
+	int ret;
+
+	msg[0] = (len >> 8) & 0xff;
+	msg[1] = len & 0xff;
+	msg[2] = (addr >> 16) & 0xff;
+	msg[3] = (addr >> 8) & 0xff;
+	msg[4] = addr & 0xff;
+	cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_READ_DPCD,
+			    sizeof(msg), msg);
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_READ_DPCD,
+					      sizeof(reg) + len);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg));
+	if (ret)
+		return ret;
+
+	return cdn_dp_mailbox_read_receive(dp, data, len);
+}
+
+int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
+{
+	u8 msg[6], reg[5];
+	int ret;
+
+	msg[0] = 0;
+	msg[1] = 1;
+	msg[2] = (addr >> 16) & 0xff;
+	msg[3] = (addr >> 8) & 0xff;
+	msg[4] = addr & 0xff;
+	msg[5] = value;
+	cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_DPCD,
+			    sizeof(msg), msg);
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_WRITE_DPCD, sizeof(reg));
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg));
+	if (ret)
+		return ret;
+
+	if (addr != (reg[2] << 16 | reg[3] << 8 | reg[4])) {
+		dev_err(dp->dev, "write dpcd failed\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
+			 u32 i_size, const u32 *d_mem, u32 d_size)
+{
+	int i, ret;
+	u32 reg;
+
+	/* reset ucpu before load firmware*/
+	writel(APB_IRAM_PATH | APB_DRAM_PATH | APB_XT_RESET,
+	       dp->regs + APB_CTRL);
+
+	for (i = 0; i < i_size; i += 4)
+		writel(*i_mem++, dp->regs + ADDR_IMEM + i);
+
+	for (i = 0; i < d_size; i += 4)
+		writel(*d_mem++, dp->regs + ADDR_DMEM + i);
+
+	/* un-reset ucpu */
+	writel(0, dp->regs + APB_CTRL);
+
+	/* check the keep alive register to make sure fw working */
+	ret = readx_poll_timeout(readl, dp->regs + KEEP_ALIVE,
+				 reg, reg, 2000, FW_ALIVE_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(dp->dev, "failed to loaded the FW reg = %x\n", reg);
+		return -EINVAL;
+	}
+
+	reg = readl(dp->regs + VER_L) & 0xff;
+	dp->fw_version = reg;
+	reg = readl(dp->regs + VER_H) & 0xff;
+	dp->fw_version |= reg << 8;
+	reg = readl(dp->regs + VER_LIB_L_ADDR) & 0xff;
+	dp->fw_version |= reg << 16;
+	reg = readl(dp->regs + VER_LIB_H_ADDR) & 0xff;
+	dp->fw_version |= reg << 24;
+
+	dev_dbg(dp->dev, "firmware version: %x\n", dp->fw_version);
+
+	return 0;
+}
+
+int cdn_dp_reg_write_bit(struct cdn_dp_device *dp, u16 addr, u8 start_bit,
+			 u8 bits_no, u32 val)
+{
+	u8 field[8];
+
+	field[0] = (addr >> 8) & 0xff;
+	field[1] = addr & 0xff;
+	field[2] = start_bit;
+	field[3] = bits_no;
+	field[4] = (val >> 24) & 0xff;
+	field[5] = (val >> 16) & 0xff;
+	field[6] = (val >> 8) & 0xff;
+	field[7] = val & 0xff;
+
+	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_FIELD,
+				   sizeof(field), field);
+}
+
+int cdn_dp_active(struct cdn_dp_device *dp, bool enable)
+{
+	u8 active = enable ? 1 : 0;
+	u8 resp;
+	int ret;
+
+	/* set firmware status, 1: active; 0: standby */
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_GENERAL,
+				  GENERAL_MAIN_CONTROL, sizeof(active),
+				  &active);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_GENERAL,
+					      GENERAL_MAIN_CONTROL,
+					      sizeof(resp));
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, &resp, sizeof(resp));
+	if (ret)
+		return ret;
+
+	return resp ? 0 : -EINVAL;
+}
+
+int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes)
+{
+	u8 msg[8];
+	int ret;
+
+	msg[0] = CDN_DP_MAX_LINK_RATE;
+	msg[1] = lanes;
+	msg[2] = VOLTAGE_LEVEL_2;
+	msg[3] = PRE_EMPHASIS_LEVEL_3;
+	msg[4] = PTS1 | PTS2 | PTS3 | PTS4;
+	msg[5] = FAST_LT_NOT_SUPPORT;
+	msg[6] = LANE_MAPPING_NORMAL;
+	msg[7] = ENHANCED;
+
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
+				  DPTX_SET_HOST_CAPABILITIES,
+				  sizeof(msg), msg);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_reg_write(dp, DP_AUX_SWAP_INVERSION_CONTROL,
+			       AUX_HOST_INVERT);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+int cdn_dp_event_config(struct cdn_dp_device *dp)
+{
+	u8 msg[5];
+
+	memset(msg, 0, sizeof(msg));
+
+	msg[0] = DPTX_EVENT_ENABLE_HPD | DPTX_EVENT_ENABLE_TRAINING;
+
+	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_ENABLE_EVENT,
+				   sizeof(msg), msg);
+}
+
+u32 cdn_dp_get_event(struct cdn_dp_device *dp)
+{
+	return readl(dp->regs + SW_EVENTS0);
+}
+
+int cdn_dp_get_hpd_status(struct cdn_dp_device *dp)
+{
+	u8 status;
+	int ret;
+
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_HPD_STATE,
+				  0, NULL);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_HPD_STATE, sizeof(status));
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, &status, sizeof(status));
+	if (ret)
+		return ret;
+
+	return status;
+}
+
+int cdn_dp_get_edid_block(void *data, u8 *edid,
+			  unsigned int block, size_t length)
+{
+	struct cdn_dp_device *dp = data;
+	u8 msg[2], reg[2];
+	int ret;
+
+	msg[0] = block / 2;
+	msg[1] = block % 2;
+
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_GET_EDID,
+				  sizeof(msg), msg);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_GET_EDID,
+					      sizeof(reg) + length);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg));
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, edid, length);
+	if (ret)
+		return ret;
+
+	if (reg[0] != length || reg[1] != block / 2) {
+		dev_err(dp->dev, "edid block size err\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int cdn_dp_training_start(struct cdn_dp_device *dp)
+{
+	u8 msg, event[2];
+	unsigned long timeout;
+	int ret;
+
+	msg = LINK_TRAINING_RUN;
+
+	/* start training */
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_TRAINING_CONTROL,
+				  sizeof(msg), &msg);
+	if (ret)
+		return ret;
+
+	timeout = jiffies + msecs_to_jiffies(LINK_TRAINING_TIMEOUT_MS);
+	while (time_before(jiffies, timeout)) {
+		msleep(LINK_TRAINING_RETRY_MS);
+		ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
+					  DPTX_READ_EVENT, 0, NULL);
+		if (ret)
+			return ret;
+
+		ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+						      DPTX_READ_EVENT,
+						      sizeof(event));
+		if (ret)
+			return ret;
+
+		ret = cdn_dp_mailbox_read_receive(dp, event, sizeof(event));
+		if (ret)
+			return ret;
+
+		if (event[1] & EQ_PHASE_FINISHED)
+			return 0;
+	}
+
+	return -ETIMEDOUT;
+}
+
+int cdn_dp_get_training_status(struct cdn_dp_device *dp)
+{
+	u8 status[10];
+	int ret;
+
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_READ_LINK_STAT,
+				  0, NULL);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_READ_LINK_STAT,
+					      sizeof(status));
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, status, sizeof(status));
+	if (ret)
+		return ret;
+
+	dp->link.rate = status[0];
+	dp->link.num_lanes = status[1];
+
+	return 0;
+}
+
+int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active)
+{
+	u8 msg;
+
+	msg = !!active;
+
+	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_SET_VIDEO,
+				   sizeof(msg), &msg);
+}
+
+static int cdn_dp_get_msa_misc(struct video_info *video,
+			       struct drm_display_mode *mode)
+{
+	u8 val[2];
+	u32 msa_misc;
+
+	switch (video->color_fmt) {
+	case PXL_RGB:
+	case Y_ONLY:
+		val[0] = 0;
+		break;
+	/* set YUV default color space conversion to BT601 */
+	case YCBCR_4_4_4:
+		val[0] = 6 + BT_601 * 8;
+		break;
+	case YCBCR_4_2_2:
+		val[0] = 5 + BT_601 * 8;
+		break;
+	case YCBCR_4_2_0:
+		val[0] = 5;
+		break;
+	};
+
+	switch (video->color_depth) {
+	case 6:
+		val[1] = 0;
+		break;
+	case 8:
+		val[1] = 1;
+		break;
+	case 10:
+		val[1] = 2;
+		break;
+	case 12:
+		val[1] = 3;
+		break;
+	case 16:
+		val[1] = 4;
+		break;
+	};
+
+	msa_misc = 2 * val[0] + 32 * val[1] +
+		   ((video->color_fmt == Y_ONLY) ? (1 << 14) : 0);
+
+	return msa_misc;
+}
+
+int cdn_dp_config_video(struct cdn_dp_device *dp)
+{
+	struct video_info *video = &dp->video_info;
+	struct drm_display_mode *mode = &dp->mode;
+	u64 symbol;
+	u32 val, link_rate;
+	u8 bit_per_pix;
+	u8 tu_size_reg = TU_SIZE;
+	int ret;
+
+	bit_per_pix = (video->color_fmt == YCBCR_4_2_2) ?
+		      (video->color_depth * 2) : (video->color_depth * 3);
+
+	link_rate = drm_dp_bw_code_to_link_rate(dp->link.rate);
+
+	val = VIF_BYPASS_INTERLACE;
+	ret = cdn_dp_reg_write(dp, BND_HSYNC2VSYNC, val);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_reg_write(dp, HSYNC2VSYNC_POL_CTRL, 0);
+	if (ret)
+		return ret;
+
+	/* get a best tu_size and symbol */
+	do {
+		tu_size_reg += 2;
+		symbol = tu_size_reg * mode->clock * bit_per_pix;
+		symbol /= dp->link.num_lanes * link_rate * 8;
+	} while ((symbol == 1) || (tu_size_reg - symbol < 4));
+
+	val = symbol + (tu_size_reg << 8);
+	ret = cdn_dp_reg_write(dp, DP_FRAMER_TU, val);
+	if (ret)
+		return ret;
+
+	switch (video->color_depth) {
+	case 6:
+		val = BCS_6;
+		break;
+	case 8:
+		val = BCS_8;
+		break;
+	case 10:
+		val = BCS_10;
+		break;
+	case 12:
+		val = BCS_12;
+		break;
+	case 16:
+		val = BCS_16;
+		break;
+	};
+
+	val += video->color_fmt << 8;
+	ret = cdn_dp_reg_write(dp, DP_FRAMER_PXL_REPR, val);
+	if (ret)
+		return ret;
+
+	val = video->h_sync_polarity ? DP_FRAMER_SP_HSP : 0;
+	val |= video->v_sync_polarity ? DP_FRAMER_SP_VSP : 0;
+	ret = cdn_dp_reg_write(dp, DP_FRAMER_SP, val);
+	if (ret)
+		return ret;
+
+	val = (mode->hsync_start - mode->hdisplay) << 16;
+	val |= mode->htotal - mode->hsync_end;
+	ret = cdn_dp_reg_write(dp, DP_FRONT_BACK_PORCH, val);
+	if (ret)
+		return ret;
+
+	val = mode->hdisplay * bit_per_pix / 8;
+	ret = cdn_dp_reg_write(dp, DP_BYTE_COUNT, val);
+	if (ret)
+		return ret;
+
+	val = mode->htotal | ((mode->htotal - mode->hsync_start) << 16);
+	ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_0, val);
+	if (ret)
+		return ret;
+
+	val = mode->hsync_end - mode->hsync_start;
+	val |= (mode->hdisplay << 16) | (video->h_sync_polarity << 15);
+	ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_1, val);
+	if (ret)
+		return ret;
+
+	val = mode->vtotal;
+	val |= ((mode->vtotal - mode->vsync_start) << 16);
+	ret = cdn_dp_reg_write(dp, MSA_VERTICAL_0, val);
+	if (ret)
+		return ret;
+
+	val = mode->vsync_end - mode->vsync_start;
+	val |= mode->vdisplay << 16 | (video->v_sync_polarity << 15);
+	ret = cdn_dp_reg_write(dp, MSA_VERTICAL_1, val);
+	if (ret)
+		return ret;
+
+	val = cdn_dp_get_msa_misc(video, mode);
+	ret = cdn_dp_reg_write(dp, MSA_MISC, val);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_reg_write(dp, STREAM_CONFIG, 1);
+	if (ret)
+		return ret;
+
+	val = mode->hsync_end - mode->hsync_start;
+	val |= (mode->hdisplay << 16);
+	ret = cdn_dp_reg_write(dp, DP_HORIZONTAL, val);
+	if (ret)
+		return ret;
+
+	val = mode->vtotal;
+	val -= (mode->vtotal - mode->vdisplay);
+	val |= (mode->vtotal - mode->vsync_start) << 16;
+	ret = cdn_dp_reg_write(dp, DP_VERTICAL_0, val);
+	if (ret)
+		return ret;
+
+	val = mode->vtotal;
+	ret = cdn_dp_reg_write(dp, DP_VERTICAL_1, val);
+	if (ret)
+		return ret;
+
+	val =  0;
+	return cdn_dp_reg_write_bit(dp, DP_VB_ID, 2, 1, val);
+}
+
+int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio)
+{
+	u32 val;
+	int ret;
+
+	ret = cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, 0);
+	if (ret)
+		return ret;
+
+	val = SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS;
+	val |= SPDIF_FIFO_MID_RANGE(0xe0);
+	val |= SPDIF_JITTER_THRSH(0xe0);
+	val |= SPDIF_JITTER_AVG_WIN(7);
+	writel(val, dp->regs + SPDIF_CTRL_ADDR);
+
+	/* clearn the audio config and reset */
+	writel(0, dp->regs + AUDIO_SRC_CNTL);
+	writel(0, dp->regs + AUDIO_SRC_CNFG);
+	writel(AUDIO_SW_RST, dp->regs + AUDIO_SRC_CNTL);
+	writel(0, dp->regs + AUDIO_SRC_CNTL);
+
+	/* reset smpl2pckt component  */
+	writel(0, dp->regs + SMPL2PKT_CNTL);
+	writel(AUDIO_SW_RST, dp->regs + SMPL2PKT_CNTL);
+	writel(0, dp->regs + SMPL2PKT_CNTL);
+
+	/* reset FIFO */
+	writel(AUDIO_SW_RST, dp->regs + FIFO_CNTL);
+	writel(0, dp->regs + FIFO_CNTL);
+
+	if (audio->format == AFMT_SPDIF)
+		clk_disable_unprepare(dp->spdif_clk);
+
+	return 0;
+}
+
+int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable)
+{
+	return cdn_dp_reg_write_bit(dp, DP_VB_ID, 4, 1, enable);
+}
+
+static void cdn_dp_audio_config_i2s(struct cdn_dp_device *dp,
+				    struct audio_info *audio)
+{
+	int sub_pckt_num = 1, i2s_port_en_val = 0xf, i;
+	u32 val;
+
+	if (audio->channels == 2) {
+		if (dp->link.num_lanes == 1)
+			sub_pckt_num = 2;
+		else
+			sub_pckt_num = 4;
+
+		i2s_port_en_val = 1;
+	} else if (audio->channels == 4) {
+		i2s_port_en_val = 3;
+	}
+
+	writel(0x0, dp->regs + SPDIF_CTRL_ADDR);
+
+	writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL);
+
+	val = MAX_NUM_CH(audio->channels);
+	val |= NUM_OF_I2S_PORTS(audio->channels);
+	val |= AUDIO_TYPE_LPCM;
+	val |= CFG_SUB_PCKT_NUM(sub_pckt_num);
+	writel(val, dp->regs + SMPL2PKT_CNFG);
+
+	if (audio->sample_width == 16)
+		val = 0;
+	else if (audio->sample_width == 24)
+		val = 1 << 9;
+	else
+		val = 2 << 9;
+
+	val |= AUDIO_CH_NUM(audio->channels);
+	val |= I2S_DEC_PORT_EN(i2s_port_en_val);
+	val |= TRANS_SMPL_WIDTH_32;
+	writel(val, dp->regs + AUDIO_SRC_CNFG);
+
+	for (i = 0; i < (audio->channels + 1) / 2; i++) {
+		if (audio->sample_width == 16)
+			val = (0x08 << 8) | (0x08 << 20);
+		else if (audio->sample_width == 24)
+			val = (0x0b << 8) | (0x0b << 20);
+
+		val |= ((2 * i) << 4) | ((2 * i + 1) << 16);
+		writel(val, dp->regs + STTS_BIT_CH(i));
+	}
+
+	switch (audio->sample_rate) {
+	case 32000:
+		val = SAMPLING_FREQ(3) |
+		      ORIGINAL_SAMP_FREQ(0xc);
+		break;
+	case 44100:
+		val = SAMPLING_FREQ(0) |
+		      ORIGINAL_SAMP_FREQ(0xf);
+		break;
+	case 48000:
+		val = SAMPLING_FREQ(2) |
+		      ORIGINAL_SAMP_FREQ(0xd);
+		break;
+	case 88200:
+		val = SAMPLING_FREQ(8) |
+		      ORIGINAL_SAMP_FREQ(0x7);
+		break;
+	case 96000:
+		val = SAMPLING_FREQ(0xa) |
+		      ORIGINAL_SAMP_FREQ(5);
+		break;
+	case 176400:
+		val = SAMPLING_FREQ(0xc) |
+		      ORIGINAL_SAMP_FREQ(3);
+		break;
+	case 192000:
+		val = SAMPLING_FREQ(0xe) |
+		      ORIGINAL_SAMP_FREQ(1);
+		break;
+	}
+	val |= 4;
+	writel(val, dp->regs + COM_CH_STTS_BITS);
+
+	writel(SMPL2PKT_EN, dp->regs + SMPL2PKT_CNTL);
+	writel(I2S_DEC_START, dp->regs + AUDIO_SRC_CNTL);
+}
+
+static void cdn_dp_audio_config_spdif(struct cdn_dp_device *dp)
+{
+	u32 val;
+
+	val = SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS;
+	val |= SPDIF_FIFO_MID_RANGE(0xe0);
+	val |= SPDIF_JITTER_THRSH(0xe0);
+	val |= SPDIF_JITTER_AVG_WIN(7);
+	writel(val, dp->regs + SPDIF_CTRL_ADDR);
+
+	writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL);
+
+	val = MAX_NUM_CH(2) | AUDIO_TYPE_LPCM | CFG_SUB_PCKT_NUM(4);
+	writel(val, dp->regs + SMPL2PKT_CNFG);
+	writel(SMPL2PKT_EN, dp->regs + SMPL2PKT_CNTL);
+
+	val = SPDIF_ENABLE | SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS;
+	val |= SPDIF_FIFO_MID_RANGE(0xe0);
+	val |= SPDIF_JITTER_THRSH(0xe0);
+	val |= SPDIF_JITTER_AVG_WIN(7);
+	writel(val, dp->regs + SPDIF_CTRL_ADDR);
+
+	clk_prepare_enable(dp->spdif_clk);
+	clk_set_rate(dp->spdif_clk, CDN_DP_SPDIF_CLK);
+}
+
+int cdn_dp_audio_config_set(struct cdn_dp_device *dp, struct audio_info *audio)
+{
+	int ret;
+
+	/* reset the spdif clk before config */
+	if (audio->format == AFMT_SPDIF) {
+		reset_control_assert(dp->spdif_rst);
+		reset_control_deassert(dp->spdif_rst);
+	}
+
+	ret = cdn_dp_reg_write(dp, CM_LANE_CTRL, LANE_REF_CYC);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_reg_write(dp, CM_CTRL, 0);
+	if (ret)
+		return ret;
+
+	if (audio->format == AFMT_I2S)
+		cdn_dp_audio_config_i2s(dp, audio);
+	else
+		cdn_dp_audio_config_spdif(dp);
+
+	return cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, AUDIO_PACK_EN);
+}
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
new file mode 100644
index 0000000..1affeed
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
@@ -0,0 +1,479 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Chris Zhong <zyw at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _CDN_DP_REG_H
+#define _CDN_DP_REG_H
+
+#include <linux/bitops.h>
+
+#define ADDR_IMEM		0x10000
+#define ADDR_DMEM		0x20000
+
+/* APB CFG addr */
+#define APB_CTRL			0
+#define XT_INT_CTRL			0x04
+#define MAILBOX_FULL_ADDR		0x08
+#define MAILBOX_EMPTY_ADDR		0x0c
+#define MAILBOX0_WR_DATA		0x10
+#define MAILBOX0_RD_DATA		0x14
+#define KEEP_ALIVE			0x18
+#define VER_L				0x1c
+#define VER_H				0x20
+#define VER_LIB_L_ADDR			0x24
+#define VER_LIB_H_ADDR			0x28
+#define SW_DEBUG_L			0x2c
+#define SW_DEBUG_H			0x30
+#define MAILBOX_INT_MASK		0x34
+#define MAILBOX_INT_STATUS		0x38
+#define SW_CLK_L			0x3c
+#define SW_CLK_H			0x40
+#define SW_EVENTS0			0x44
+#define SW_EVENTS1			0x48
+#define SW_EVENTS2			0x4c
+#define SW_EVENTS3			0x50
+#define XT_OCD_CTRL			0x60
+#define APB_INT_MASK			0x6c
+#define APB_STATUS_MASK			0x70
+
+/* audio decoder addr */
+#define AUDIO_SRC_CNTL			0x30000
+#define AUDIO_SRC_CNFG			0x30004
+#define COM_CH_STTS_BITS		0x30008
+#define STTS_BIT_CH(x)			(0x3000c + ((x) << 2))
+#define SPDIF_CTRL_ADDR			0x3004c
+#define SPDIF_CH1_CS_3100_ADDR		0x30050
+#define SPDIF_CH1_CS_6332_ADDR		0x30054
+#define SPDIF_CH1_CS_9564_ADDR		0x30058
+#define SPDIF_CH1_CS_12796_ADDR		0x3005c
+#define SPDIF_CH1_CS_159128_ADDR	0x30060
+#define SPDIF_CH1_CS_191160_ADDR	0x30064
+#define SPDIF_CH2_CS_3100_ADDR		0x30068
+#define SPDIF_CH2_CS_6332_ADDR		0x3006c
+#define SPDIF_CH2_CS_9564_ADDR		0x30070
+#define SPDIF_CH2_CS_12796_ADDR		0x30074
+#define SPDIF_CH2_CS_159128_ADDR	0x30078
+#define SPDIF_CH2_CS_191160_ADDR	0x3007c
+#define SMPL2PKT_CNTL			0x30080
+#define SMPL2PKT_CNFG			0x30084
+#define FIFO_CNTL			0x30088
+#define FIFO_STTS			0x3008c
+
+/* source pif addr */
+#define SOURCE_PIF_WR_ADDR		0x30800
+#define SOURCE_PIF_WR_REQ		0x30804
+#define SOURCE_PIF_RD_ADDR		0x30808
+#define SOURCE_PIF_RD_REQ		0x3080c
+#define SOURCE_PIF_DATA_WR		0x30810
+#define SOURCE_PIF_DATA_RD		0x30814
+#define SOURCE_PIF_FIFO1_FLUSH		0x30818
+#define SOURCE_PIF_FIFO2_FLUSH		0x3081c
+#define SOURCE_PIF_STATUS		0x30820
+#define SOURCE_PIF_INTERRUPT_SOURCE	0x30824
+#define SOURCE_PIF_INTERRUPT_MASK	0x30828
+#define SOURCE_PIF_PKT_ALLOC_REG	0x3082c
+#define SOURCE_PIF_PKT_ALLOC_WR_EN	0x30830
+#define SOURCE_PIF_SW_RESET		0x30834
+
+/* bellow registers need access by mailbox */
+/* source car addr */
+#define SOURCE_HDTX_CAR			0x0900
+#define SOURCE_DPTX_CAR			0x0904
+#define SOURCE_PHY_CAR			0x0908
+#define SOURCE_CEC_CAR			0x090c
+#define SOURCE_CBUS_CAR			0x0910
+#define SOURCE_PKT_CAR			0x0918
+#define SOURCE_AIF_CAR			0x091c
+#define SOURCE_CIPHER_CAR		0x0920
+#define SOURCE_CRYPTO_CAR		0x0924
+
+/* clock meters addr */
+#define CM_CTRL				0x0a00
+#define CM_I2S_CTRL			0x0a04
+#define CM_SPDIF_CTRL			0x0a08
+#define CM_VID_CTRL			0x0a0c
+#define CM_LANE_CTRL			0x0a10
+#define I2S_NM_STABLE			0x0a14
+#define I2S_NCTS_STABLE			0x0a18
+#define SPDIF_NM_STABLE			0x0a1c
+#define SPDIF_NCTS_STABLE		0x0a20
+#define NMVID_MEAS_STABLE		0x0a24
+#define I2S_MEAS			0x0a40
+#define SPDIF_MEAS			0x0a80
+#define NMVID_MEAS			0x0ac0
+
+/* source vif addr */
+#define BND_HSYNC2VSYNC			0x0b00
+#define HSYNC2VSYNC_F1_L1		0x0b04
+#define HSYNC2VSYNC_F2_L1		0x0b08
+#define HSYNC2VSYNC_STATUS		0x0b0c
+#define HSYNC2VSYNC_POL_CTRL		0x0b10
+
+/* dptx phy addr */
+#define DP_TX_PHY_CONFIG_REG		0x2000
+#define DP_TX_PHY_STATUS_REG		0x2004
+#define DP_TX_PHY_SW_RESET		0x2008
+#define DP_TX_PHY_SCRAMBLER_SEED	0x200c
+#define DP_TX_PHY_TRAINING_01_04	0x2010
+#define DP_TX_PHY_TRAINING_05_08	0x2014
+#define DP_TX_PHY_TRAINING_09_10	0x2018
+#define TEST_COR			0x23fc
+
+/* dptx hpd addr */
+#define HPD_IRQ_DET_MIN_TIMER		0x2100
+#define HPD_IRQ_DET_MAX_TIMER		0x2104
+#define HPD_UNPLGED_DET_MIN_TIMER	0x2108
+#define HPD_STABLE_TIMER		0x210c
+#define HPD_FILTER_TIMER		0x2110
+#define HPD_EVENT_MASK			0x211c
+#define HPD_EVENT_DET			0x2120
+
+/* dpyx framer addr */
+#define DP_FRAMER_GLOBAL_CONFIG		0x2200
+#define DP_SW_RESET			0x2204
+#define DP_FRAMER_TU			0x2208
+#define DP_FRAMER_PXL_REPR		0x220c
+#define DP_FRAMER_SP			0x2210
+#define AUDIO_PACK_CONTROL		0x2214
+#define DP_VC_TABLE(x)			(0x2218 + ((x) << 2))
+#define DP_VB_ID			0x2258
+#define DP_MTPH_LVP_CONTROL		0x225c
+#define DP_MTPH_SYMBOL_VALUES		0x2260
+#define DP_MTPH_ECF_CONTROL		0x2264
+#define DP_MTPH_ACT_CONTROL		0x2268
+#define DP_MTPH_STATUS			0x226c
+#define DP_INTERRUPT_SOURCE		0x2270
+#define DP_INTERRUPT_MASK		0x2274
+#define DP_FRONT_BACK_PORCH		0x2278
+#define DP_BYTE_COUNT			0x227c
+
+/* dptx stream addr */
+#define MSA_HORIZONTAL_0		0x2280
+#define MSA_HORIZONTAL_1		0x2284
+#define MSA_VERTICAL_0			0x2288
+#define MSA_VERTICAL_1			0x228c
+#define MSA_MISC			0x2290
+#define STREAM_CONFIG			0x2294
+#define AUDIO_PACK_STATUS		0x2298
+#define VIF_STATUS			0x229c
+#define PCK_STUFF_STATUS_0		0x22a0
+#define PCK_STUFF_STATUS_1		0x22a4
+#define INFO_PACK_STATUS		0x22a8
+#define RATE_GOVERNOR_STATUS		0x22ac
+#define DP_HORIZONTAL			0x22b0
+#define DP_VERTICAL_0			0x22b4
+#define DP_VERTICAL_1			0x22b8
+#define DP_BLOCK_SDP			0x22bc
+
+/* dptx glbl addr */
+#define DPTX_LANE_EN			0x2300
+#define DPTX_ENHNCD			0x2304
+#define DPTX_INT_MASK			0x2308
+#define DPTX_INT_STATUS			0x230c
+
+/* dp aux addr */
+#define DP_AUX_HOST_CONTROL		0x2800
+#define DP_AUX_INTERRUPT_SOURCE		0x2804
+#define DP_AUX_INTERRUPT_MASK		0x2808
+#define DP_AUX_SWAP_INVERSION_CONTROL	0x280c
+#define DP_AUX_SEND_NACK_TRANSACTION	0x2810
+#define DP_AUX_CLEAR_RX			0x2814
+#define DP_AUX_CLEAR_TX			0x2818
+#define DP_AUX_TIMER_STOP		0x281c
+#define DP_AUX_TIMER_CLEAR		0x2820
+#define DP_AUX_RESET_SW			0x2824
+#define DP_AUX_DIVIDE_2M		0x2828
+#define DP_AUX_TX_PREACHARGE_LENGTH	0x282c
+#define DP_AUX_FREQUENCY_1M_MAX		0x2830
+#define DP_AUX_FREQUENCY_1M_MIN		0x2834
+#define DP_AUX_RX_PRE_MIN		0x2838
+#define DP_AUX_RX_PRE_MAX		0x283c
+#define DP_AUX_TIMER_PRESET		0x2840
+#define DP_AUX_NACK_FORMAT		0x2844
+#define DP_AUX_TX_DATA			0x2848
+#define DP_AUX_RX_DATA			0x284c
+#define DP_AUX_TX_STATUS		0x2850
+#define DP_AUX_RX_STATUS		0x2854
+#define DP_AUX_RX_CYCLE_COUNTER		0x2858
+#define DP_AUX_MAIN_STATES		0x285c
+#define DP_AUX_MAIN_TIMER		0x2860
+#define DP_AUX_AFE_OUT			0x2864
+
+/* crypto addr */
+#define CRYPTO_HDCP_REVISION		0x5800
+#define HDCP_CRYPTO_CONFIG		0x5804
+#define CRYPTO_INTERRUPT_SOURCE		0x5808
+#define CRYPTO_INTERRUPT_MASK		0x580c
+#define CRYPTO22_CONFIG			0x5818
+#define CRYPTO22_STATUS			0x581c
+#define SHA_256_DATA_IN			0x583c
+#define SHA_256_DATA_OUT_(x)		(0x5850 + ((x) << 2))
+#define AES_32_KEY_(x)			(0x5870 + ((x) << 2))
+#define AES_32_DATA_IN			0x5880
+#define AES_32_DATA_OUT_(x)		(0x5884 + ((x) << 2))
+#define CRYPTO14_CONFIG			0x58a0
+#define CRYPTO14_STATUS			0x58a4
+#define CRYPTO14_PRNM_OUT		0x58a8
+#define CRYPTO14_KM_0			0x58ac
+#define CRYPTO14_KM_1			0x58b0
+#define CRYPTO14_AN_0			0x58b4
+#define CRYPTO14_AN_1			0x58b8
+#define CRYPTO14_YOUR_KSV_0		0x58bc
+#define CRYPTO14_YOUR_KSV_1		0x58c0
+#define CRYPTO14_MI_0			0x58c4
+#define CRYPTO14_MI_1			0x58c8
+#define CRYPTO14_TI_0			0x58cc
+#define CRYPTO14_KI_0			0x58d0
+#define CRYPTO14_KI_1			0x58d4
+#define CRYPTO14_BLOCKS_NUM		0x58d8
+#define CRYPTO14_KEY_MEM_DATA_0		0x58dc
+#define CRYPTO14_KEY_MEM_DATA_1		0x58e0
+#define CRYPTO14_SHA1_MSG_DATA		0x58e4
+#define CRYPTO14_SHA1_V_VALUE_(x)	(0x58e8 + ((x) << 2))
+#define TRNG_CTRL			0x58fc
+#define TRNG_DATA_RDY			0x5900
+#define TRNG_DATA			0x5904
+
+/* cipher addr */
+#define HDCP_REVISION			0x60000
+#define INTERRUPT_SOURCE		0x60004
+#define INTERRUPT_MASK			0x60008
+#define HDCP_CIPHER_CONFIG		0x6000c
+#define AES_128_KEY_0			0x60010
+#define AES_128_KEY_1			0x60014
+#define AES_128_KEY_2			0x60018
+#define AES_128_KEY_3			0x6001c
+#define AES_128_RANDOM_0		0x60020
+#define AES_128_RANDOM_1		0x60024
+#define CIPHER14_KM_0			0x60028
+#define CIPHER14_KM_1			0x6002c
+#define CIPHER14_STATUS			0x60030
+#define CIPHER14_RI_PJ_STATUS		0x60034
+#define CIPHER_MODE			0x60038
+#define CIPHER14_AN_0			0x6003c
+#define CIPHER14_AN_1			0x60040
+#define CIPHER22_AUTH			0x60044
+#define CIPHER14_R0_DP_STATUS		0x60048
+#define CIPHER14_BOOTSTRAP		0x6004c
+
+#define DPTX_FRMR_DATA_CLK_RSTN_EN	BIT(11)
+#define DPTX_FRMR_DATA_CLK_EN		BIT(10)
+#define DPTX_PHY_DATA_RSTN_EN		BIT(9)
+#define DPTX_PHY_DATA_CLK_EN		BIT(8)
+#define DPTX_PHY_CHAR_RSTN_EN		BIT(7)
+#define DPTX_PHY_CHAR_CLK_EN		BIT(6)
+#define SOURCE_AUX_SYS_CLK_RSTN_EN	BIT(5)
+#define SOURCE_AUX_SYS_CLK_EN		BIT(4)
+#define DPTX_SYS_CLK_RSTN_EN		BIT(3)
+#define DPTX_SYS_CLK_EN			BIT(2)
+#define CFG_DPTX_VIF_CLK_RSTN_EN	BIT(1)
+#define CFG_DPTX_VIF_CLK_EN		BIT(0)
+
+#define SOURCE_PHY_RSTN_EN		BIT(1)
+#define SOURCE_PHY_CLK_EN		BIT(0)
+
+#define SOURCE_PKT_SYS_RSTN_EN		BIT(3)
+#define SOURCE_PKT_SYS_CLK_EN		BIT(2)
+#define SOURCE_PKT_DATA_RSTN_EN		BIT(1)
+#define SOURCE_PKT_DATA_CLK_EN		BIT(0)
+
+#define SPDIF_CDR_CLK_RSTN_EN		BIT(5)
+#define SPDIF_CDR_CLK_EN		BIT(4)
+#define SOURCE_AIF_SYS_RSTN_EN		BIT(3)
+#define SOURCE_AIF_SYS_CLK_EN		BIT(2)
+#define SOURCE_AIF_CLK_RSTN_EN		BIT(1)
+#define SOURCE_AIF_CLK_EN		BIT(0)
+
+#define SOURCE_CIPHER_SYSTEM_CLK_RSTN_EN	BIT(3)
+#define SOURCE_CIPHER_SYS_CLK_EN		BIT(2)
+#define SOURCE_CIPHER_CHAR_CLK_RSTN_EN		BIT(1)
+#define SOURCE_CIPHER_CHAR_CLK_EN		BIT(0)
+
+#define SOURCE_CRYPTO_SYS_CLK_RSTN_EN	BIT(1)
+#define SOURCE_CRYPTO_SYS_CLK_EN	BIT(0)
+
+#define APB_IRAM_PATH			BIT(2)
+#define APB_DRAM_PATH			BIT(1)
+#define APB_XT_RESET			BIT(0)
+
+#define MAILBOX_INT_MASK_BIT		BIT(1)
+#define PIF_INT_MASK_BIT		BIT(0)
+#define ALL_INT_MASK			3
+
+/* mailbox */
+#define MB_OPCODE_ID			0
+#define MB_MODULE_ID			1
+#define MB_SIZE_MSB_ID			2
+#define MB_SIZE_LSB_ID			3
+#define MB_DATA_ID			4
+
+#define MB_MODULE_ID_DP_TX		0x01
+#define MB_MODULE_ID_HDCP_TX		0x07
+#define MB_MODULE_ID_HDCP_RX		0x08
+#define MB_MODULE_ID_HDCP_GENERAL	0x09
+#define MB_MODULE_ID_GENERAL		0x0a
+
+/* general opcode */
+#define GENERAL_MAIN_CONTROL            0x01
+#define GENERAL_TEST_ECHO               0x02
+#define GENERAL_BUS_SETTINGS            0x03
+#define GENERAL_TEST_ACCESS             0x04
+
+#define DPTX_SET_POWER_MNG			0x00
+#define DPTX_SET_HOST_CAPABILITIES		0x01
+#define DPTX_GET_EDID				0x02
+#define DPTX_READ_DPCD				0x03
+#define DPTX_WRITE_DPCD				0x04
+#define DPTX_ENABLE_EVENT			0x05
+#define DPTX_WRITE_REGISTER			0x06
+#define DPTX_READ_REGISTER			0x07
+#define DPTX_WRITE_FIELD			0x08
+#define DPTX_TRAINING_CONTROL			0x09
+#define DPTX_READ_EVENT				0x0a
+#define DPTX_READ_LINK_STAT			0x0b
+#define DPTX_SET_VIDEO				0x0c
+#define DPTX_SET_AUDIO				0x0d
+#define DPTX_GET_LAST_AUX_STAUS			0x0e
+#define DPTX_SET_LINK_BREAK_POINT		0x0f
+#define DPTX_FORCE_LANES			0x10
+#define DPTX_HPD_STATE				0x11
+
+#define DPTX_EVENT_ENABLE_HPD			BIT(0)
+#define DPTX_EVENT_ENABLE_TRAINING		BIT(1)
+
+#define LINK_TRAINING_NOT_ACTIVE		0
+#define LINK_TRAINING_RUN			1
+#define LINK_TRAINING_RESTART			2
+
+#define CONTROL_VIDEO_IDLE			0
+#define CONTROL_VIDEO_VALID			1
+
+#define VIF_BYPASS_INTERLACE			BIT(13)
+#define INTERLACE_FMT_DET			BIT(12)
+#define INTERLACE_DTCT_WIN			0x20
+
+#define DP_FRAMER_SP_INTERLACE_EN		BIT(2)
+#define DP_FRAMER_SP_HSP			BIT(1)
+#define DP_FRAMER_SP_VSP			BIT(0)
+
+/* capability */
+#define AUX_HOST_INVERT				3
+#define	FAST_LT_SUPPORT				1
+#define FAST_LT_NOT_SUPPORT			0
+#define LANE_MAPPING_NORMAL			0xe4
+#define LANE_MAPPING_FLIPPED			0x1b
+#define ENHANCED				1
+
+#define	FULL_LT_STARTED				BIT(0)
+#define FASE_LT_STARTED				BIT(1)
+#define CLK_RECOVERY_FINISHED			BIT(2)
+#define EQ_PHASE_FINISHED			BIT(3)
+#define FASE_LT_START_FINISHED			BIT(4)
+#define CLK_RECOVERY_FAILED			BIT(5)
+#define EQ_PHASE_FAILED				BIT(6)
+#define FASE_LT_FAILED				BIT(7)
+
+#define DPTX_HPD_EVENT				BIT(0)
+#define DPTX_TRAINING_EVENT			BIT(1)
+#define HDCP_TX_STATUS_EVENT			BIT(4)
+#define HDCP2_TX_IS_KM_STORED_EVENT		BIT(5)
+#define HDCP2_TX_STORE_KM_EVENT			BIT(6)
+#define HDCP_TX_IS_RECEIVER_ID_VALID_EVENT	BIT(7)
+
+#define TU_SIZE					30
+#define CDN_DP_MAX_LINK_RATE			DP_LINK_BW_5_4
+
+/* audio */
+#define AUDIO_PACK_EN				BIT(8)
+#define SAMPLING_FREQ(x)			(((x) & 0xf) << 16)
+#define ORIGINAL_SAMP_FREQ(x)			(((x) & 0xf) << 24)
+#define SYNC_WR_TO_CH_ZERO			BIT(1)
+#define I2S_DEC_START				BIT(1)
+#define AUDIO_SW_RST				BIT(0)
+#define SMPL2PKT_EN				BIT(1)
+#define MAX_NUM_CH(x)				(((x) & 0x1f) - 1)
+#define NUM_OF_I2S_PORTS(x)			((((x) / 2 - 1) & 0x3) << 5)
+#define AUDIO_TYPE_LPCM				(2 << 7)
+#define CFG_SUB_PCKT_NUM(x)			((((x) - 1) & 0x7) << 11)
+#define AUDIO_CH_NUM(x)				((((x) - 1) & 0x1f) << 2)
+#define TRANS_SMPL_WIDTH_16			0
+#define TRANS_SMPL_WIDTH_24			BIT(11)
+#define TRANS_SMPL_WIDTH_32			(2 << 11)
+#define I2S_DEC_PORT_EN(x)			(((x) & 0xf) << 17)
+#define SPDIF_ENABLE				BIT(21)
+#define SPDIF_AVG_SEL				BIT(20)
+#define SPDIF_JITTER_BYPASS			BIT(19)
+#define SPDIF_FIFO_MID_RANGE(x)			(((x) & 0xff) << 11)
+#define SPDIF_JITTER_THRSH(x)			(((x) & 0xff) << 3)
+#define SPDIF_JITTER_AVG_WIN(x)			((x) & 0x7)
+
+/* Refernce cycles when using lane clock as refernce */
+#define LANE_REF_CYC				0x8000
+
+enum voltage_swing_level {
+	VOLTAGE_LEVEL_0,
+	VOLTAGE_LEVEL_1,
+	VOLTAGE_LEVEL_2,
+	VOLTAGE_LEVEL_3,
+};
+
+enum pre_emphasis_level {
+	PRE_EMPHASIS_LEVEL_0,
+	PRE_EMPHASIS_LEVEL_1,
+	PRE_EMPHASIS_LEVEL_2,
+	PRE_EMPHASIS_LEVEL_3,
+};
+
+enum pattern_set {
+	PTS1		= BIT(0),
+	PTS2		= BIT(1),
+	PTS3		= BIT(2),
+	PTS4		= BIT(3),
+	DP_NONE		= BIT(4)
+};
+
+enum vic_color_depth {
+	BCS_6 = 0x1,
+	BCS_8 = 0x2,
+	BCS_10 = 0x4,
+	BCS_12 = 0x8,
+	BCS_16 = 0x10,
+};
+
+enum vic_bt_type {
+	BT_601 = 0x0,
+	BT_709 = 0x1,
+};
+
+void cdn_dp_clock_reset(struct cdn_dp_device *dp);
+
+void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, u32 clk);
+int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
+			 u32 i_size, const u32 *d_mem, u32 d_size);
+int cdn_dp_active(struct cdn_dp_device *dp, bool enable);
+int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes);
+int cdn_dp_event_config(struct cdn_dp_device *dp);
+u32 cdn_dp_get_event(struct cdn_dp_device *dp);
+int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
+int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value);
+int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len);
+int cdn_dp_get_edid_block(void *dp, u8 *edid,
+			  unsigned int block, size_t length);
+int cdn_dp_training_start(struct cdn_dp_device *dp);
+int cdn_dp_get_training_status(struct cdn_dp_device *dp);
+int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active);
+int cdn_dp_config_video(struct cdn_dp_device *dp);
+int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio);
+int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable);
+int cdn_dp_audio_config_set(struct cdn_dp_device *dp, struct audio_info *audio);
+#endif /* _CDN_DP_REG_H */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index edd7ec2..ceb4b86 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -969,9 +969,9 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
 		vop_dsp_hold_valid_irq_disable(vop);
 	}
 
-	pin_pol = 0x8;
-	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1;
-	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : (1 << 1);
+	pin_pol = BIT(DCLK_INVERT);
+	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : BIT(HSYNC_POSITIVE);
+	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : BIT(VSYNC_POSITIVE);
 	VOP_CTRL_SET(vop, pin_pol, pin_pol);
 
 	switch (s->output_type) {
@@ -991,6 +991,11 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
 		VOP_CTRL_SET(vop, mipi_pin_pol, pin_pol);
 		VOP_CTRL_SET(vop, mipi_en, 1);
 		break;
+	case DRM_MODE_CONNECTOR_DisplayPort:
+		pin_pol &= ~BIT(DCLK_INVERT);
+		VOP_CTRL_SET(vop, dp_pin_pol, pin_pol);
+		VOP_CTRL_SET(vop, dp_en, 1);
+		break;
 	default:
 		DRM_ERROR("unsupport connector_type[%d]\n", s->output_type);
 	}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index ff4f52e..4820a8b 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -45,6 +45,7 @@ struct vop_ctrl {
 	struct vop_reg edp_en;
 	struct vop_reg hdmi_en;
 	struct vop_reg mipi_en;
+	struct vop_reg dp_en;
 	struct vop_reg out_mode;
 	struct vop_reg dither_down;
 	struct vop_reg dither_up;
@@ -53,6 +54,7 @@ struct vop_ctrl {
 	struct vop_reg hdmi_pin_pol;
 	struct vop_reg edp_pin_pol;
 	struct vop_reg mipi_pin_pol;
+	struct vop_reg dp_pin_pol;
 
 	struct vop_reg htotal_pw;
 	struct vop_reg hact_st_end;
@@ -242,6 +244,13 @@ enum scale_down_mode {
 	SCALE_DOWN_AVG = 0x1
 };
 
+enum vop_pol {
+	HSYNC_POSITIVE = 0,
+	VSYNC_POSITIVE = 1,
+	DEN_NEGATIVE   = 2,
+	DCLK_INVERT    = 3
+};
+
 #define FRAC_16_16(mult, div)    (((mult) << 16) / (div))
 #define SCL_FT_DEFAULT_FIXPOINT_SHIFT	12
 #define SCL_MAX_VSKIPLINES		4
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 5b1ae1f..dcf172e 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -281,6 +281,7 @@ static const struct vop_data rk3288_vop = {
 static const struct vop_ctrl rk3399_ctrl_data = {
 	.standby = VOP_REG(RK3399_SYS_CTRL, 0x1, 22),
 	.gate_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 23),
+	.dp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 11),
 	.rgb_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 12),
 	.hdmi_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 13),
 	.edp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 14),
@@ -290,6 +291,7 @@ static const struct vop_ctrl rk3399_ctrl_data = {
 	.data_blank = VOP_REG(RK3399_DSP_CTRL0, 0x1, 19),
 	.out_mode = VOP_REG(RK3399_DSP_CTRL0, 0xf, 0),
 	.rgb_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16),
+	.dp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16),
 	.hdmi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 20),
 	.edp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 24),
 	.mipi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 28),
-- 
2.6.3



From zyw at rock-chips.com  Fri Jul 15 01:18:13 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Fri, 15 Jul 2016 16:18:13 +0800
Subject: [v5.2 PATCH 5/5] drm/rockchip: cdn-dp: add cdn DP support for rk3399
In-Reply-To: <CAOw6vbKCCoQHO-4TB3qCOuAA09aheXx0H6jRjQQnrkb4KqmtCg@mail.gmail.com>
References: <CAOw6vbKCCoQHO-4TB3qCOuAA09aheXx0H6jRjQQnrkb4KqmtCg@mail.gmail.com>
Message-ID: <1468570693-25198-1-git-send-email-zyw@rock-chips.com>

Add support for cdn DP controller which is embedded in the rk3399
SoCs. The DP is compliant with DisplayPort Specification,
Version 1.3, This IP is compatible with the rockchip type-c PHY IP.
There is a uCPU in DP controller, it need a firmware to work,
please put the firmware file to /lib/firmware/cdn/dptx.bin. The
uCPU in charge of aux communication and link training, the host use
mailbox to communicate with the ucpu.
The dclk pin_pol of vop must not be invert for DP.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>

---

Changes in v5.2:
- fixed the fw_wait always 0

Changes in v5.1:
- modify according to Sean Paul's comments

Changes in v5:
- alphabetical order
- do not use long, use u32 or u64
- return MODE_CLOCK_HIGH when requested > actual
- Optimized Coding Style
- add a formula to get better tu size and symbol value.

Changes in v4:
- use phy framework to control DP phy
- support 2 phys

Changes in v3:
- use EXTCON_DISP_DP and EXTCON_DISP_DP_ALT cable to get dp port state.
- reset spdif before config it
- modify the firmware clk to 100Mhz
- retry load firmware if fw file is requested too early

Changes in v2:
- Alphabetic order
- remove excess error message
- use define clk_rate
- check all return value
- remove dev_set_name(dp->dev, "cdn-dp");
- use schedule_delayed_work
- remove never-called functions
- remove some unnecessary ()

Changes in v1:
- use extcon API
- use hdmi-codec for the DP Asoc
- do not initialize the "ret"
- printk a err log when drm_of_encoder_active_endpoint_id
- modify the dclk pin_pol to a single line

 drivers/gpu/drm/rockchip/Kconfig            |   9 +
 drivers/gpu/drm/rockchip/Makefile           |   1 +
 drivers/gpu/drm/rockchip/cdn-dp-core.c      | 842 ++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/cdn-dp-core.h      |  99 +++
 drivers/gpu/drm/rockchip/cdn-dp-reg.c       | 898 ++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/cdn-dp-reg.h       | 479 +++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c |  11 +-
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   9 +
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c |   2 +
 9 files changed, 2347 insertions(+), 3 deletions(-)
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.c
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.h
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.c
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.h

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index d30bdc3..a04446a 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -25,6 +25,15 @@ config ROCKCHIP_ANALOGIX_DP
 	  for the Analogix Core DP driver. If you want to enable DP
 	  on RK3288 based SoC, you should selet this option.
 
+config ROCKCHIP_CDN_DP
+        tristate "Rockchip cdn DP"
+        depends on DRM_ROCKCHIP
+        help
+	  This selects support for Rockchip SoC specific extensions
+	  for the cdn DP driver. If you want to enable Dp on
+	  RK3399 based SoC, you should select this
+	  option.
+
 config ROCKCHIP_DW_HDMI
         tristate "Rockchip specific extensions for Synopsys DW HDMI"
         depends on DRM_ROCKCHIP
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index 05d0713..abdecd5 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -7,6 +7,7 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
 rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
 
 obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
+obj-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
 obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
 obj-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
 obj-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
new file mode 100644
index 0000000..ed38dbc
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
@@ -0,0 +1,842 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Chris Zhong <zyw at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_of.h>
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/extcon.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/mfd/syscon.h>
+#include <linux/phy/phy.h>
+
+#include <sound/hdmi-codec.h>
+
+#include "cdn-dp-core.h"
+#include "cdn-dp-reg.h"
+#include "rockchip_drm_vop.h"
+
+#define connector_to_dp(c) \
+		container_of(c, struct cdn_dp_device, connector)
+
+#define encoder_to_dp(c) \
+		container_of(c, struct cdn_dp_device, encoder)
+
+/* dp grf register offset */
+#define DP_VOP_SEL		0x6224
+#define DP_SEL_VOP_LIT		BIT(12)
+#define MAX_FW_WAIT_SECS	64
+#define CDN_DP_FIRMWARE		"rockchip/dptx.bin"
+
+struct cdn_dp_data {
+	u8 max_phy;
+};
+
+struct cdn_dp_data rk3399_cdn_dp = {
+	.max_phy = 2,
+};
+
+static const struct of_device_id cdn_dp_dt_ids[] = {
+	{ .compatible = "rockchip,rk3399-cdn-dp",
+		.data = (void *)&rk3399_cdn_dp },
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, cdn_dp_dt_ids);
+
+static int cdn_dp_clk_enable(struct cdn_dp_device *dp)
+{
+	int ret;
+	u32 rate;
+
+	ret = clk_prepare_enable(dp->pclk);
+	if (ret < 0) {
+		dev_err(dp->dev, "cannot enable dp pclk %d\n", ret);
+		goto err_pclk;
+	}
+
+	ret = clk_prepare_enable(dp->core_clk);
+	if (ret < 0) {
+		dev_err(dp->dev, "cannot enable core_clk %d\n", ret);
+		goto err_core_clk;
+	}
+
+	rate = clk_get_rate(dp->core_clk);
+	if (rate < 0) {
+		dev_err(dp->dev, "get clk rate failed: %d\n", rate);
+		goto err_set_rate;
+	}
+
+	cdn_dp_set_fw_clk(dp, rate);
+
+	return 0;
+
+err_set_rate:
+	clk_disable_unprepare(dp->core_clk);
+err_core_clk:
+	clk_disable_unprepare(dp->pclk);
+err_pclk:
+	return ret;
+}
+
+static enum drm_connector_status
+cdn_dp_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct cdn_dp_device *dp = connector_to_dp(connector);
+
+	return dp->hpd_status;
+}
+
+static void cdn_dp_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+static struct drm_connector_funcs cdn_dp_atomic_connector_funcs = {
+	.dpms = drm_atomic_helper_connector_dpms,
+	.detect = cdn_dp_connector_detect,
+	.destroy = cdn_dp_connector_destroy,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int cdn_dp_connector_get_modes(struct drm_connector *connector)
+{
+	struct cdn_dp_device *dp = connector_to_dp(connector);
+	struct edid *edid;
+	int ret = 0;
+
+	edid = drm_do_get_edid(connector, cdn_dp_get_edid_block, dp);
+	if (edid) {
+		dev_dbg(dp->dev, "got edid: width[%d] x height[%d]\n",
+			edid->width_cm, edid->height_cm);
+
+		dp->sink_has_audio = drm_detect_monitor_audio(edid);
+		ret = drm_add_edid_modes(connector, edid);
+		if (ret) {
+			drm_mode_connector_update_edid_property(connector,
+								edid);
+			drm_edid_to_eld(connector, edid);
+		}
+		kfree(edid);
+	} else {
+		dev_warn(dp->dev, "failed to get edid\n");
+	}
+
+	return ret;
+}
+
+static struct drm_encoder *
+	cdn_dp_connector_best_encoder(struct drm_connector *connector)
+{
+	struct cdn_dp_device *dp = connector_to_dp(connector);
+
+	return &dp->encoder;
+}
+
+static int cdn_dp_connector_mode_valid(struct drm_connector *connector,
+				       struct drm_display_mode *mode)
+{
+	struct cdn_dp_device *dp = connector_to_dp(connector);
+	struct drm_display_info *display_info = &dp->connector.display_info;
+	u32 requested = mode->clock * display_info->bpc * 3 / 1000;
+	u32 sink_max, source_max, rate;
+	u8 lanes, id;
+	u32 actual;
+
+	/* find the running port id */
+	for (id = 0; id < dp->max_phy; id++) {
+		if (dp->phy_status[id] == 1)
+			break;
+	}
+
+	sink_max = drm_dp_max_lane_count(dp->dpcd);
+	source_max = dp->cap_lanes[id];
+	lanes = min(sink_max, source_max);
+
+	sink_max = drm_dp_max_link_rate(dp->dpcd);
+	source_max = drm_dp_bw_code_to_link_rate(CDN_DP_MAX_LINK_RATE);
+	rate = min(sink_max, source_max);
+
+	actual = rate * lanes / 100;
+
+	/* efficiency is about 0.8 */
+	actual = actual * 8 / 10;
+
+	if (requested > actual) {
+		dev_dbg(dp->dev, "requested=%d, actual=%d, clock=%d\n",
+			requested, actual, mode->clock);
+		return MODE_CLOCK_HIGH;
+	}
+
+	return MODE_OK;
+}
+
+static struct drm_connector_helper_funcs cdn_dp_connector_helper_funcs = {
+	.get_modes = cdn_dp_connector_get_modes,
+	.best_encoder = cdn_dp_connector_best_encoder,
+	.mode_valid = cdn_dp_connector_mode_valid,
+};
+
+static void cdn_dp_commit(struct drm_encoder *encoder)
+{
+	struct cdn_dp_device *dp = encoder_to_dp(encoder);
+	int ret;
+
+	ret = cdn_dp_training_start(dp);
+	if (ret) {
+		dev_err(dp->dev, "link training failed: %d\n", ret);
+		return;
+	}
+
+	ret = cdn_dp_get_training_status(dp);
+	if (ret) {
+		dev_err(dp->dev, "get link training status failed: %d\n", ret);
+		return;
+	}
+
+	dev_info(dp->dev, "rate:%d, lanes:%d\n",
+		 dp->link.rate, dp->link.num_lanes);
+
+	if (cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE))
+		return;
+
+	if (cdn_dp_config_video(dp)) {
+		dev_err(dp->dev, "unable to config video\n");
+		return;
+	}
+
+	if (cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID))
+		return;
+
+	dp->dpms_mode = DRM_MODE_DPMS_ON;
+}
+
+static void cdn_dp_encoder_mode_set(struct drm_encoder *encoder,
+				    struct drm_display_mode *mode,
+				    struct drm_display_mode *adjusted)
+{
+	struct cdn_dp_device *dp = encoder_to_dp(encoder);
+	struct drm_display_info *display_info = &dp->connector.display_info;
+	struct rockchip_crtc_state *state;
+	struct video_info *video = &dp->video_info;
+	int ret, val;
+
+	switch (display_info->bpc) {
+	case 16:
+	case 12:
+	case 10:
+		video->color_depth = 10;
+		break;
+	case 6:
+		video->color_depth = 6;
+		break;
+	default:
+		video->color_depth = 8;
+		break;
+	}
+
+	video->color_fmt = PXL_RGB;
+
+	video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC);
+	video->h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC);
+
+	ret = drm_of_encoder_active_endpoint_id(dp->dev->of_node, encoder);
+	if (ret < 0) {
+		dev_err(dp->dev, "Could not get vop id, %d", ret);
+		return;
+	}
+
+	dev_dbg(dp->dev, "vop %s output to cdn-dp\n", (ret) ? "LIT" : "BIG");
+	state = to_rockchip_crtc_state(encoder->crtc->state);
+	if (ret) {
+		val = DP_SEL_VOP_LIT | (DP_SEL_VOP_LIT << 16);
+		state->output_mode = ROCKCHIP_OUT_MODE_P888;
+	} else {
+		val = DP_SEL_VOP_LIT << 16;
+		state->output_mode = ROCKCHIP_OUT_MODE_AAAA;
+	}
+	ret = regmap_write(dp->grf, DP_VOP_SEL, val);
+	if (ret != 0)
+		dev_err(dp->dev, "Could not write to GRF: %d\n", ret);
+
+	memcpy(&dp->mode, adjusted, sizeof(*mode));
+}
+
+static void cdn_dp_encoder_enable(struct drm_encoder *encoder)
+{
+	struct cdn_dp_device *dp = encoder_to_dp(encoder);
+
+	if (dp->dpms_mode != DRM_MODE_DPMS_ON)
+		cdn_dp_commit(encoder);
+}
+
+static void cdn_dp_encoder_disable(struct drm_encoder *encoder)
+{
+	struct cdn_dp_device *dp = encoder_to_dp(encoder);
+
+	if (dp->dpms_mode != DRM_MODE_DPMS_OFF) {
+		cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE);
+		dp->dpms_mode = DRM_MODE_DPMS_OFF;
+	}
+}
+
+static int
+cdn_dp_encoder_atomic_check(struct drm_encoder *encoder,
+			    struct drm_crtc_state *crtc_state,
+			    struct drm_connector_state *conn_state)
+{
+	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+
+	s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
+	s->output_type = DRM_MODE_CONNECTOR_DisplayPort;
+
+	return 0;
+}
+
+static struct drm_encoder_helper_funcs cdn_dp_encoder_helper_funcs = {
+	.mode_set = cdn_dp_encoder_mode_set,
+	.enable = cdn_dp_encoder_enable,
+	.disable = cdn_dp_encoder_disable,
+	.atomic_check = cdn_dp_encoder_atomic_check,
+};
+
+static struct drm_encoder_funcs cdn_dp_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+static int cdn_dp_firmware_init(struct cdn_dp_device *dp)
+{
+	int ret;
+	const u32 *iram_data, *dram_data;
+	const struct firmware *fw = dp->fw;
+	const struct cdn_firmware_header *hdr;
+
+	hdr = (struct cdn_firmware_header *)fw->data;
+	if (fw->size != le32_to_cpu(hdr->size_bytes)) {
+		dev_err(dp->dev, "firmware is invalid\n");
+		return -EINVAL;
+	}
+
+	iram_data = (const u32 *)(fw->data + hdr->header_size);
+	dram_data = (const u32 *)(fw->data + hdr->header_size + hdr->iram_size);
+
+	ret = cdn_dp_load_firmware(dp, iram_data, hdr->iram_size,
+				   dram_data, hdr->dram_size);
+	if (ret)
+		return ret;
+
+	dp->fw_loaded = 1;
+
+	ret = cdn_dp_active(dp, true);
+	if (ret) {
+		dev_err(dp->dev, "active ucpu failed: %d\n", ret);
+		return ret;
+	}
+
+	return cdn_dp_event_config(dp);
+}
+
+static int cdn_dp_init(struct cdn_dp_device *dp)
+{
+	struct device *dev = dp->dev;
+	struct device_node *np = dev->of_node;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct resource *res;
+	int ret;
+
+	dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+	if (IS_ERR(dp->grf)) {
+		dev_err(dev, "cdn-dp needs rockchip,grf property\n");
+		return PTR_ERR(dp->grf);
+	}
+
+	dp->irq = platform_get_irq(pdev, 0);
+	if (dp->irq < 0) {
+		dev_err(dev, "cdn-dp can not get irq\n");
+		return dp->irq;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	dp->regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(dp->regs)) {
+		dev_err(dev, "ioremap reg failed\n");
+		return PTR_ERR(dp->regs);
+	}
+
+	dp->core_clk = devm_clk_get(dev, "core-clk");
+	if (IS_ERR(dp->core_clk)) {
+		dev_err(dev, "cannot get core_clk_dp\n");
+		return PTR_ERR(dp->core_clk);
+	}
+
+	dp->pclk = devm_clk_get(dev, "pclk");
+	if (IS_ERR(dp->pclk)) {
+		dev_err(dev, "cannot get pclk\n");
+		return PTR_ERR(dp->pclk);
+	}
+
+	dp->spdif_clk = devm_clk_get(dev, "spdif");
+	if (IS_ERR(dp->spdif_clk)) {
+		dev_err(dev, "cannot get spdif_clk\n");
+		return PTR_ERR(dp->spdif_clk);
+	}
+
+	dp->spdif_rst = devm_reset_control_get(dev, "spdif");
+	if (IS_ERR(dp->spdif_rst)) {
+		dev_err(dev, "no spdif reset control found\n");
+		return PTR_ERR(dp->spdif_rst);
+	}
+
+	dp->dpms_mode = DRM_MODE_DPMS_OFF;
+
+	ret = cdn_dp_clk_enable(dp);
+	if (ret < 0)
+		return ret;
+
+	cdn_dp_clock_reset(dp);
+
+	return 0;
+}
+
+static int cdn_dp_audio_hw_params(struct device *dev,
+				  struct hdmi_codec_daifmt *daifmt,
+				  struct hdmi_codec_params *params)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	struct audio_info audio = {
+		.sample_width = params->sample_width,
+		.sample_rate = params->sample_rate,
+		.channels = params->channels,
+	};
+	int ret;
+
+	if (!dp->encoder.crtc)
+		return -ENODEV;
+
+	switch (daifmt->fmt) {
+	case HDMI_I2S:
+		audio.format = AFMT_I2S;
+		break;
+	case HDMI_SPDIF:
+		audio.format = AFMT_SPDIF;
+		break;
+	default:
+		dev_err(dev, "%s: Invalid format %d\n", __func__, daifmt->fmt);
+		return -EINVAL;
+	}
+
+	ret = cdn_dp_audio_config_set(dp, &audio);
+	if (!ret)
+		dp->audio_info = audio;
+
+	return ret;
+}
+
+static void cdn_dp_audio_shutdown(struct device *dev)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	int ret = cdn_dp_audio_stop(dp, &dp->audio_info);
+
+	if (!ret)
+		dp->audio_info.format = AFMT_UNUSED;
+}
+
+static int cdn_dp_audio_digital_mute(struct device *dev, bool enable)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+
+	return cdn_dp_audio_mute(dp, enable);
+}
+
+static int cdn_dp_audio_get_eld(struct device *dev, uint8_t *buf, size_t len)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	struct drm_mode_config *config = &dp->encoder.dev->mode_config;
+	struct drm_connector *connector;
+	int ret = -ENODEV;
+
+	mutex_lock(&config->mutex);
+	list_for_each_entry(connector, &config->connector_list, head) {
+		if (&dp->encoder == connector->encoder) {
+			memcpy(buf, connector->eld,
+			       min(sizeof(connector->eld), len));
+			ret = 0;
+		}
+	}
+	mutex_unlock(&config->mutex);
+
+	return ret;
+}
+
+static const struct hdmi_codec_ops audio_codec_ops = {
+	.hw_params = cdn_dp_audio_hw_params,
+	.audio_shutdown = cdn_dp_audio_shutdown,
+	.digital_mute = cdn_dp_audio_digital_mute,
+	.get_eld = cdn_dp_audio_get_eld,
+};
+
+static int cdn_dp_audio_codec_init(struct cdn_dp_device *dp,
+				   struct device *dev)
+{
+	struct hdmi_codec_pdata codec_data = {
+		.i2s = 1,
+		.spdif = 1,
+		.ops = &audio_codec_ops,
+		.max_i2s_channels = 8,
+	};
+
+	dp->audio_pdev = platform_device_register_data(
+			 dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO,
+			 &codec_data, sizeof(codec_data));
+
+	return PTR_ERR_OR_ZERO(dp->audio_pdev);
+}
+
+static int cdn_dp_get_cap_lanes(struct cdn_dp_device *dp,
+				struct extcon_dev *edev)
+{
+	bool dfp, dptx;
+	u8 lanes;
+
+	dfp = extcon_get_cable_state_(edev, EXTCON_USB_HOST);
+	dptx = extcon_get_cable_state_(edev, EXTCON_DISP_DP);
+
+	if (dfp && dptx)
+		lanes = 2;
+	else if (dptx)
+		lanes = 4;
+	else
+		lanes = 0;
+
+	return lanes;
+}
+
+static int cdn_dp_pd_event(struct notifier_block *nb,
+			   unsigned long event, void *priv)
+{
+	struct cdn_dp_device *dp;
+	struct extcon_dev *edev = priv;
+	u8 i;
+
+	dp = container_of(nb, struct cdn_dp_device, event_nb);
+
+	for (i = 0; i < dp->max_phy; i++) {
+		if (edev == dp->extcon[i]) {
+			dp->port_id = i;
+			break;
+		}
+	}
+
+	schedule_delayed_work(&dp->event_wq, 0);
+
+	return 0;
+}
+
+static void cdn_dp_pd_event_wq(struct work_struct *work)
+{
+	struct cdn_dp_device *dp = container_of(work, struct cdn_dp_device,
+						event_wq.work);
+	u8 id = dp->port_id, i;
+	u8 old_cap_lanes = dp->cap_lanes[id];
+	int ret;
+
+	dp->cap_lanes[id] = cdn_dp_get_cap_lanes(dp, dp->extcon[id]);
+	if (dp->cap_lanes[id] == old_cap_lanes)
+		return;
+
+	/* if phy is on, need reset it when dp lanes count is changed */
+	if (dp->phy_status[id]) {
+		ret = phy_power_off(dp->phy[id]);
+		if (ret) {
+			dev_err(dp->dev, "phy power off failed: %d", ret);
+			goto err_phy_power_off;
+		}
+		dp->phy_status[id] = 0;
+	}
+
+	/* if other phy is running, do not touch the hpd_status, and return */
+	for (i = 0; i < dp->max_phy; i++) {
+		if (dp->phy_status[i] == 1) {
+			dev_warn(dp->dev, "busy, phy[%d] is running", i);
+			goto err_phy_power_off;
+		}
+	}
+
+	if (!dp->cap_lanes[id]) {
+		dp->hpd_status = connector_status_disconnected;
+		drm_helper_hpd_irq_event(dp->drm_dev);
+		return;
+	}
+
+	if (!dp->fw_loaded) {
+		ret = request_firmware(&dp->fw, CDN_DP_FIRMWARE, dp->dev);
+		if (ret == -ENOENT && dp->fw_wait <= MAX_FW_WAIT_SECS) {
+			unsigned long time = msecs_to_jiffies(dp->fw_wait * HZ);
+
+			/*
+			 * If can not find the file, retry to load the firmware
+			 * in 1 second, if still failed after 1 minute, give up.
+			 */
+			schedule_delayed_work(&dp->event_wq, time);
+			dev_warn(dp->dev, "retry to request firmware in %d\n",
+				 dp->fw_wait);
+			dp->fw_wait *= 2;
+			goto err_phy_power_off;
+		} else if (ret) {
+			dev_err(dp->dev, "failed to request firmware: %d\n",
+				ret);
+			goto err_phy_power_off;
+		}
+	}
+
+	ret = phy_power_on(dp->phy[id]);
+	if (ret) {
+		dev_err(dp->dev, "phy power on failed: %d", ret);
+		if (!dp->fw_loaded)
+			release_firmware(dp->fw);
+		goto err_phy_power_off;
+	}
+
+	dp->phy_status[id] = 1;
+
+	if (!dp->fw_loaded) {
+		ret = cdn_dp_firmware_init(dp);
+		release_firmware(dp->fw);
+		if (ret) {
+			dev_err(dp->dev, "firmware init failed: %d", ret);
+			goto err_firmware;
+		}
+	}
+
+	/* read hpd status failed, or the hpd does not exist. */
+	ret = cdn_dp_get_hpd_status(dp);
+	if (ret <= 0) {
+		dev_err(dp->dev, "get hpd failed: %d", ret);
+		goto err_firmware;
+	}
+
+	ret = cdn_dp_set_host_cap(dp, dp->cap_lanes[id]);
+	if (ret) {
+		dev_err(dp->dev, "set host capabilities failed: %d\n", ret);
+		goto err_firmware;
+	}
+
+	/*
+	 * Native read with retry for link status and receiver capability reads
+	 * for cases where the sink may still be asleep.
+	 *
+	 * Sinks are *supposed* to come up within 1ms from an off state, but
+	 * we're also supposed to retry 3 times per the spec.
+	 */
+	for (i = 0; i < 3; i++) {
+		ret = cdn_dp_dpcd_read(dp, 0x000, dp->dpcd,
+				       DP_RECEIVER_CAP_SIZE);
+		if (!ret) {
+			dp->hpd_status = connector_status_connected;
+			drm_helper_hpd_irq_event(dp->drm_dev);
+			return;
+		}
+		usleep_range(1000, 1050);
+	}
+
+err_firmware:
+	ret = phy_power_off(dp->phy[id]);
+	if (ret)
+		dev_err(dp->dev, "phy power off failed: %d", ret);
+	else
+		dp->phy_status[id] = 0;
+
+err_phy_power_off:
+	dp->cap_lanes[id] = 0;
+}
+
+static int cdn_dp_bind(struct device *dev, struct device *master,
+		       void *data)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	struct drm_encoder *encoder;
+	struct drm_connector *connector;
+	struct drm_device *drm_dev = data;
+	int ret, i;
+
+	ret = cdn_dp_init(dp);
+	if (ret < 0)
+		return ret;
+
+	dp->drm_dev = drm_dev;
+	dp->hpd_status = connector_status_disconnected;
+	dp->fw_wait = 1;
+
+	encoder = &dp->encoder;
+
+	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
+							     dev->of_node);
+	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
+
+	ret = drm_encoder_init(drm_dev, encoder, &cdn_dp_encoder_funcs,
+			       DRM_MODE_ENCODER_TMDS, NULL);
+	if (ret) {
+		DRM_ERROR("failed to initialize encoder with drm\n");
+		return ret;
+	}
+
+	drm_encoder_helper_add(encoder, &cdn_dp_encoder_helper_funcs);
+
+	connector = &dp->connector;
+	connector->polled = DRM_CONNECTOR_POLL_HPD;
+	connector->dpms = DRM_MODE_DPMS_OFF;
+
+	ret = drm_connector_init(drm_dev, connector,
+				 &cdn_dp_atomic_connector_funcs,
+				 DRM_MODE_CONNECTOR_DisplayPort);
+	if (ret) {
+		DRM_ERROR("failed to initialize connector with drm\n");
+		goto err_free_encoder;
+	}
+
+	drm_connector_helper_add(connector, &cdn_dp_connector_helper_funcs);
+
+	ret = drm_mode_connector_attach_encoder(connector, encoder);
+	if (ret) {
+		DRM_ERROR("failed to attach connector and encoder\n");
+		goto err_free_connector;
+	}
+
+	cdn_dp_audio_codec_init(dp, dev);
+
+	dp->event_nb.notifier_call = cdn_dp_pd_event;
+	INIT_DELAYED_WORK(&dp->event_wq, cdn_dp_pd_event_wq);
+
+	for (i = 0; i < dp->max_phy; i++) {
+		if (IS_ERR(dp->extcon[i]))
+			continue;
+
+		ret = extcon_register_notifier(dp->extcon[i], EXTCON_DISP_DP,
+					       &dp->event_nb);
+		if (ret) {
+			dev_err(dev, "regitster EXTCON_DISP_DP notifier err\n");
+			return ret;
+		}
+
+		dp->port_id = i;
+		schedule_delayed_work(&dp->event_wq, 0);
+	}
+
+	return 0;
+
+err_free_connector:
+	drm_connector_cleanup(connector);
+err_free_encoder:
+	drm_encoder_cleanup(encoder);
+	return ret;
+}
+
+static void cdn_dp_unbind(struct device *dev, struct device *master, void *data)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	struct drm_encoder *encoder = &dp->encoder;
+	struct drm_connector *connector = &dp->connector;
+	int i;
+
+	platform_device_unregister(dp->audio_pdev);
+	cdn_dp_encoder_disable(encoder);
+	encoder->funcs->destroy(encoder);
+	connector->funcs->destroy(connector);
+
+	for (i = 0; i < dp->max_phy; i++) {
+		if (!IS_ERR(dp->extcon[i]))
+			extcon_unregister_notifier(dp->extcon[i], EXTCON_USB,
+						   &dp->event_nb);
+	}
+}
+
+static const struct component_ops cdn_dp_component_ops = {
+	.bind = cdn_dp_bind,
+	.unbind = cdn_dp_unbind,
+};
+
+static int cdn_dp_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	const struct of_device_id *match;
+	struct cdn_dp_data *dp_data;
+	struct cdn_dp_device *dp;
+	u8 count = 0;
+	int i;
+
+	dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
+	if (!dp)
+		return -ENOMEM;
+	dp->dev = dev;
+
+	match = of_match_node(cdn_dp_dt_ids, pdev->dev.of_node);
+	dp_data = (struct cdn_dp_data *)match->data;
+
+	dp->max_phy = dp_data->max_phy;
+
+	for (i = 0; i < dp->max_phy; i++) {
+		dp->extcon[i] = extcon_get_edev_by_phandle(dev, i);
+		dp->phy[i] = devm_of_phy_get_by_index(dev, dev->of_node, i);
+
+		if (!IS_ERR(dp->extcon[i]) && !IS_ERR(dp->phy[i]))
+			count++;
+
+		if (PTR_ERR(dp->extcon[i]) == -EPROBE_DEFER ||
+		    PTR_ERR(dp->phy[i]) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+	}
+
+	if (!count) {
+		dev_err(dev, "missing extcon or phy\n");
+		return -EINVAL;
+	}
+
+	dev_set_drvdata(dev, dp);
+
+	return component_add(dev, &cdn_dp_component_ops);
+}
+
+static int cdn_dp_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &cdn_dp_component_ops);
+
+	return 0;
+}
+
+static struct platform_driver cdn_dp_driver = {
+	.probe = cdn_dp_probe,
+	.remove = cdn_dp_remove,
+	.driver = {
+		   .name = "cdn-dp",
+		   .owner = THIS_MODULE,
+		   .of_match_table = of_match_ptr(cdn_dp_dt_ids),
+	},
+};
+
+module_platform_driver(cdn_dp_driver);
+
+MODULE_AUTHOR("Chris Zhong <zyw at rock-chips.com>");
+MODULE_DESCRIPTION("cdn DP Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
new file mode 100644
index 0000000..1c577bf
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 Chris Zhong <zyw at rock-chips.com>
+ * Copyright (C) 2016 ROCKCHIP, Inc.
+ *
+ * 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.
+ */
+
+#ifndef _CDN_DP_CORE_H
+#define _CDN_DP_CORE_H
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_panel.h>
+#include "rockchip_drm_drv.h"
+
+#define MAX_PHY		2
+
+enum audio_format {
+	AFMT_I2S = 0,
+	AFMT_SPDIF = 1,
+	AFMT_UNUSED,
+};
+
+struct audio_info {
+	enum audio_format format;
+	int sample_rate;
+	int channels;
+	int sample_width;
+};
+
+enum vic_pxl_encoding_format {
+	PXL_RGB = 0x1,
+	YCBCR_4_4_4 = 0x2,
+	YCBCR_4_2_2 = 0x4,
+	YCBCR_4_2_0 = 0x8,
+	Y_ONLY = 0x10,
+};
+
+struct video_info {
+	bool h_sync_polarity;
+	bool v_sync_polarity;
+	bool interlaced;
+	int color_depth;
+	enum vic_pxl_encoding_format color_fmt;
+};
+
+struct cdn_firmware_header {
+	u32 size_bytes; /* size of the entire header+image(s) in bytes */
+	u32 header_size; /* size of just the header in bytes */
+	u32 iram_size; /* size of iram */
+	u32 dram_size; /* size of dram */
+};
+
+struct cdn_dp_device {
+	struct device *dev;
+	struct drm_device *drm_dev;
+	struct drm_connector connector;
+	struct drm_encoder encoder;
+	struct drm_display_mode mode;
+	struct platform_device *audio_pdev;
+
+	const struct firmware *fw;	/* cdn dp firmware */
+	unsigned int fw_version;	/* cdn fw version */
+	u32 fw_wait;
+	bool fw_loaded;
+	void __iomem *regs;
+	struct regmap *grf;
+	unsigned int irq;
+	struct clk *core_clk;
+	struct clk *pclk;
+	struct clk *spdif_clk;
+	struct reset_control *spdif_rst;
+	struct audio_info audio_info;
+	struct video_info video_info;
+	struct extcon_dev *extcon[MAX_PHY];
+	struct phy *phy[MAX_PHY];
+	struct notifier_block event_nb;
+	struct delayed_work event_wq;
+	struct drm_dp_link link;
+
+	u8 cap_lanes[MAX_PHY];
+	u8 dpcd[DP_RECEIVER_CAP_SIZE];
+	u8 port_id;
+	u8 max_phy;
+	bool phy_status[MAX_PHY];
+	enum drm_connector_status hpd_status;
+
+	int dpms_mode;
+	bool sink_has_audio;
+};
+#endif  /* _CDN_DP_CORE_H */
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
new file mode 100644
index 0000000..afc8ae9
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
@@ -0,0 +1,898 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Chris Zhong <zyw at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/clk.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/reset.h>
+
+#include "cdn-dp-core.h"
+#include "cdn-dp-reg.h"
+
+#define CDN_DP_SPDIF_CLK		200000000
+#define FW_ALIVE_TIMEOUT_US		1000000
+#define MAILBOX_RETRY_US		1000
+#define MAILBOX_TIMEOUT_US		5000000
+#define LINK_TRAINING_RETRY_MS		20
+#define LINK_TRAINING_TIMEOUT_MS	500
+
+void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, u32 clk)
+{
+	writel(clk / 1000000, dp->regs + SW_CLK_H);
+}
+
+void cdn_dp_clock_reset(struct cdn_dp_device *dp)
+{
+	u32 val;
+
+	val = DPTX_FRMR_DATA_CLK_RSTN_EN |
+	      DPTX_FRMR_DATA_CLK_EN |
+	      DPTX_PHY_DATA_RSTN_EN |
+	      DPTX_PHY_DATA_CLK_EN |
+	      DPTX_PHY_CHAR_RSTN_EN |
+	      DPTX_PHY_CHAR_CLK_EN |
+	      SOURCE_AUX_SYS_CLK_RSTN_EN |
+	      SOURCE_AUX_SYS_CLK_EN |
+	      DPTX_SYS_CLK_RSTN_EN |
+	      DPTX_SYS_CLK_EN |
+	      CFG_DPTX_VIF_CLK_RSTN_EN |
+	      CFG_DPTX_VIF_CLK_EN;
+	writel(val, dp->regs + SOURCE_DPTX_CAR);
+
+	val = SOURCE_PHY_RSTN_EN | SOURCE_PHY_CLK_EN;
+	writel(val, dp->regs + SOURCE_PHY_CAR);
+
+	val = SOURCE_PKT_SYS_RSTN_EN |
+	      SOURCE_PKT_SYS_CLK_EN |
+	      SOURCE_PKT_DATA_RSTN_EN |
+	      SOURCE_PKT_DATA_CLK_EN;
+	writel(val, dp->regs + SOURCE_PKT_CAR);
+
+	val = SPDIF_CDR_CLK_RSTN_EN |
+	      SPDIF_CDR_CLK_EN |
+	      SOURCE_AIF_SYS_RSTN_EN |
+	      SOURCE_AIF_SYS_CLK_EN |
+	      SOURCE_AIF_CLK_RSTN_EN |
+	      SOURCE_AIF_CLK_EN;
+	writel(val, dp->regs + SOURCE_AIF_CAR);
+
+	val = SOURCE_CIPHER_SYSTEM_CLK_RSTN_EN |
+	      SOURCE_CIPHER_SYS_CLK_EN |
+	      SOURCE_CIPHER_CHAR_CLK_RSTN_EN |
+	      SOURCE_CIPHER_CHAR_CLK_EN;
+	writel(val, dp->regs + SOURCE_CIPHER_CAR);
+
+	val = SOURCE_CRYPTO_SYS_CLK_RSTN_EN |
+	      SOURCE_CRYPTO_SYS_CLK_EN;
+	writel(val, dp->regs + SOURCE_CRYPTO_CAR);
+
+	val = ~(MAILBOX_INT_MASK_BIT | PIF_INT_MASK_BIT) & ALL_INT_MASK;
+	writel(val, dp->regs + APB_INT_MASK);
+}
+
+static int cdn_dp_mailbox_read(struct cdn_dp_device *dp)
+{
+	int val, ret;
+
+	if (!dp->fw_loaded)
+		return -EPERM;
+
+	ret = readx_poll_timeout(readl, dp->regs + MAILBOX_EMPTY_ADDR,
+				 val, !val, MAILBOX_RETRY_US,
+				 MAILBOX_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(dp->dev, "failed to read mailbox, keep alive = %x\n",
+			readl(dp->regs + KEEP_ALIVE));
+		return ret;
+	}
+
+	return readl(dp->regs + MAILBOX0_RD_DATA) & 0xff;
+}
+
+static int cdp_dp_mailbox_write(struct cdn_dp_device *dp, u8 val)
+{
+	int ret, full;
+
+	if (!dp->fw_loaded)
+		return -EPERM;
+
+	ret = readx_poll_timeout(readl, dp->regs + MAILBOX_FULL_ADDR,
+				 full, !full, MAILBOX_RETRY_US,
+				 MAILBOX_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(dp->dev, "mailbox is full, keep alive = %x\n",
+			readl(dp->regs + KEEP_ALIVE));
+		return ret;
+	}
+
+	writel(val, dp->regs + MAILBOX0_WR_DATA);
+
+	return 0;
+}
+
+static int cdn_dp_mailbox_validate_receive(struct cdn_dp_device *dp,
+					   u8 module_id, u8 opcode,
+					   u8 req_size)
+{
+	u32 mbox_size, i;
+	u8 header[4];
+	int ret;
+
+	/* read the header of the message */
+	for (i = 0; i < 4; i++)  {
+		ret = cdn_dp_mailbox_read(dp);
+		if (ret < 0)
+			return ret;
+
+		header[i] = ret;
+	}
+
+	mbox_size = (header[2] << 8) | header[3];
+
+	if (opcode != header[0] || module_id != header[1] ||
+	    req_size != mbox_size) {
+		dump_stack();
+		dev_err(dp->dev, "mailbox validate failed");
+
+		/*
+		 * If the message in mailbox is not what we want, we need to
+		 * clear the mailbox by read.
+		 */
+		for (i = 0; i < mbox_size; i++)
+			if (cdn_dp_mailbox_read(dp) < 0)
+				break;
+
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int cdn_dp_mailbox_read_receive(struct cdn_dp_device *dp,
+				       u8 *buff, u8 buff_size)
+{
+	u32 i;
+	int ret;
+
+	for (i = 0; i < buff_size; i++) {
+		ret = cdn_dp_mailbox_read(dp);
+		if (ret < 0)
+			return ret;
+
+		buff[i] = ret;
+	}
+
+	return 0;
+}
+
+static int cdn_dp_mailbox_send(struct cdn_dp_device *dp, u8 module_id,
+			       u8 opcode, u16 size, u8 *message)
+{
+	u8 header[4];
+	int ret, i;
+
+	header[0] = opcode;
+	header[1] = module_id;
+	header[2] = (size >> 8) & 0xff;
+	header[3] = size & 0xff;
+
+	for (i = 0; i < 4; i++) {
+		ret = cdp_dp_mailbox_write(dp, header[i]);
+		if (ret)
+			return ret;
+	}
+
+	for (i = 0; i < size; i++) {
+		ret = cdp_dp_mailbox_write(dp, message[i]);
+		if (ret) {
+			dev_err(dp->dev, "mailbox send failed: %d", ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
+{
+	u8 msg[6];
+
+	msg[0] = (addr >> 8) & 0xff;
+	msg[1] = addr & 0xff;
+	msg[2] = (val >> 24) & 0xff;
+	msg[3] = (val >> 16) & 0xff;
+	msg[4] = (val >> 8) & 0xff;
+	msg[5] = val & 0xff;
+	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_REGISTER,
+				   sizeof(msg), msg);
+}
+
+int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
+{
+	u8 msg[5], reg[5];
+	int ret;
+
+	msg[0] = (len >> 8) & 0xff;
+	msg[1] = len & 0xff;
+	msg[2] = (addr >> 16) & 0xff;
+	msg[3] = (addr >> 8) & 0xff;
+	msg[4] = addr & 0xff;
+	cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_READ_DPCD,
+			    sizeof(msg), msg);
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_READ_DPCD,
+					      sizeof(reg) + len);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg));
+	if (ret)
+		return ret;
+
+	return cdn_dp_mailbox_read_receive(dp, data, len);
+}
+
+int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
+{
+	u8 msg[6], reg[5];
+	int ret;
+
+	msg[0] = 0;
+	msg[1] = 1;
+	msg[2] = (addr >> 16) & 0xff;
+	msg[3] = (addr >> 8) & 0xff;
+	msg[4] = addr & 0xff;
+	msg[5] = value;
+	cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_DPCD,
+			    sizeof(msg), msg);
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_WRITE_DPCD, sizeof(reg));
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg));
+	if (ret)
+		return ret;
+
+	if (addr != (reg[2] << 16 | reg[3] << 8 | reg[4])) {
+		dev_err(dp->dev, "write dpcd failed\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
+			 u32 i_size, const u32 *d_mem, u32 d_size)
+{
+	int i, ret;
+	u32 reg;
+
+	/* reset ucpu before load firmware*/
+	writel(APB_IRAM_PATH | APB_DRAM_PATH | APB_XT_RESET,
+	       dp->regs + APB_CTRL);
+
+	for (i = 0; i < i_size; i += 4)
+		writel(*i_mem++, dp->regs + ADDR_IMEM + i);
+
+	for (i = 0; i < d_size; i += 4)
+		writel(*d_mem++, dp->regs + ADDR_DMEM + i);
+
+	/* un-reset ucpu */
+	writel(0, dp->regs + APB_CTRL);
+
+	/* check the keep alive register to make sure fw working */
+	ret = readx_poll_timeout(readl, dp->regs + KEEP_ALIVE,
+				 reg, reg, 2000, FW_ALIVE_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(dp->dev, "failed to loaded the FW reg = %x\n", reg);
+		return -EINVAL;
+	}
+
+	reg = readl(dp->regs + VER_L) & 0xff;
+	dp->fw_version = reg;
+	reg = readl(dp->regs + VER_H) & 0xff;
+	dp->fw_version |= reg << 8;
+	reg = readl(dp->regs + VER_LIB_L_ADDR) & 0xff;
+	dp->fw_version |= reg << 16;
+	reg = readl(dp->regs + VER_LIB_H_ADDR) & 0xff;
+	dp->fw_version |= reg << 24;
+
+	dev_dbg(dp->dev, "firmware version: %x\n", dp->fw_version);
+
+	return 0;
+}
+
+int cdn_dp_reg_write_bit(struct cdn_dp_device *dp, u16 addr, u8 start_bit,
+			 u8 bits_no, u32 val)
+{
+	u8 field[8];
+
+	field[0] = (addr >> 8) & 0xff;
+	field[1] = addr & 0xff;
+	field[2] = start_bit;
+	field[3] = bits_no;
+	field[4] = (val >> 24) & 0xff;
+	field[5] = (val >> 16) & 0xff;
+	field[6] = (val >> 8) & 0xff;
+	field[7] = val & 0xff;
+
+	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_FIELD,
+				   sizeof(field), field);
+}
+
+int cdn_dp_active(struct cdn_dp_device *dp, bool enable)
+{
+	u8 active = enable ? 1 : 0;
+	u8 resp;
+	int ret;
+
+	/* set firmware status, 1: active; 0: standby */
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_GENERAL,
+				  GENERAL_MAIN_CONTROL, sizeof(active),
+				  &active);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_GENERAL,
+					      GENERAL_MAIN_CONTROL,
+					      sizeof(resp));
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, &resp, sizeof(resp));
+	if (ret)
+		return ret;
+
+	return resp ? 0 : -EINVAL;
+}
+
+int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes)
+{
+	u8 msg[8];
+	int ret;
+
+	msg[0] = CDN_DP_MAX_LINK_RATE;
+	msg[1] = lanes;
+	msg[2] = VOLTAGE_LEVEL_2;
+	msg[3] = PRE_EMPHASIS_LEVEL_3;
+	msg[4] = PTS1 | PTS2 | PTS3 | PTS4;
+	msg[5] = FAST_LT_NOT_SUPPORT;
+	msg[6] = LANE_MAPPING_NORMAL;
+	msg[7] = ENHANCED;
+
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
+				  DPTX_SET_HOST_CAPABILITIES,
+				  sizeof(msg), msg);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_reg_write(dp, DP_AUX_SWAP_INVERSION_CONTROL,
+			       AUX_HOST_INVERT);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+int cdn_dp_event_config(struct cdn_dp_device *dp)
+{
+	u8 msg[5];
+
+	memset(msg, 0, sizeof(msg));
+
+	msg[0] = DPTX_EVENT_ENABLE_HPD | DPTX_EVENT_ENABLE_TRAINING;
+
+	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_ENABLE_EVENT,
+				   sizeof(msg), msg);
+}
+
+u32 cdn_dp_get_event(struct cdn_dp_device *dp)
+{
+	return readl(dp->regs + SW_EVENTS0);
+}
+
+int cdn_dp_get_hpd_status(struct cdn_dp_device *dp)
+{
+	u8 status;
+	int ret;
+
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_HPD_STATE,
+				  0, NULL);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_HPD_STATE, sizeof(status));
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, &status, sizeof(status));
+	if (ret)
+		return ret;
+
+	return status;
+}
+
+int cdn_dp_get_edid_block(void *data, u8 *edid,
+			  unsigned int block, size_t length)
+{
+	struct cdn_dp_device *dp = data;
+	u8 msg[2], reg[2];
+	int ret;
+
+	msg[0] = block / 2;
+	msg[1] = block % 2;
+
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_GET_EDID,
+				  sizeof(msg), msg);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_GET_EDID,
+					      sizeof(reg) + length);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg));
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, edid, length);
+	if (ret)
+		return ret;
+
+	if (reg[0] != length || reg[1] != block / 2) {
+		dev_err(dp->dev, "edid block size err\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int cdn_dp_training_start(struct cdn_dp_device *dp)
+{
+	u8 msg, event[2];
+	unsigned long timeout;
+	int ret;
+
+	msg = LINK_TRAINING_RUN;
+
+	/* start training */
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_TRAINING_CONTROL,
+				  sizeof(msg), &msg);
+	if (ret)
+		return ret;
+
+	timeout = jiffies + msecs_to_jiffies(LINK_TRAINING_TIMEOUT_MS);
+	while (time_before(jiffies, timeout)) {
+		msleep(LINK_TRAINING_RETRY_MS);
+		ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
+					  DPTX_READ_EVENT, 0, NULL);
+		if (ret)
+			return ret;
+
+		ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+						      DPTX_READ_EVENT,
+						      sizeof(event));
+		if (ret)
+			return ret;
+
+		ret = cdn_dp_mailbox_read_receive(dp, event, sizeof(event));
+		if (ret)
+			return ret;
+
+		if (event[1] & EQ_PHASE_FINISHED)
+			return 0;
+	}
+
+	return -ETIMEDOUT;
+}
+
+int cdn_dp_get_training_status(struct cdn_dp_device *dp)
+{
+	u8 status[10];
+	int ret;
+
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_READ_LINK_STAT,
+				  0, NULL);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_READ_LINK_STAT,
+					      sizeof(status));
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, status, sizeof(status));
+	if (ret)
+		return ret;
+
+	dp->link.rate = status[0];
+	dp->link.num_lanes = status[1];
+
+	return 0;
+}
+
+int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active)
+{
+	u8 msg;
+
+	msg = !!active;
+
+	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_SET_VIDEO,
+				   sizeof(msg), &msg);
+}
+
+static int cdn_dp_get_msa_misc(struct video_info *video,
+			       struct drm_display_mode *mode)
+{
+	u8 val[2];
+	u32 msa_misc;
+
+	switch (video->color_fmt) {
+	case PXL_RGB:
+	case Y_ONLY:
+		val[0] = 0;
+		break;
+	/* set YUV default color space conversion to BT601 */
+	case YCBCR_4_4_4:
+		val[0] = 6 + BT_601 * 8;
+		break;
+	case YCBCR_4_2_2:
+		val[0] = 5 + BT_601 * 8;
+		break;
+	case YCBCR_4_2_0:
+		val[0] = 5;
+		break;
+	};
+
+	switch (video->color_depth) {
+	case 6:
+		val[1] = 0;
+		break;
+	case 8:
+		val[1] = 1;
+		break;
+	case 10:
+		val[1] = 2;
+		break;
+	case 12:
+		val[1] = 3;
+		break;
+	case 16:
+		val[1] = 4;
+		break;
+	};
+
+	msa_misc = 2 * val[0] + 32 * val[1] +
+		   ((video->color_fmt == Y_ONLY) ? (1 << 14) : 0);
+
+	return msa_misc;
+}
+
+int cdn_dp_config_video(struct cdn_dp_device *dp)
+{
+	struct video_info *video = &dp->video_info;
+	struct drm_display_mode *mode = &dp->mode;
+	u64 symbol;
+	u32 val, link_rate;
+	u8 bit_per_pix;
+	u8 tu_size_reg = TU_SIZE;
+	int ret;
+
+	bit_per_pix = (video->color_fmt == YCBCR_4_2_2) ?
+		      (video->color_depth * 2) : (video->color_depth * 3);
+
+	link_rate = drm_dp_bw_code_to_link_rate(dp->link.rate);
+
+	val = VIF_BYPASS_INTERLACE;
+	ret = cdn_dp_reg_write(dp, BND_HSYNC2VSYNC, val);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_reg_write(dp, HSYNC2VSYNC_POL_CTRL, 0);
+	if (ret)
+		return ret;
+
+	/* get a best tu_size and symbol */
+	do {
+		tu_size_reg += 2;
+		symbol = tu_size_reg * mode->clock * bit_per_pix;
+		symbol /= dp->link.num_lanes * link_rate * 8;
+	} while ((symbol == 1) || (tu_size_reg - symbol < 4));
+
+	val = symbol + (tu_size_reg << 8);
+	ret = cdn_dp_reg_write(dp, DP_FRAMER_TU, val);
+	if (ret)
+		return ret;
+
+	switch (video->color_depth) {
+	case 6:
+		val = BCS_6;
+		break;
+	case 8:
+		val = BCS_8;
+		break;
+	case 10:
+		val = BCS_10;
+		break;
+	case 12:
+		val = BCS_12;
+		break;
+	case 16:
+		val = BCS_16;
+		break;
+	};
+
+	val += video->color_fmt << 8;
+	ret = cdn_dp_reg_write(dp, DP_FRAMER_PXL_REPR, val);
+	if (ret)
+		return ret;
+
+	val = video->h_sync_polarity ? DP_FRAMER_SP_HSP : 0;
+	val |= video->v_sync_polarity ? DP_FRAMER_SP_VSP : 0;
+	ret = cdn_dp_reg_write(dp, DP_FRAMER_SP, val);
+	if (ret)
+		return ret;
+
+	val = (mode->hsync_start - mode->hdisplay) << 16;
+	val |= mode->htotal - mode->hsync_end;
+	ret = cdn_dp_reg_write(dp, DP_FRONT_BACK_PORCH, val);
+	if (ret)
+		return ret;
+
+	val = mode->hdisplay * bit_per_pix / 8;
+	ret = cdn_dp_reg_write(dp, DP_BYTE_COUNT, val);
+	if (ret)
+		return ret;
+
+	val = mode->htotal | ((mode->htotal - mode->hsync_start) << 16);
+	ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_0, val);
+	if (ret)
+		return ret;
+
+	val = mode->hsync_end - mode->hsync_start;
+	val |= (mode->hdisplay << 16) | (video->h_sync_polarity << 15);
+	ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_1, val);
+	if (ret)
+		return ret;
+
+	val = mode->vtotal;
+	val |= ((mode->vtotal - mode->vsync_start) << 16);
+	ret = cdn_dp_reg_write(dp, MSA_VERTICAL_0, val);
+	if (ret)
+		return ret;
+
+	val = mode->vsync_end - mode->vsync_start;
+	val |= mode->vdisplay << 16 | (video->v_sync_polarity << 15);
+	ret = cdn_dp_reg_write(dp, MSA_VERTICAL_1, val);
+	if (ret)
+		return ret;
+
+	val = cdn_dp_get_msa_misc(video, mode);
+	ret = cdn_dp_reg_write(dp, MSA_MISC, val);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_reg_write(dp, STREAM_CONFIG, 1);
+	if (ret)
+		return ret;
+
+	val = mode->hsync_end - mode->hsync_start;
+	val |= (mode->hdisplay << 16);
+	ret = cdn_dp_reg_write(dp, DP_HORIZONTAL, val);
+	if (ret)
+		return ret;
+
+	val = mode->vtotal;
+	val -= (mode->vtotal - mode->vdisplay);
+	val |= (mode->vtotal - mode->vsync_start) << 16;
+	ret = cdn_dp_reg_write(dp, DP_VERTICAL_0, val);
+	if (ret)
+		return ret;
+
+	val = mode->vtotal;
+	ret = cdn_dp_reg_write(dp, DP_VERTICAL_1, val);
+	if (ret)
+		return ret;
+
+	val =  0;
+	return cdn_dp_reg_write_bit(dp, DP_VB_ID, 2, 1, val);
+}
+
+int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio)
+{
+	u32 val;
+	int ret;
+
+	ret = cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, 0);
+	if (ret)
+		return ret;
+
+	val = SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS;
+	val |= SPDIF_FIFO_MID_RANGE(0xe0);
+	val |= SPDIF_JITTER_THRSH(0xe0);
+	val |= SPDIF_JITTER_AVG_WIN(7);
+	writel(val, dp->regs + SPDIF_CTRL_ADDR);
+
+	/* clearn the audio config and reset */
+	writel(0, dp->regs + AUDIO_SRC_CNTL);
+	writel(0, dp->regs + AUDIO_SRC_CNFG);
+	writel(AUDIO_SW_RST, dp->regs + AUDIO_SRC_CNTL);
+	writel(0, dp->regs + AUDIO_SRC_CNTL);
+
+	/* reset smpl2pckt component  */
+	writel(0, dp->regs + SMPL2PKT_CNTL);
+	writel(AUDIO_SW_RST, dp->regs + SMPL2PKT_CNTL);
+	writel(0, dp->regs + SMPL2PKT_CNTL);
+
+	/* reset FIFO */
+	writel(AUDIO_SW_RST, dp->regs + FIFO_CNTL);
+	writel(0, dp->regs + FIFO_CNTL);
+
+	if (audio->format == AFMT_SPDIF)
+		clk_disable_unprepare(dp->spdif_clk);
+
+	return 0;
+}
+
+int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable)
+{
+	return cdn_dp_reg_write_bit(dp, DP_VB_ID, 4, 1, enable);
+}
+
+static void cdn_dp_audio_config_i2s(struct cdn_dp_device *dp,
+				    struct audio_info *audio)
+{
+	int sub_pckt_num = 1, i2s_port_en_val = 0xf, i;
+	u32 val;
+
+	if (audio->channels == 2) {
+		if (dp->link.num_lanes == 1)
+			sub_pckt_num = 2;
+		else
+			sub_pckt_num = 4;
+
+		i2s_port_en_val = 1;
+	} else if (audio->channels == 4) {
+		i2s_port_en_val = 3;
+	}
+
+	writel(0x0, dp->regs + SPDIF_CTRL_ADDR);
+
+	writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL);
+
+	val = MAX_NUM_CH(audio->channels);
+	val |= NUM_OF_I2S_PORTS(audio->channels);
+	val |= AUDIO_TYPE_LPCM;
+	val |= CFG_SUB_PCKT_NUM(sub_pckt_num);
+	writel(val, dp->regs + SMPL2PKT_CNFG);
+
+	if (audio->sample_width == 16)
+		val = 0;
+	else if (audio->sample_width == 24)
+		val = 1 << 9;
+	else
+		val = 2 << 9;
+
+	val |= AUDIO_CH_NUM(audio->channels);
+	val |= I2S_DEC_PORT_EN(i2s_port_en_val);
+	val |= TRANS_SMPL_WIDTH_32;
+	writel(val, dp->regs + AUDIO_SRC_CNFG);
+
+	for (i = 0; i < (audio->channels + 1) / 2; i++) {
+		if (audio->sample_width == 16)
+			val = (0x08 << 8) | (0x08 << 20);
+		else if (audio->sample_width == 24)
+			val = (0x0b << 8) | (0x0b << 20);
+
+		val |= ((2 * i) << 4) | ((2 * i + 1) << 16);
+		writel(val, dp->regs + STTS_BIT_CH(i));
+	}
+
+	switch (audio->sample_rate) {
+	case 32000:
+		val = SAMPLING_FREQ(3) |
+		      ORIGINAL_SAMP_FREQ(0xc);
+		break;
+	case 44100:
+		val = SAMPLING_FREQ(0) |
+		      ORIGINAL_SAMP_FREQ(0xf);
+		break;
+	case 48000:
+		val = SAMPLING_FREQ(2) |
+		      ORIGINAL_SAMP_FREQ(0xd);
+		break;
+	case 88200:
+		val = SAMPLING_FREQ(8) |
+		      ORIGINAL_SAMP_FREQ(0x7);
+		break;
+	case 96000:
+		val = SAMPLING_FREQ(0xa) |
+		      ORIGINAL_SAMP_FREQ(5);
+		break;
+	case 176400:
+		val = SAMPLING_FREQ(0xc) |
+		      ORIGINAL_SAMP_FREQ(3);
+		break;
+	case 192000:
+		val = SAMPLING_FREQ(0xe) |
+		      ORIGINAL_SAMP_FREQ(1);
+		break;
+	}
+	val |= 4;
+	writel(val, dp->regs + COM_CH_STTS_BITS);
+
+	writel(SMPL2PKT_EN, dp->regs + SMPL2PKT_CNTL);
+	writel(I2S_DEC_START, dp->regs + AUDIO_SRC_CNTL);
+}
+
+static void cdn_dp_audio_config_spdif(struct cdn_dp_device *dp)
+{
+	u32 val;
+
+	val = SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS;
+	val |= SPDIF_FIFO_MID_RANGE(0xe0);
+	val |= SPDIF_JITTER_THRSH(0xe0);
+	val |= SPDIF_JITTER_AVG_WIN(7);
+	writel(val, dp->regs + SPDIF_CTRL_ADDR);
+
+	writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL);
+
+	val = MAX_NUM_CH(2) | AUDIO_TYPE_LPCM | CFG_SUB_PCKT_NUM(4);
+	writel(val, dp->regs + SMPL2PKT_CNFG);
+	writel(SMPL2PKT_EN, dp->regs + SMPL2PKT_CNTL);
+
+	val = SPDIF_ENABLE | SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS;
+	val |= SPDIF_FIFO_MID_RANGE(0xe0);
+	val |= SPDIF_JITTER_THRSH(0xe0);
+	val |= SPDIF_JITTER_AVG_WIN(7);
+	writel(val, dp->regs + SPDIF_CTRL_ADDR);
+
+	clk_prepare_enable(dp->spdif_clk);
+	clk_set_rate(dp->spdif_clk, CDN_DP_SPDIF_CLK);
+}
+
+int cdn_dp_audio_config_set(struct cdn_dp_device *dp, struct audio_info *audio)
+{
+	int ret;
+
+	/* reset the spdif clk before config */
+	if (audio->format == AFMT_SPDIF) {
+		reset_control_assert(dp->spdif_rst);
+		reset_control_deassert(dp->spdif_rst);
+	}
+
+	ret = cdn_dp_reg_write(dp, CM_LANE_CTRL, LANE_REF_CYC);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_reg_write(dp, CM_CTRL, 0);
+	if (ret)
+		return ret;
+
+	if (audio->format == AFMT_I2S)
+		cdn_dp_audio_config_i2s(dp, audio);
+	else
+		cdn_dp_audio_config_spdif(dp);
+
+	return cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, AUDIO_PACK_EN);
+}
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
new file mode 100644
index 0000000..1affeed
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
@@ -0,0 +1,479 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Chris Zhong <zyw at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _CDN_DP_REG_H
+#define _CDN_DP_REG_H
+
+#include <linux/bitops.h>
+
+#define ADDR_IMEM		0x10000
+#define ADDR_DMEM		0x20000
+
+/* APB CFG addr */
+#define APB_CTRL			0
+#define XT_INT_CTRL			0x04
+#define MAILBOX_FULL_ADDR		0x08
+#define MAILBOX_EMPTY_ADDR		0x0c
+#define MAILBOX0_WR_DATA		0x10
+#define MAILBOX0_RD_DATA		0x14
+#define KEEP_ALIVE			0x18
+#define VER_L				0x1c
+#define VER_H				0x20
+#define VER_LIB_L_ADDR			0x24
+#define VER_LIB_H_ADDR			0x28
+#define SW_DEBUG_L			0x2c
+#define SW_DEBUG_H			0x30
+#define MAILBOX_INT_MASK		0x34
+#define MAILBOX_INT_STATUS		0x38
+#define SW_CLK_L			0x3c
+#define SW_CLK_H			0x40
+#define SW_EVENTS0			0x44
+#define SW_EVENTS1			0x48
+#define SW_EVENTS2			0x4c
+#define SW_EVENTS3			0x50
+#define XT_OCD_CTRL			0x60
+#define APB_INT_MASK			0x6c
+#define APB_STATUS_MASK			0x70
+
+/* audio decoder addr */
+#define AUDIO_SRC_CNTL			0x30000
+#define AUDIO_SRC_CNFG			0x30004
+#define COM_CH_STTS_BITS		0x30008
+#define STTS_BIT_CH(x)			(0x3000c + ((x) << 2))
+#define SPDIF_CTRL_ADDR			0x3004c
+#define SPDIF_CH1_CS_3100_ADDR		0x30050
+#define SPDIF_CH1_CS_6332_ADDR		0x30054
+#define SPDIF_CH1_CS_9564_ADDR		0x30058
+#define SPDIF_CH1_CS_12796_ADDR		0x3005c
+#define SPDIF_CH1_CS_159128_ADDR	0x30060
+#define SPDIF_CH1_CS_191160_ADDR	0x30064
+#define SPDIF_CH2_CS_3100_ADDR		0x30068
+#define SPDIF_CH2_CS_6332_ADDR		0x3006c
+#define SPDIF_CH2_CS_9564_ADDR		0x30070
+#define SPDIF_CH2_CS_12796_ADDR		0x30074
+#define SPDIF_CH2_CS_159128_ADDR	0x30078
+#define SPDIF_CH2_CS_191160_ADDR	0x3007c
+#define SMPL2PKT_CNTL			0x30080
+#define SMPL2PKT_CNFG			0x30084
+#define FIFO_CNTL			0x30088
+#define FIFO_STTS			0x3008c
+
+/* source pif addr */
+#define SOURCE_PIF_WR_ADDR		0x30800
+#define SOURCE_PIF_WR_REQ		0x30804
+#define SOURCE_PIF_RD_ADDR		0x30808
+#define SOURCE_PIF_RD_REQ		0x3080c
+#define SOURCE_PIF_DATA_WR		0x30810
+#define SOURCE_PIF_DATA_RD		0x30814
+#define SOURCE_PIF_FIFO1_FLUSH		0x30818
+#define SOURCE_PIF_FIFO2_FLUSH		0x3081c
+#define SOURCE_PIF_STATUS		0x30820
+#define SOURCE_PIF_INTERRUPT_SOURCE	0x30824
+#define SOURCE_PIF_INTERRUPT_MASK	0x30828
+#define SOURCE_PIF_PKT_ALLOC_REG	0x3082c
+#define SOURCE_PIF_PKT_ALLOC_WR_EN	0x30830
+#define SOURCE_PIF_SW_RESET		0x30834
+
+/* bellow registers need access by mailbox */
+/* source car addr */
+#define SOURCE_HDTX_CAR			0x0900
+#define SOURCE_DPTX_CAR			0x0904
+#define SOURCE_PHY_CAR			0x0908
+#define SOURCE_CEC_CAR			0x090c
+#define SOURCE_CBUS_CAR			0x0910
+#define SOURCE_PKT_CAR			0x0918
+#define SOURCE_AIF_CAR			0x091c
+#define SOURCE_CIPHER_CAR		0x0920
+#define SOURCE_CRYPTO_CAR		0x0924
+
+/* clock meters addr */
+#define CM_CTRL				0x0a00
+#define CM_I2S_CTRL			0x0a04
+#define CM_SPDIF_CTRL			0x0a08
+#define CM_VID_CTRL			0x0a0c
+#define CM_LANE_CTRL			0x0a10
+#define I2S_NM_STABLE			0x0a14
+#define I2S_NCTS_STABLE			0x0a18
+#define SPDIF_NM_STABLE			0x0a1c
+#define SPDIF_NCTS_STABLE		0x0a20
+#define NMVID_MEAS_STABLE		0x0a24
+#define I2S_MEAS			0x0a40
+#define SPDIF_MEAS			0x0a80
+#define NMVID_MEAS			0x0ac0
+
+/* source vif addr */
+#define BND_HSYNC2VSYNC			0x0b00
+#define HSYNC2VSYNC_F1_L1		0x0b04
+#define HSYNC2VSYNC_F2_L1		0x0b08
+#define HSYNC2VSYNC_STATUS		0x0b0c
+#define HSYNC2VSYNC_POL_CTRL		0x0b10
+
+/* dptx phy addr */
+#define DP_TX_PHY_CONFIG_REG		0x2000
+#define DP_TX_PHY_STATUS_REG		0x2004
+#define DP_TX_PHY_SW_RESET		0x2008
+#define DP_TX_PHY_SCRAMBLER_SEED	0x200c
+#define DP_TX_PHY_TRAINING_01_04	0x2010
+#define DP_TX_PHY_TRAINING_05_08	0x2014
+#define DP_TX_PHY_TRAINING_09_10	0x2018
+#define TEST_COR			0x23fc
+
+/* dptx hpd addr */
+#define HPD_IRQ_DET_MIN_TIMER		0x2100
+#define HPD_IRQ_DET_MAX_TIMER		0x2104
+#define HPD_UNPLGED_DET_MIN_TIMER	0x2108
+#define HPD_STABLE_TIMER		0x210c
+#define HPD_FILTER_TIMER		0x2110
+#define HPD_EVENT_MASK			0x211c
+#define HPD_EVENT_DET			0x2120
+
+/* dpyx framer addr */
+#define DP_FRAMER_GLOBAL_CONFIG		0x2200
+#define DP_SW_RESET			0x2204
+#define DP_FRAMER_TU			0x2208
+#define DP_FRAMER_PXL_REPR		0x220c
+#define DP_FRAMER_SP			0x2210
+#define AUDIO_PACK_CONTROL		0x2214
+#define DP_VC_TABLE(x)			(0x2218 + ((x) << 2))
+#define DP_VB_ID			0x2258
+#define DP_MTPH_LVP_CONTROL		0x225c
+#define DP_MTPH_SYMBOL_VALUES		0x2260
+#define DP_MTPH_ECF_CONTROL		0x2264
+#define DP_MTPH_ACT_CONTROL		0x2268
+#define DP_MTPH_STATUS			0x226c
+#define DP_INTERRUPT_SOURCE		0x2270
+#define DP_INTERRUPT_MASK		0x2274
+#define DP_FRONT_BACK_PORCH		0x2278
+#define DP_BYTE_COUNT			0x227c
+
+/* dptx stream addr */
+#define MSA_HORIZONTAL_0		0x2280
+#define MSA_HORIZONTAL_1		0x2284
+#define MSA_VERTICAL_0			0x2288
+#define MSA_VERTICAL_1			0x228c
+#define MSA_MISC			0x2290
+#define STREAM_CONFIG			0x2294
+#define AUDIO_PACK_STATUS		0x2298
+#define VIF_STATUS			0x229c
+#define PCK_STUFF_STATUS_0		0x22a0
+#define PCK_STUFF_STATUS_1		0x22a4
+#define INFO_PACK_STATUS		0x22a8
+#define RATE_GOVERNOR_STATUS		0x22ac
+#define DP_HORIZONTAL			0x22b0
+#define DP_VERTICAL_0			0x22b4
+#define DP_VERTICAL_1			0x22b8
+#define DP_BLOCK_SDP			0x22bc
+
+/* dptx glbl addr */
+#define DPTX_LANE_EN			0x2300
+#define DPTX_ENHNCD			0x2304
+#define DPTX_INT_MASK			0x2308
+#define DPTX_INT_STATUS			0x230c
+
+/* dp aux addr */
+#define DP_AUX_HOST_CONTROL		0x2800
+#define DP_AUX_INTERRUPT_SOURCE		0x2804
+#define DP_AUX_INTERRUPT_MASK		0x2808
+#define DP_AUX_SWAP_INVERSION_CONTROL	0x280c
+#define DP_AUX_SEND_NACK_TRANSACTION	0x2810
+#define DP_AUX_CLEAR_RX			0x2814
+#define DP_AUX_CLEAR_TX			0x2818
+#define DP_AUX_TIMER_STOP		0x281c
+#define DP_AUX_TIMER_CLEAR		0x2820
+#define DP_AUX_RESET_SW			0x2824
+#define DP_AUX_DIVIDE_2M		0x2828
+#define DP_AUX_TX_PREACHARGE_LENGTH	0x282c
+#define DP_AUX_FREQUENCY_1M_MAX		0x2830
+#define DP_AUX_FREQUENCY_1M_MIN		0x2834
+#define DP_AUX_RX_PRE_MIN		0x2838
+#define DP_AUX_RX_PRE_MAX		0x283c
+#define DP_AUX_TIMER_PRESET		0x2840
+#define DP_AUX_NACK_FORMAT		0x2844
+#define DP_AUX_TX_DATA			0x2848
+#define DP_AUX_RX_DATA			0x284c
+#define DP_AUX_TX_STATUS		0x2850
+#define DP_AUX_RX_STATUS		0x2854
+#define DP_AUX_RX_CYCLE_COUNTER		0x2858
+#define DP_AUX_MAIN_STATES		0x285c
+#define DP_AUX_MAIN_TIMER		0x2860
+#define DP_AUX_AFE_OUT			0x2864
+
+/* crypto addr */
+#define CRYPTO_HDCP_REVISION		0x5800
+#define HDCP_CRYPTO_CONFIG		0x5804
+#define CRYPTO_INTERRUPT_SOURCE		0x5808
+#define CRYPTO_INTERRUPT_MASK		0x580c
+#define CRYPTO22_CONFIG			0x5818
+#define CRYPTO22_STATUS			0x581c
+#define SHA_256_DATA_IN			0x583c
+#define SHA_256_DATA_OUT_(x)		(0x5850 + ((x) << 2))
+#define AES_32_KEY_(x)			(0x5870 + ((x) << 2))
+#define AES_32_DATA_IN			0x5880
+#define AES_32_DATA_OUT_(x)		(0x5884 + ((x) << 2))
+#define CRYPTO14_CONFIG			0x58a0
+#define CRYPTO14_STATUS			0x58a4
+#define CRYPTO14_PRNM_OUT		0x58a8
+#define CRYPTO14_KM_0			0x58ac
+#define CRYPTO14_KM_1			0x58b0
+#define CRYPTO14_AN_0			0x58b4
+#define CRYPTO14_AN_1			0x58b8
+#define CRYPTO14_YOUR_KSV_0		0x58bc
+#define CRYPTO14_YOUR_KSV_1		0x58c0
+#define CRYPTO14_MI_0			0x58c4
+#define CRYPTO14_MI_1			0x58c8
+#define CRYPTO14_TI_0			0x58cc
+#define CRYPTO14_KI_0			0x58d0
+#define CRYPTO14_KI_1			0x58d4
+#define CRYPTO14_BLOCKS_NUM		0x58d8
+#define CRYPTO14_KEY_MEM_DATA_0		0x58dc
+#define CRYPTO14_KEY_MEM_DATA_1		0x58e0
+#define CRYPTO14_SHA1_MSG_DATA		0x58e4
+#define CRYPTO14_SHA1_V_VALUE_(x)	(0x58e8 + ((x) << 2))
+#define TRNG_CTRL			0x58fc
+#define TRNG_DATA_RDY			0x5900
+#define TRNG_DATA			0x5904
+
+/* cipher addr */
+#define HDCP_REVISION			0x60000
+#define INTERRUPT_SOURCE		0x60004
+#define INTERRUPT_MASK			0x60008
+#define HDCP_CIPHER_CONFIG		0x6000c
+#define AES_128_KEY_0			0x60010
+#define AES_128_KEY_1			0x60014
+#define AES_128_KEY_2			0x60018
+#define AES_128_KEY_3			0x6001c
+#define AES_128_RANDOM_0		0x60020
+#define AES_128_RANDOM_1		0x60024
+#define CIPHER14_KM_0			0x60028
+#define CIPHER14_KM_1			0x6002c
+#define CIPHER14_STATUS			0x60030
+#define CIPHER14_RI_PJ_STATUS		0x60034
+#define CIPHER_MODE			0x60038
+#define CIPHER14_AN_0			0x6003c
+#define CIPHER14_AN_1			0x60040
+#define CIPHER22_AUTH			0x60044
+#define CIPHER14_R0_DP_STATUS		0x60048
+#define CIPHER14_BOOTSTRAP		0x6004c
+
+#define DPTX_FRMR_DATA_CLK_RSTN_EN	BIT(11)
+#define DPTX_FRMR_DATA_CLK_EN		BIT(10)
+#define DPTX_PHY_DATA_RSTN_EN		BIT(9)
+#define DPTX_PHY_DATA_CLK_EN		BIT(8)
+#define DPTX_PHY_CHAR_RSTN_EN		BIT(7)
+#define DPTX_PHY_CHAR_CLK_EN		BIT(6)
+#define SOURCE_AUX_SYS_CLK_RSTN_EN	BIT(5)
+#define SOURCE_AUX_SYS_CLK_EN		BIT(4)
+#define DPTX_SYS_CLK_RSTN_EN		BIT(3)
+#define DPTX_SYS_CLK_EN			BIT(2)
+#define CFG_DPTX_VIF_CLK_RSTN_EN	BIT(1)
+#define CFG_DPTX_VIF_CLK_EN		BIT(0)
+
+#define SOURCE_PHY_RSTN_EN		BIT(1)
+#define SOURCE_PHY_CLK_EN		BIT(0)
+
+#define SOURCE_PKT_SYS_RSTN_EN		BIT(3)
+#define SOURCE_PKT_SYS_CLK_EN		BIT(2)
+#define SOURCE_PKT_DATA_RSTN_EN		BIT(1)
+#define SOURCE_PKT_DATA_CLK_EN		BIT(0)
+
+#define SPDIF_CDR_CLK_RSTN_EN		BIT(5)
+#define SPDIF_CDR_CLK_EN		BIT(4)
+#define SOURCE_AIF_SYS_RSTN_EN		BIT(3)
+#define SOURCE_AIF_SYS_CLK_EN		BIT(2)
+#define SOURCE_AIF_CLK_RSTN_EN		BIT(1)
+#define SOURCE_AIF_CLK_EN		BIT(0)
+
+#define SOURCE_CIPHER_SYSTEM_CLK_RSTN_EN	BIT(3)
+#define SOURCE_CIPHER_SYS_CLK_EN		BIT(2)
+#define SOURCE_CIPHER_CHAR_CLK_RSTN_EN		BIT(1)
+#define SOURCE_CIPHER_CHAR_CLK_EN		BIT(0)
+
+#define SOURCE_CRYPTO_SYS_CLK_RSTN_EN	BIT(1)
+#define SOURCE_CRYPTO_SYS_CLK_EN	BIT(0)
+
+#define APB_IRAM_PATH			BIT(2)
+#define APB_DRAM_PATH			BIT(1)
+#define APB_XT_RESET			BIT(0)
+
+#define MAILBOX_INT_MASK_BIT		BIT(1)
+#define PIF_INT_MASK_BIT		BIT(0)
+#define ALL_INT_MASK			3
+
+/* mailbox */
+#define MB_OPCODE_ID			0
+#define MB_MODULE_ID			1
+#define MB_SIZE_MSB_ID			2
+#define MB_SIZE_LSB_ID			3
+#define MB_DATA_ID			4
+
+#define MB_MODULE_ID_DP_TX		0x01
+#define MB_MODULE_ID_HDCP_TX		0x07
+#define MB_MODULE_ID_HDCP_RX		0x08
+#define MB_MODULE_ID_HDCP_GENERAL	0x09
+#define MB_MODULE_ID_GENERAL		0x0a
+
+/* general opcode */
+#define GENERAL_MAIN_CONTROL            0x01
+#define GENERAL_TEST_ECHO               0x02
+#define GENERAL_BUS_SETTINGS            0x03
+#define GENERAL_TEST_ACCESS             0x04
+
+#define DPTX_SET_POWER_MNG			0x00
+#define DPTX_SET_HOST_CAPABILITIES		0x01
+#define DPTX_GET_EDID				0x02
+#define DPTX_READ_DPCD				0x03
+#define DPTX_WRITE_DPCD				0x04
+#define DPTX_ENABLE_EVENT			0x05
+#define DPTX_WRITE_REGISTER			0x06
+#define DPTX_READ_REGISTER			0x07
+#define DPTX_WRITE_FIELD			0x08
+#define DPTX_TRAINING_CONTROL			0x09
+#define DPTX_READ_EVENT				0x0a
+#define DPTX_READ_LINK_STAT			0x0b
+#define DPTX_SET_VIDEO				0x0c
+#define DPTX_SET_AUDIO				0x0d
+#define DPTX_GET_LAST_AUX_STAUS			0x0e
+#define DPTX_SET_LINK_BREAK_POINT		0x0f
+#define DPTX_FORCE_LANES			0x10
+#define DPTX_HPD_STATE				0x11
+
+#define DPTX_EVENT_ENABLE_HPD			BIT(0)
+#define DPTX_EVENT_ENABLE_TRAINING		BIT(1)
+
+#define LINK_TRAINING_NOT_ACTIVE		0
+#define LINK_TRAINING_RUN			1
+#define LINK_TRAINING_RESTART			2
+
+#define CONTROL_VIDEO_IDLE			0
+#define CONTROL_VIDEO_VALID			1
+
+#define VIF_BYPASS_INTERLACE			BIT(13)
+#define INTERLACE_FMT_DET			BIT(12)
+#define INTERLACE_DTCT_WIN			0x20
+
+#define DP_FRAMER_SP_INTERLACE_EN		BIT(2)
+#define DP_FRAMER_SP_HSP			BIT(1)
+#define DP_FRAMER_SP_VSP			BIT(0)
+
+/* capability */
+#define AUX_HOST_INVERT				3
+#define	FAST_LT_SUPPORT				1
+#define FAST_LT_NOT_SUPPORT			0
+#define LANE_MAPPING_NORMAL			0xe4
+#define LANE_MAPPING_FLIPPED			0x1b
+#define ENHANCED				1
+
+#define	FULL_LT_STARTED				BIT(0)
+#define FASE_LT_STARTED				BIT(1)
+#define CLK_RECOVERY_FINISHED			BIT(2)
+#define EQ_PHASE_FINISHED			BIT(3)
+#define FASE_LT_START_FINISHED			BIT(4)
+#define CLK_RECOVERY_FAILED			BIT(5)
+#define EQ_PHASE_FAILED				BIT(6)
+#define FASE_LT_FAILED				BIT(7)
+
+#define DPTX_HPD_EVENT				BIT(0)
+#define DPTX_TRAINING_EVENT			BIT(1)
+#define HDCP_TX_STATUS_EVENT			BIT(4)
+#define HDCP2_TX_IS_KM_STORED_EVENT		BIT(5)
+#define HDCP2_TX_STORE_KM_EVENT			BIT(6)
+#define HDCP_TX_IS_RECEIVER_ID_VALID_EVENT	BIT(7)
+
+#define TU_SIZE					30
+#define CDN_DP_MAX_LINK_RATE			DP_LINK_BW_5_4
+
+/* audio */
+#define AUDIO_PACK_EN				BIT(8)
+#define SAMPLING_FREQ(x)			(((x) & 0xf) << 16)
+#define ORIGINAL_SAMP_FREQ(x)			(((x) & 0xf) << 24)
+#define SYNC_WR_TO_CH_ZERO			BIT(1)
+#define I2S_DEC_START				BIT(1)
+#define AUDIO_SW_RST				BIT(0)
+#define SMPL2PKT_EN				BIT(1)
+#define MAX_NUM_CH(x)				(((x) & 0x1f) - 1)
+#define NUM_OF_I2S_PORTS(x)			((((x) / 2 - 1) & 0x3) << 5)
+#define AUDIO_TYPE_LPCM				(2 << 7)
+#define CFG_SUB_PCKT_NUM(x)			((((x) - 1) & 0x7) << 11)
+#define AUDIO_CH_NUM(x)				((((x) - 1) & 0x1f) << 2)
+#define TRANS_SMPL_WIDTH_16			0
+#define TRANS_SMPL_WIDTH_24			BIT(11)
+#define TRANS_SMPL_WIDTH_32			(2 << 11)
+#define I2S_DEC_PORT_EN(x)			(((x) & 0xf) << 17)
+#define SPDIF_ENABLE				BIT(21)
+#define SPDIF_AVG_SEL				BIT(20)
+#define SPDIF_JITTER_BYPASS			BIT(19)
+#define SPDIF_FIFO_MID_RANGE(x)			(((x) & 0xff) << 11)
+#define SPDIF_JITTER_THRSH(x)			(((x) & 0xff) << 3)
+#define SPDIF_JITTER_AVG_WIN(x)			((x) & 0x7)
+
+/* Refernce cycles when using lane clock as refernce */
+#define LANE_REF_CYC				0x8000
+
+enum voltage_swing_level {
+	VOLTAGE_LEVEL_0,
+	VOLTAGE_LEVEL_1,
+	VOLTAGE_LEVEL_2,
+	VOLTAGE_LEVEL_3,
+};
+
+enum pre_emphasis_level {
+	PRE_EMPHASIS_LEVEL_0,
+	PRE_EMPHASIS_LEVEL_1,
+	PRE_EMPHASIS_LEVEL_2,
+	PRE_EMPHASIS_LEVEL_3,
+};
+
+enum pattern_set {
+	PTS1		= BIT(0),
+	PTS2		= BIT(1),
+	PTS3		= BIT(2),
+	PTS4		= BIT(3),
+	DP_NONE		= BIT(4)
+};
+
+enum vic_color_depth {
+	BCS_6 = 0x1,
+	BCS_8 = 0x2,
+	BCS_10 = 0x4,
+	BCS_12 = 0x8,
+	BCS_16 = 0x10,
+};
+
+enum vic_bt_type {
+	BT_601 = 0x0,
+	BT_709 = 0x1,
+};
+
+void cdn_dp_clock_reset(struct cdn_dp_device *dp);
+
+void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, u32 clk);
+int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
+			 u32 i_size, const u32 *d_mem, u32 d_size);
+int cdn_dp_active(struct cdn_dp_device *dp, bool enable);
+int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes);
+int cdn_dp_event_config(struct cdn_dp_device *dp);
+u32 cdn_dp_get_event(struct cdn_dp_device *dp);
+int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
+int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value);
+int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len);
+int cdn_dp_get_edid_block(void *dp, u8 *edid,
+			  unsigned int block, size_t length);
+int cdn_dp_training_start(struct cdn_dp_device *dp);
+int cdn_dp_get_training_status(struct cdn_dp_device *dp);
+int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active);
+int cdn_dp_config_video(struct cdn_dp_device *dp);
+int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio);
+int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable);
+int cdn_dp_audio_config_set(struct cdn_dp_device *dp, struct audio_info *audio);
+#endif /* _CDN_DP_REG_H */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index edd7ec2..ceb4b86 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -969,9 +969,9 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
 		vop_dsp_hold_valid_irq_disable(vop);
 	}
 
-	pin_pol = 0x8;
-	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1;
-	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : (1 << 1);
+	pin_pol = BIT(DCLK_INVERT);
+	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : BIT(HSYNC_POSITIVE);
+	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : BIT(VSYNC_POSITIVE);
 	VOP_CTRL_SET(vop, pin_pol, pin_pol);
 
 	switch (s->output_type) {
@@ -991,6 +991,11 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
 		VOP_CTRL_SET(vop, mipi_pin_pol, pin_pol);
 		VOP_CTRL_SET(vop, mipi_en, 1);
 		break;
+	case DRM_MODE_CONNECTOR_DisplayPort:
+		pin_pol &= ~BIT(DCLK_INVERT);
+		VOP_CTRL_SET(vop, dp_pin_pol, pin_pol);
+		VOP_CTRL_SET(vop, dp_en, 1);
+		break;
 	default:
 		DRM_ERROR("unsupport connector_type[%d]\n", s->output_type);
 	}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index ff4f52e..4820a8b 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -45,6 +45,7 @@ struct vop_ctrl {
 	struct vop_reg edp_en;
 	struct vop_reg hdmi_en;
 	struct vop_reg mipi_en;
+	struct vop_reg dp_en;
 	struct vop_reg out_mode;
 	struct vop_reg dither_down;
 	struct vop_reg dither_up;
@@ -53,6 +54,7 @@ struct vop_ctrl {
 	struct vop_reg hdmi_pin_pol;
 	struct vop_reg edp_pin_pol;
 	struct vop_reg mipi_pin_pol;
+	struct vop_reg dp_pin_pol;
 
 	struct vop_reg htotal_pw;
 	struct vop_reg hact_st_end;
@@ -242,6 +244,13 @@ enum scale_down_mode {
 	SCALE_DOWN_AVG = 0x1
 };
 
+enum vop_pol {
+	HSYNC_POSITIVE = 0,
+	VSYNC_POSITIVE = 1,
+	DEN_NEGATIVE   = 2,
+	DCLK_INVERT    = 3
+};
+
 #define FRAC_16_16(mult, div)    (((mult) << 16) / (div))
 #define SCL_FT_DEFAULT_FIXPOINT_SHIFT	12
 #define SCL_MAX_VSKIPLINES		4
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 5b1ae1f..dcf172e 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -281,6 +281,7 @@ static const struct vop_data rk3288_vop = {
 static const struct vop_ctrl rk3399_ctrl_data = {
 	.standby = VOP_REG(RK3399_SYS_CTRL, 0x1, 22),
 	.gate_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 23),
+	.dp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 11),
 	.rgb_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 12),
 	.hdmi_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 13),
 	.edp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 14),
@@ -290,6 +291,7 @@ static const struct vop_ctrl rk3399_ctrl_data = {
 	.data_blank = VOP_REG(RK3399_DSP_CTRL0, 0x1, 19),
 	.out_mode = VOP_REG(RK3399_DSP_CTRL0, 0xf, 0),
 	.rgb_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16),
+	.dp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16),
 	.hdmi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 20),
 	.edp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 24),
 	.mipi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 28),
-- 
2.6.3



From matthias.bgg at gmail.com  Fri Jul 15 03:09:51 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Fri, 15 Jul 2016 12:09:51 +0200
Subject: [PATCH v3] arm64: dts: rockchip: Add basic support for orion-r68
Message-ID: <1468577391-5096-1-git-send-email-matthias.bgg@gmail.com>

This patch adds basic support for the Tronsmart orion r86 set-top-box.

Signed-off-by: Matthias Brugger <mbrugger at suse.com>
---
Changes since v2:
- gpio-keys cleanpup
- set sdmmc properties correctly

 Documentation/devicetree/bindings/arm/rockchip.txt |   4 +
 arch/arm64/boot/dts/rockchip/Makefile              |   1 +
 .../boot/dts/rockchip/rk3368-orion-r68-meta.dts    | 385 +++++++++++++++++++++
 3 files changed, 390 insertions(+)
 create mode 100644 arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts

diff --git a/Documentation/devicetree/bindings/arm/rockchip.txt b/Documentation/devicetree/bindings/arm/rockchip.txt
index 715d960..1d5c65e0 100644
--- a/Documentation/devicetree/bindings/arm/rockchip.txt
+++ b/Documentation/devicetree/bindings/arm/rockchip.txt
@@ -110,3 +110,7 @@ Rockchip platforms device tree bindings
 - Rockchip RK3399 evb:
     Required root node properties:
       - compatible = "rockchip,rk3399-evb", "rockchip,rk3399";
+
+- Tronsmart Orion R68 Meta
+    Required root node properties:
+      - compatible = "tronsmart,orion-r68-meta", "rockchip,rk3368";
diff --git a/arch/arm64/boot/dts/rockchip/Makefile b/arch/arm64/boot/dts/rockchip/Makefile
index 7037a16..87669f6 100644
--- a/arch/arm64/boot/dts/rockchip/Makefile
+++ b/arch/arm64/boot/dts/rockchip/Makefile
@@ -1,5 +1,6 @@
 dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-evb-act8846.dtb
 dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-geekbox.dtb
+dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-orion-r68-meta.dtb
 dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-r88.dtb
 dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-evb.dtb
 
diff --git a/arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts b/arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts
new file mode 100644
index 0000000..d5e1af5
--- /dev/null
+++ b/arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts
@@ -0,0 +1,385 @@
+/*
+ * Copyright (c) 2016 Matthias Brugger <mbrugger at suse.com>
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of the
+ *     License, or (at your option) any later version.
+ *
+ *     This file 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.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/dts-v1/;
+#include <dt-bindings/input/input.h>
+#include "rk3368.dtsi"
+
+/ {
+	model = "Rockchip Orion R68";
+	compatible = "tronsmart,orion-r68-meta", "rockchip,rk3368";
+
+	chosen {
+		stdout-path = "serial2:115200n8";
+	};
+
+	memory {
+		device_type = "memory";
+		reg = <0x0 0x0 0x0 0x80000000>;
+	};
+
+	emmc_pwrseq: emmc-pwrseq {
+		compatible = "mmc-pwrseq-emmc";
+		pinctrl-0 = <&emmc_reset>;
+		pinctrl-names = "default";
+		reset-gpios = <&gpio2 3 GPIO_ACTIVE_HIGH>;
+	};
+
+	ext_gmac: external-gmac-clock {
+		compatible = "fixed-clock";
+		#clock-cells = <0>;
+		clock-frequency = <125000000>;
+		clock-output-names = "ext_gmac";
+	};
+
+	keys: gpio-keys {
+		compatible = "gpio-keys";
+		pinctrl-names = "default";
+		pinctrl-0 = <&pwr_key>;
+
+		power {
+			wakeup-source;
+			gpios = <&gpio0 5 GPIO_ACTIVE_HIGH>;
+			label = "GPIO Power";
+			linux,code = <KEY_POWER>;
+		};
+	};
+
+	leds: gpio-leds {
+		compatible = "gpio-leds";
+
+		red {
+			gpios = <&gpio3 29 GPIO_ACTIVE_HIGH>;
+			label = "orion:red:led";
+			pinctrl-names = "default";
+			pinctrl-0 = <&led_ctl>;
+			default-state = "on";
+		};
+
+		blue {
+			gpios = <&gpio0 12 GPIO_ACTIVE_HIGH>;
+			label = "orion:blue:led";
+			pinctrl-names = "default";
+			pinctrl-0 = <&stby_pwren>;
+			default-state = "off";
+		};
+	};
+
+	vcc_18: vcc18-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_18";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+		regulator-always-on;
+		regulator-boot-on;
+		vin-supply = <&vcc_sys>;
+	};
+
+	/* supplies both host and otg */
+	vcc_host: vcc-host-regulator {
+		compatible = "regulator-fixed";
+		gpio = <&gpio0 4 GPIO_ACTIVE_LOW>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&host_vbus_drv>;
+		regulator-name = "vcc_host";
+		regulator-always-on;
+		regulator-boot-on;
+		vin-supply = <&vcc_sys>;
+	};
+
+	vccio_sd: vcc-io-sd-regulator {
+		compatible = "regulator-fixed";
+		regulator-name= "vccio_sd";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-always-on;
+		regulator-boot-on;
+		vin-supply = <&vcc_io>;
+	};
+
+	vcc_sd: vcc-sd-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_sd";
+		gpio = <&gpio3 11 GPIO_ACTIVE_LOW>;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-always-on;
+		regulator-boot-on;
+		vin-supply = <&vcc_io>;
+	};
+
+	vcc_io: vcc-io-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_io";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-always-on;
+		regulator-boot-on;
+		vin-supply = <&vcc_sys>;
+	};
+
+	vcc_lan: vcc-lan-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_lan";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-always-on;
+		regulator-boot-on;
+		vin-supply = <&vcc_io>;
+	};
+
+	vcc_sys: vcc-sys-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_sys";
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+		regulator-always-on;
+		regulator-boot-on;
+	};
+
+	vccio_wl: vccio-wl-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vccio_wl";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-always-on;
+		regulator-boot-on;
+		vin-supply = <&vcc_io>;
+	};
+
+	vdd_10: vdd-10-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vdd_10";
+		regulator-min-microvolt = <1000000>;
+		regulator-max-microvolt = <1000000>;
+		regulator-always-on;
+		regulator-boot-on;
+		vin-supply = <&vcc_sys>;
+	};
+};
+
+&emmc {
+	bus-width = <8>;
+	cap-mmc-highspeed;
+	disable-wp;
+	mmc-pwrseq = <&emmc_pwrseq>;
+	non-removable;
+	num-slots = <1>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&emmc_clk &emmc_cmd &emmc_bus8>;
+	keep-power-in-suspend;
+	cap-mmc-highspeed;
+	mmc-hs200-1_2v;
+	mmc-hs200-1_8v;
+	status = "okay";
+};
+
+&gmac {
+	assigned-clocks = <&cru SCLK_MAC>;
+	assigned-clock-parents = <&ext_gmac>;
+	phy-supply = <&vcc_lan>;
+	phy-mode = "rgmii";
+	clock_in_out = "input";
+	snps,reset-gpio = <&gpio3 12 0>;
+	snps,reset-active-low;
+	snps,reset-delays-us = <0 10000 1000000>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&rgmii_pins>;
+	tx_delay = <0x30>;
+	rx_delay = <0x10>;
+	status = "ok";
+};
+
+&i2c0 {
+	status = "okay";
+
+	vdd_cpu: syr827 at 40 {
+		compatible = "silergy,syr827";
+		reg = <0x40>;
+		fcs,suspend-voltage-selector = <1>;
+		regulator-name = "vdd_cpu";
+		regulator-enable-ramp-delay = <300>;
+		regulator-min-microvolt = <712500>;
+		regulator-max-microvolt = <1500000>;
+		regulator-ramp-delay = <8000>;
+		regulator-always-on;
+		regulator-boot-on;
+		vin-supply = <&vcc_sys>;
+	};
+
+	hym8563: hym8563 at 51 {
+		compatible = "haoyu,hym8563";
+		reg = <0x51>;
+		#clock-cells = <0>;
+		clock-frequency = <32768>;
+		clock-output-names = "xin32k";
+		/* rtc_int is not connected */
+	};
+};
+
+&pinctrl {
+	pcfg_pull_none_drv_8ma: pcfg-pull-none-drv-8ma {
+		bias-disable;
+		drive-strength = <8>;
+	};
+
+	pcfg_pull_up_drv_8ma: pcfg-pull-up-drv-8ma {
+		bias-pull-up;
+		drive-strength = <8>;
+	};
+
+	emmc {
+		emmc_bus8: emmc-bus8 {
+			rockchip,pins = <1 18 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
+					<1 19 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
+					<1 20 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
+					<1 21 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
+					<1 22 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
+					<1 23 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
+					<1 24 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
+					<1 25 RK_FUNC_2 &pcfg_pull_up_drv_8ma>;
+		};
+
+		emmc-clk {
+			rockchip,pins = <2 4 RK_FUNC_2 &pcfg_pull_none_drv_8ma>;
+		};
+
+		emmc-cmd {
+			rockchip,pins = <1 26 RK_FUNC_2 &pcfg_pull_up_drv_8ma>;
+		};
+
+		emmc_reset: emmc-reset {
+			rockchip,pins = <2 3 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+
+	keys {
+		pwr_key: pwr-key {
+			rockchip,pins = <0 5 RK_FUNC_GPIO &pcfg_pull_down>;
+		};
+	};
+
+	leds {
+		stby_pwren: stby-pwren {
+			rockchip,pins = <0 12 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+
+		led_ctl: led-ctl {
+			rockchip,pins = <3 29 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+
+	sdmmc {
+		sdmmc_clk: sdmmc-clk {
+			rockchip,pins = <2 9 RK_FUNC_1 &pcfg_pull_none_drv_8ma>;
+		};
+
+		sdmmc_cmd: sdmmc-cmd {
+			rockchip,pins = <2 10 RK_FUNC_1 &pcfg_pull_up_drv_8ma>;
+		};
+
+		sdmmc_cd: sdmmc-cd {
+			rockchip,pins = <2 11 RK_FUNC_1 &pcfg_pull_up_drv_8ma>;
+		};
+
+		sdmmc_bus1: sdmmc-bus1 {
+			rockchip,pins = <2 5 RK_FUNC_1 &pcfg_pull_up_drv_8ma>;
+		};
+
+		sdmmc_bus4: sdmmc-bus4 {
+			rockchip,pins = <2 5 RK_FUNC_1 &pcfg_pull_up_drv_8ma>,
+					<2 6 RK_FUNC_1 &pcfg_pull_up_drv_8ma>,
+					<2 7 RK_FUNC_1 &pcfg_pull_up_drv_8ma>,
+					<2 8 RK_FUNC_1 &pcfg_pull_up_drv_8ma>;
+		};
+	};
+
+	usb {
+		host_vbus_drv: host-vbus-drv {
+			rockchip,pins = <0 4 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+};
+
+&saradc {
+	vref-supply = <&vcc_18>;
+	status = "okay";
+};
+
+&sdmmc {
+	clock-frequency = <50000000>;
+	clock-freq-min-max = <400000 50000000>;
+	cap-sd-highspeed;
+	card-detect-delay = <200>;
+
+	pinctrl-names = "default";
+	pinctrl-0 = <&sdmmc_clk &sdmmc_cmd &sdmmc_cd &sdmmc_bus4>;
+	bus-width = <4>;
+	num-slots = <1>;
+	keep-power-in-suspend;
+
+	vmmc-supply = <&vcc_sd>;
+	vqmmc-supply = <&vccio_sd>;
+	status = "okay";
+};
+
+&uart2 {
+	status = "okay";
+};
+
+&uart4 {
+	status = "okay";
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart4_xfer>;
+};
+
+&usb_host0_ehci {
+	status = "okay";
+};
+
+&usb_otg {
+	status = "okay";
+};
+
+&wdt {
+	status = "okay";
+};
-- 
2.6.6



From ykk at rock-chips.com  Fri Jul 15 03:55:07 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 15 Jul 2016 18:55:07 +0800
Subject: [PATCH v4.1 1/4] drm/rockchip: vop: export line flag function
In-Reply-To: <1468469704-12440-1-git-send-email-ykk@rock-chips.com>
References: <1468469704-12440-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1468580107-18644-1-git-send-email-ykk@rock-chips.com>

VOP have integrated a hardware counter which indicate the exact display
line that vop is scanning. And if we're interested in a specific line,
we can set the line number to vop line_flag register, and then vop would
generate a line_flag interrupt for it.

For example eDP PSR function is interested in the vertical blanking
period, then driver could set the line number to zero.

This patch have exported a symbol that allow other driver to listen the
line flag event with given timeout limit:
-  rockchip_drm_wait_line_flag()

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
Changes in v4.1:
- Remove the completion_done() check in irq handler (Sean)

Changes in v4:
- Avoid the weird behavior in rockchip_drm_wait_line_flag(). (Sean)
- Make line_flag_num_x to an array. (Sean)
- Remove the unused vop_cfg_done() in vop_line_flag_irq_enable(). (Stephane, reviewed in Google gerrit)
    [https://chromium-review.googlesource.com/#/c/349084/33/drivers/gpu/drm/rockchip/rockchip_drm_vop.c at 466]

Changes in v3:
- Export the 'rockchip_drm_wait_line_flag' symbol, and document it.
- Add 'line_flag_num_0' for RK3288/RK3036
- Remove the notify for waiting line_flag event (Daniel)

Changes in v2:
- Introduce in v2, split VOP line flag changes out

 drivers/gpu/drm/rockchip/rockchip_drm_drv.h |   3 +
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 117 ++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   2 +
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c |   4 +
 4 files changed, 126 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index ea39329..239b830 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -70,4 +70,7 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
 				   struct device *dev);
 void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
 				    struct device *dev);
+int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
+				unsigned int mstimeout);
+
 #endif /* _ROCKCHIP_DRM_DRV_H_ */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index c8a62a8..8a4f36e 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -121,6 +121,8 @@ struct vop {
 	/* protected by dev->event_lock */
 	struct drm_pending_vblank_event *event;
 
+	struct completion line_flag_completion;
+
 	const struct vop_data *data;
 
 	uint32_t *regsbak;
@@ -431,6 +433,71 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop)
 	spin_unlock_irqrestore(&vop->irq_lock, flags);
 }
 
+/*
+ * (1) each frame starts at the start of the Vsync pulse which is signaled by
+ *     the "FRAME_SYNC" interrupt.
+ * (2) the active data region of each frame ends at dsp_vact_end
+ * (3) we should program this same number (dsp_vact_end) into dsp_line_frag_num,
+ *      to get "LINE_FLAG" interrupt at the end of the active on screen data.
+ *
+ * VOP_INTR_CTRL0.dsp_line_frag_num = VOP_DSP_VACT_ST_END.dsp_vact_end
+ * Interrupts
+ * LINE_FLAG -------------------------------+
+ * FRAME_SYNC ----+                         |
+ *                |                         |
+ *                v                         v
+ *                | Vsync | Vbp |  Vactive  | Vfp |
+ *                        ^     ^           ^     ^
+ *                        |     |           |     |
+ *                        |     |           |     |
+ * dsp_vs_end ------------+     |           |     |   VOP_DSP_VTOTAL_VS_END
+ * dsp_vact_start --------------+           |     |   VOP_DSP_VACT_ST_END
+ * dsp_vact_end ----------------------------+     |   VOP_DSP_VACT_ST_END
+ * dsp_total -------------------------------------+   VOP_DSP_VTOTAL_VS_END
+ */
+static bool vop_line_flag_irq_is_enabled(struct vop *vop)
+{
+	uint32_t line_flag_irq;
+	unsigned long flags;
+
+	spin_lock_irqsave(&vop->irq_lock, flags);
+
+	line_flag_irq = VOP_INTR_GET_TYPE(vop, enable, LINE_FLAG_INTR);
+
+	spin_unlock_irqrestore(&vop->irq_lock, flags);
+
+	return !!line_flag_irq;
+}
+
+static void vop_line_flag_irq_enable(struct vop *vop, int line_num)
+{
+	unsigned long flags;
+
+	if (WARN_ON(!vop->is_enabled))
+		return;
+
+	spin_lock_irqsave(&vop->irq_lock, flags);
+
+	VOP_CTRL_SET(vop, line_flag_num[0], line_num);
+	VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1);
+
+	spin_unlock_irqrestore(&vop->irq_lock, flags);
+}
+
+static void vop_line_flag_irq_disable(struct vop *vop)
+{
+	unsigned long flags;
+
+	if (WARN_ON(!vop->is_enabled))
+		return;
+
+	spin_lock_irqsave(&vop->irq_lock, flags);
+
+	VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 0);
+
+	spin_unlock_irqrestore(&vop->irq_lock, flags);
+}
+
 static void vop_enable(struct drm_crtc *crtc)
 {
 	struct vop *vop = to_vop(crtc);
@@ -1157,6 +1224,12 @@ static irqreturn_t vop_isr(int irq, void *data)
 		ret = IRQ_HANDLED;
 	}
 
+	if (active_irqs & LINE_FLAG_INTR) {
+		complete(&vop->line_flag_completion);
+		active_irqs &= ~LINE_FLAG_INTR;
+		ret = IRQ_HANDLED;
+	}
+
 	if (active_irqs & FS_INTR) {
 		drm_crtc_handle_vblank(crtc);
 		vop_handle_vblank(vop);
@@ -1255,6 +1328,7 @@ static int vop_create_crtc(struct vop *vop)
 
 	init_completion(&vop->dsp_hold_completion);
 	init_completion(&vop->wait_update_complete);
+	init_completion(&vop->line_flag_completion);
 	crtc->port = port;
 	rockchip_register_crtc_funcs(crtc, &private_crtc_funcs);
 
@@ -1411,6 +1485,49 @@ static void vop_win_init(struct vop *vop)
 	}
 }
 
+/**
+ * rockchip_drm_wait_line_flag - acqiure the give line flag event
+ * @crtc: CRTC to enable line flag
+ * @line_num: interested line number
+ * @mstimeout: millisecond for timeout
+ *
+ * Driver would hold here until the interested line flag interrupt have
+ * happened or timeout to wait.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
+				unsigned int mstimeout)
+{
+	struct vop *vop = to_vop(crtc);
+	unsigned long jiffies_left;
+
+	if (!crtc || !vop->is_enabled)
+		return -ENODEV;
+
+	if (line_num > crtc->mode.vtotal || mstimeout <= 0)
+		return -EINVAL;
+
+	if (vop_line_flag_irq_is_enabled(vop))
+		return -EBUSY;
+
+	reinit_completion(&vop->line_flag_completion);
+	vop_line_flag_irq_enable(vop, line_num);
+
+	jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion,
+						   msecs_to_jiffies(mstimeout));
+	vop_line_flag_irq_disable(vop);
+
+	if (jiffies_left == 0) {
+		dev_err(vop->dev, "Timeout waiting for IRQ\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_wait_line_flag);
+
 static int vop_bind(struct device *dev, struct device *master, void *data)
 {
 	struct platform_device *pdev = to_platform_device(dev);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index ff4f52e..1dbc526 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -61,6 +61,8 @@ struct vop_ctrl {
 	struct vop_reg hpost_st_end;
 	struct vop_reg vpost_st_end;
 
+	struct vop_reg line_flag_num[2];
+
 	struct vop_reg cfg_done;
 };
 
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 6f42e56..eea8427 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -122,6 +122,7 @@ static const struct vop_ctrl rk3036_ctrl_data = {
 	.hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0),
 	.vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
 	.vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0),
+	.line_flag_num[0] = VOP_REG(RK3036_INT_STATUS, 0xfff, 12),
 	.cfg_done = VOP_REG(RK3036_REG_CFG_DONE, 0x1, 0),
 };
 
@@ -221,6 +222,7 @@ static const struct vop_ctrl rk3288_ctrl_data = {
 	.vact_st_end = VOP_REG(RK3288_DSP_VACT_ST_END, 0x1fff1fff, 0),
 	.hpost_st_end = VOP_REG(RK3288_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
 	.vpost_st_end = VOP_REG(RK3288_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
+	.line_flag_num[0] = VOP_REG(RK3288_INTR_CTRL0, 0x1fff, 12),
 	.cfg_done = VOP_REG(RK3288_REG_CFG_DONE, 0x1, 0),
 };
 
@@ -299,6 +301,8 @@ static const struct vop_ctrl rk3399_ctrl_data = {
 	.vact_st_end = VOP_REG(RK3399_DSP_VACT_ST_END, 0x1fff1fff, 0),
 	.hpost_st_end = VOP_REG(RK3399_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
 	.vpost_st_end = VOP_REG(RK3399_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
+	.line_flag_num[0] = VOP_REG(RK3399_LINE_FLAG, 0xffff, 0),
+	.line_flag_num[1] = VOP_REG(RK3399_LINE_FLAG, 0xffff, 16),
 	.cfg_done = VOP_REG_MASK(RK3399_REG_CFG_DONE, 0x1, 0),
 };
 
-- 
1.9.1




From ykk at rock-chips.com  Fri Jul 15 03:55:17 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 15 Jul 2016 18:55:17 +0800
Subject: [PATCH v4.1 3/4] drm/bridge: analogix_dp: add the PSR function support
In-Reply-To: <1468469704-12440-1-git-send-email-ykk@rock-chips.com>
References: <1468469704-12440-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1468580117-18697-1-git-send-email-ykk@rock-chips.com>

The full name of PSR is Panel Self Refresh, panel device could refresh
itself with the hardware framebuffer in panel, this would make lots of
sense to save the power consumption.

This patch have exported two symbols for platform driver to implement
the PSR function in hardware side:
- analogix_dp_active_psr()
- analogix_dp_inactive_psr()

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
Changes in v4.1:
- Take use of existing edp_psr_vsc struct to swap HBx and DBx setting. (Sean)
- Remove PSR_VID_CRC_FLUSH setting analogix_dp_enable_psr_crc().
- Add comment about PBx magic numbers. (Sean)

Changes in v4:
- Downgrade the PSR version print message to debug level. (Sean)
- Return 'void' instead of 'int' in analogix_dp_enable_sink_psr(). (Sean)
- Delete the unused read dpcd operations in analogix_dp_enable_sink_psr(). (Sean)
- Delete the arbitrary usleep_range in analogix_dp_enable_psr_crc. (Sean).
- Clean up the hardcoded values in analogix_dp_send_psr_spd(). (Sean)
- Rename "active/inactive" to "enable/disable". (Sean, Dominik)
- Keep set the PSR_VID_CRC_FLUSH gate in analogix_dp_enable_psr_crc().

Changes in v3:
- split analogix_dp_enable_psr(), make it more clearly
    analogix_dp_detect_sink_psr()
    analogix_dp_enable_sink_psr()
- remove some nosie register setting comments

Changes in v2:
- introduce in v2, splite the common Analogix DP changes out

 drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 81 ++++++++++++++++++++++
 drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |  5 ++
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  | 51 ++++++++++++++
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h  | 34 +++++++++
 include/drm/bridge/analogix_dp.h                   |  3 +
 5 files changed, 174 insertions(+)

diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index 32715da..381b25e 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -97,6 +97,83 @@ static int analogix_dp_detect_hpd(struct analogix_dp_device *dp)
 	return 0;
 }
 
+int analogix_dp_enable_psr(struct device *dev)
+{
+	struct analogix_dp_device *dp = dev_get_drvdata(dev);
+	struct edp_vsc_psr psr_vsc;
+
+	if (!dp->psr_support)
+		return -EINVAL;
+
+	/* Prepare VSC packet as per EDP 1.4 spec, Table 6.9 */
+	memset(&psr_vsc, 0, sizeof(psr_vsc));
+	psr_vsc.sdp_header.HB0 = 0;
+	psr_vsc.sdp_header.HB1 = 0x7;
+	psr_vsc.sdp_header.HB2 = 0x2;
+	psr_vsc.sdp_header.HB3 = 0x8;
+
+	psr_vsc.DB0 = 0;
+	psr_vsc.DB1 = EDP_VSC_PSR_STATE_ACTIVE | EDP_VSC_PSR_CRC_VALUES_VALID;
+
+	analogix_dp_send_psr_spd(dp, &psr_vsc);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(analogix_dp_enable_psr);
+
+int analogix_dp_disable_psr(struct device *dev)
+{
+	struct analogix_dp_device *dp = dev_get_drvdata(dev);
+	struct edp_vsc_psr psr_vsc;
+
+	if (!dp->psr_support)
+		return -EINVAL;
+
+	/* Prepare VSC packet as per EDP 1.4 spec, Table 6.9 */
+	memset(&psr_vsc, 0, sizeof(psr_vsc));
+	psr_vsc.sdp_header.HB0 = 0;
+	psr_vsc.sdp_header.HB1 = 0x7;
+	psr_vsc.sdp_header.HB2 = 0x2;
+	psr_vsc.sdp_header.HB3 = 0x8;
+
+	psr_vsc.DB0 = 0;
+	psr_vsc.DB1 = 0;
+
+	analogix_dp_send_psr_spd(dp, &psr_vsc);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(analogix_dp_disable_psr);
+
+static bool analogix_dp_detect_sink_psr(struct analogix_dp_device *dp)
+{
+	unsigned char psr_version;
+
+	analogix_dp_read_byte_from_dpcd(dp, DP_PSR_SUPPORT, &psr_version);
+	dev_dbg(dp->dev, "Panel PSR version : %x\n", psr_version);
+
+	return (psr_version & DP_PSR_IS_SUPPORTED) ? true : false;
+}
+
+static void analogix_dp_enable_sink_psr(struct analogix_dp_device *dp)
+{
+	unsigned char psr_en;
+
+	/* Disable psr function */
+	analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
+	psr_en &= ~DP_PSR_ENABLE;
+	analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
+
+	/* Main-Link transmitter remains active during PSR active states */
+	psr_en = DP_PSR_MAIN_LINK_ACTIVE | DP_PSR_CRC_VERIFICATION;
+	analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
+
+	/* Enable psr function */
+	psr_en = DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE |
+		 DP_PSR_CRC_VERIFICATION;
+	analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
+
+	analogix_dp_enable_psr_crc(dp);
+}
+
 static unsigned char analogix_dp_calc_edid_check_sum(unsigned char *edid_data)
 {
 	int i;
@@ -921,6 +998,10 @@ static void analogix_dp_commit(struct analogix_dp_device *dp)
 
 	/* Enable video */
 	analogix_dp_start_video(dp);
+
+	dp->psr_support = analogix_dp_detect_sink_psr(dp);
+	if (dp->psr_support)
+		analogix_dp_enable_sink_psr(dp);
 }
 
 int analogix_dp_get_modes(struct drm_connector *connector)
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
index b456380..17fbcee 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
@@ -177,6 +177,7 @@ struct analogix_dp_device {
 	int			hpd_gpio;
 	bool                    force_hpd;
 	unsigned char           edid[EDID_BLOCK_LENGTH * 2];
+	bool			psr_support;
 
 	struct analogix_dp_plat_data *plat_data;
 };
@@ -278,4 +279,8 @@ int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp);
 void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp);
 void analogix_dp_enable_scrambling(struct analogix_dp_device *dp);
 void analogix_dp_disable_scrambling(struct analogix_dp_device *dp);
+void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp);
+void analogix_dp_send_psr_spd(struct analogix_dp_device *dp,
+			      struct edp_vsc_psr *vsc);
+
 #endif /* _ANALOGIX_DP_CORE_H */
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
index 48030f0..52c1b6b 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
@@ -1322,3 +1322,54 @@ void analogix_dp_disable_scrambling(struct analogix_dp_device *dp)
 	reg |= SCRAMBLING_DISABLE;
 	writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
 }
+
+void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp)
+{
+	writel(PSR_VID_CRC_ENABLE, dp->reg_base + ANALOGIX_DP_CRC_CON);
+}
+
+void analogix_dp_send_psr_spd(struct analogix_dp_device *dp,
+			      struct edp_vsc_psr *vsc)
+{
+	unsigned int val;
+
+	/* don't send info frame */
+	val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+	val &= ~IF_EN;
+	writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+
+	/* configure single frame update mode */
+	writel(PSR_FRAME_UP_TYPE_BURST | PSR_CRC_SEL_HARDWARE,
+	       dp->reg_base + ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL);
+
+	/* configure VSC HB0~HB3 */
+	writel(vsc->sdp_header.HB0, dp->reg_base + ANALOGIX_DP_SPD_HB0);
+	writel(vsc->sdp_header.HB1, dp->reg_base + ANALOGIX_DP_SPD_HB1);
+	writel(vsc->sdp_header.HB2, dp->reg_base + ANALOGIX_DP_SPD_HB2);
+	writel(vsc->sdp_header.HB3, dp->reg_base + ANALOGIX_DP_SPD_HB3);
+
+	/* configure reused VSC PB0~PB3, magic number from vendor */
+	writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_PB0);
+	writel(0x16, dp->reg_base + ANALOGIX_DP_SPD_PB1);
+	writel(0xCE, dp->reg_base + ANALOGIX_DP_SPD_PB2);
+	writel(0x5D, dp->reg_base + ANALOGIX_DP_SPD_PB3);
+
+	/* configure DB0 / DB1 values */
+	writel(vsc->DB0, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB0);
+	writel(vsc->DB1, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB1);
+
+	/* set reuse spd inforframe */
+	val = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
+	val |= REUSE_SPD_EN;
+	writel(val, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
+
+	/* mark info frame update */
+	val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+	val = (val | IF_UP) & ~IF_EN;
+	writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+
+	/* send info frame */
+	val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+	val |= IF_EN;
+	writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+}
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
index cdcc6c5..40200c6 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
@@ -22,6 +22,8 @@
 #define ANALOGIX_DP_VIDEO_CTL_8			0x3C
 #define ANALOGIX_DP_VIDEO_CTL_10		0x44
 
+#define ANALOGIX_DP_SPDIF_AUDIO_CTL_0		0xD8
+
 #define ANALOGIX_DP_PLL_REG_1			0xfc
 #define ANALOGIX_DP_PLL_REG_2			0x9e4
 #define ANALOGIX_DP_PLL_REG_3			0x9e8
@@ -30,6 +32,21 @@
 
 #define ANALOGIX_DP_PD				0x12c
 
+#define ANALOGIX_DP_IF_TYPE			0x244
+#define ANALOGIX_DP_IF_PKT_DB1			0x254
+#define ANALOGIX_DP_IF_PKT_DB2			0x258
+#define ANALOGIX_DP_SPD_HB0			0x2F8
+#define ANALOGIX_DP_SPD_HB1			0x2FC
+#define ANALOGIX_DP_SPD_HB2			0x300
+#define ANALOGIX_DP_SPD_HB3			0x304
+#define ANALOGIX_DP_SPD_PB0			0x308
+#define ANALOGIX_DP_SPD_PB1			0x30C
+#define ANALOGIX_DP_SPD_PB2			0x310
+#define ANALOGIX_DP_SPD_PB3			0x314
+#define ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL	0x318
+#define ANALOGIX_DP_VSC_SHADOW_DB0		0x31C
+#define ANALOGIX_DP_VSC_SHADOW_DB1		0x320
+
 #define ANALOGIX_DP_LANE_MAP			0x35C
 
 #define ANALOGIX_DP_ANALOG_CTL_1		0x370
@@ -103,6 +120,8 @@
 
 #define ANALOGIX_DP_SOC_GENERAL_CTL		0x800
 
+#define ANALOGIX_DP_CRC_CON			0x890
+
 /* ANALOGIX_DP_TX_SW_RESET */
 #define RESET_DP_TX				(0x1 << 0)
 
@@ -151,6 +170,7 @@
 #define VID_CHK_UPDATE_TYPE_SHIFT		(4)
 #define VID_CHK_UPDATE_TYPE_1			(0x1 << 4)
 #define VID_CHK_UPDATE_TYPE_0			(0x0 << 4)
+#define REUSE_SPD_EN				(0x1 << 3)
 
 /* ANALOGIX_DP_VIDEO_CTL_8 */
 #define VID_HRES_TH(x)				(((x) & 0xf) << 4)
@@ -167,6 +187,12 @@
 #define REF_CLK_27M				(0x0 << 0)
 #define REF_CLK_MASK				(0x1 << 0)
 
+/* ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL */
+#define PSR_FRAME_UP_TYPE_BURST			(0x1 << 0)
+#define PSR_FRAME_UP_TYPE_SINGLE		(0x0 << 0)
+#define PSR_CRC_SEL_HARDWARE			(0x1 << 1)
+#define PSR_CRC_SEL_MANUALLY			(0x0 << 1)
+
 /* ANALOGIX_DP_LANE_MAP */
 #define LANE3_MAP_LOGIC_LANE_0			(0x0 << 6)
 #define LANE3_MAP_LOGIC_LANE_1			(0x1 << 6)
@@ -376,4 +402,12 @@
 #define VIDEO_MODE_SLAVE_MODE			(0x1 << 0)
 #define VIDEO_MODE_MASTER_MODE			(0x0 << 0)
 
+/* ANALOGIX_DP_PKT_SEND_CTL */
+#define IF_UP					(0x1 << 4)
+#define IF_EN					(0x1 << 0)
+
+/* ANALOGIX_DP_CRC_CON */
+#define PSR_VID_CRC_FLUSH			(0x1 << 2)
+#define PSR_VID_CRC_ENABLE			(0x1 << 0)
+
 #endif /* _ANALOGIX_DP_REG_H */
diff --git a/include/drm/bridge/analogix_dp.h b/include/drm/bridge/analogix_dp.h
index 261b86d..9cd8838 100644
--- a/include/drm/bridge/analogix_dp.h
+++ b/include/drm/bridge/analogix_dp.h
@@ -38,6 +38,9 @@ struct analogix_dp_plat_data {
 			 struct drm_connector *);
 };
 
+int analogix_dp_enable_psr(struct device *dev);
+int analogix_dp_disable_psr(struct device *dev);
+
 int analogix_dp_resume(struct device *dev);
 int analogix_dp_suspend(struct device *dev);
 
-- 
1.9.1




From seanpaul at chromium.org  Fri Jul 15 06:04:56 2016
From: seanpaul at chromium.org (Sean Paul)
Date: Fri, 15 Jul 2016 09:04:56 -0400
Subject: [PATCH v4.1 1/4] drm/rockchip: vop: export line flag function
In-Reply-To: <1468580107-18644-1-git-send-email-ykk@rock-chips.com>
References: <1468469704-12440-1-git-send-email-ykk@rock-chips.com>
 <1468580107-18644-1-git-send-email-ykk@rock-chips.com>
Message-ID: <CAOw6vbJE5OZro-nLae045+MkDpn6UhdekdYWfeXdEMM=+fUFjw@mail.gmail.com>

On Fri, Jul 15, 2016 at 6:55 AM, Yakir Yang <ykk at rock-chips.com> wrote:
> VOP have integrated a hardware counter which indicate the exact display
> line that vop is scanning. And if we're interested in a specific line,
> we can set the line number to vop line_flag register, and then vop would
> generate a line_flag interrupt for it.
>
> For example eDP PSR function is interested in the vertical blanking
> period, then driver could set the line number to zero.
>
> This patch have exported a symbol that allow other driver to listen the
> line flag event with given timeout limit:
> -  rockchip_drm_wait_line_flag()
>
> Signed-off-by: Yakir Yang <ykk at rock-chips.com>


Thanks for the update.

Reviewed-by: Sean Paul <seanpaul at chromium.org>

> ---
> Changes in v4.1:
> - Remove the completion_done() check in irq handler (Sean)
>
> Changes in v4:
> - Avoid the weird behavior in rockchip_drm_wait_line_flag(). (Sean)
> - Make line_flag_num_x to an array. (Sean)
> - Remove the unused vop_cfg_done() in vop_line_flag_irq_enable(). (Stephane, reviewed in Google gerrit)
>     [https://chromium-review.googlesource.com/#/c/349084/33/drivers/gpu/drm/rockchip/rockchip_drm_vop.c at 466]
>
> Changes in v3:
> - Export the 'rockchip_drm_wait_line_flag' symbol, and document it.
> - Add 'line_flag_num_0' for RK3288/RK3036
> - Remove the notify for waiting line_flag event (Daniel)
>
> Changes in v2:
> - Introduce in v2, split VOP line flag changes out
>
>  drivers/gpu/drm/rockchip/rockchip_drm_drv.h |   3 +
>  drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 117 ++++++++++++++++++++++++++++
>  drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   2 +
>  drivers/gpu/drm/rockchip/rockchip_vop_reg.c |   4 +
>  4 files changed, 126 insertions(+)
>
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> index ea39329..239b830 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> @@ -70,4 +70,7 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
>                                    struct device *dev);
>  void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
>                                     struct device *dev);
> +int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
> +                               unsigned int mstimeout);
> +
>  #endif /* _ROCKCHIP_DRM_DRV_H_ */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> index c8a62a8..8a4f36e 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> @@ -121,6 +121,8 @@ struct vop {
>         /* protected by dev->event_lock */
>         struct drm_pending_vblank_event *event;
>
> +       struct completion line_flag_completion;
> +
>         const struct vop_data *data;
>
>         uint32_t *regsbak;
> @@ -431,6 +433,71 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop)
>         spin_unlock_irqrestore(&vop->irq_lock, flags);
>  }
>
> +/*
> + * (1) each frame starts at the start of the Vsync pulse which is signaled by
> + *     the "FRAME_SYNC" interrupt.
> + * (2) the active data region of each frame ends at dsp_vact_end
> + * (3) we should program this same number (dsp_vact_end) into dsp_line_frag_num,
> + *      to get "LINE_FLAG" interrupt at the end of the active on screen data.
> + *
> + * VOP_INTR_CTRL0.dsp_line_frag_num = VOP_DSP_VACT_ST_END.dsp_vact_end
> + * Interrupts
> + * LINE_FLAG -------------------------------+
> + * FRAME_SYNC ----+                         |
> + *                |                         |
> + *                v                         v
> + *                | Vsync | Vbp |  Vactive  | Vfp |
> + *                        ^     ^           ^     ^
> + *                        |     |           |     |
> + *                        |     |           |     |
> + * dsp_vs_end ------------+     |           |     |   VOP_DSP_VTOTAL_VS_END
> + * dsp_vact_start --------------+           |     |   VOP_DSP_VACT_ST_END
> + * dsp_vact_end ----------------------------+     |   VOP_DSP_VACT_ST_END
> + * dsp_total -------------------------------------+   VOP_DSP_VTOTAL_VS_END
> + */
> +static bool vop_line_flag_irq_is_enabled(struct vop *vop)
> +{
> +       uint32_t line_flag_irq;
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&vop->irq_lock, flags);
> +
> +       line_flag_irq = VOP_INTR_GET_TYPE(vop, enable, LINE_FLAG_INTR);
> +
> +       spin_unlock_irqrestore(&vop->irq_lock, flags);
> +
> +       return !!line_flag_irq;
> +}
> +
> +static void vop_line_flag_irq_enable(struct vop *vop, int line_num)
> +{
> +       unsigned long flags;
> +
> +       if (WARN_ON(!vop->is_enabled))
> +               return;
> +
> +       spin_lock_irqsave(&vop->irq_lock, flags);
> +
> +       VOP_CTRL_SET(vop, line_flag_num[0], line_num);
> +       VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1);
> +
> +       spin_unlock_irqrestore(&vop->irq_lock, flags);
> +}
> +
> +static void vop_line_flag_irq_disable(struct vop *vop)
> +{
> +       unsigned long flags;
> +
> +       if (WARN_ON(!vop->is_enabled))
> +               return;
> +
> +       spin_lock_irqsave(&vop->irq_lock, flags);
> +
> +       VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 0);
> +
> +       spin_unlock_irqrestore(&vop->irq_lock, flags);
> +}
> +
>  static void vop_enable(struct drm_crtc *crtc)
>  {
>         struct vop *vop = to_vop(crtc);
> @@ -1157,6 +1224,12 @@ static irqreturn_t vop_isr(int irq, void *data)
>                 ret = IRQ_HANDLED;
>         }
>
> +       if (active_irqs & LINE_FLAG_INTR) {
> +               complete(&vop->line_flag_completion);
> +               active_irqs &= ~LINE_FLAG_INTR;
> +               ret = IRQ_HANDLED;
> +       }
> +
>         if (active_irqs & FS_INTR) {
>                 drm_crtc_handle_vblank(crtc);
>                 vop_handle_vblank(vop);
> @@ -1255,6 +1328,7 @@ static int vop_create_crtc(struct vop *vop)
>
>         init_completion(&vop->dsp_hold_completion);
>         init_completion(&vop->wait_update_complete);
> +       init_completion(&vop->line_flag_completion);
>         crtc->port = port;
>         rockchip_register_crtc_funcs(crtc, &private_crtc_funcs);
>
> @@ -1411,6 +1485,49 @@ static void vop_win_init(struct vop *vop)
>         }
>  }
>
> +/**
> + * rockchip_drm_wait_line_flag - acqiure the give line flag event
> + * @crtc: CRTC to enable line flag
> + * @line_num: interested line number
> + * @mstimeout: millisecond for timeout
> + *
> + * Driver would hold here until the interested line flag interrupt have
> + * happened or timeout to wait.
> + *
> + * Returns:
> + * Zero on success, negative errno on failure.
> + */
> +int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
> +                               unsigned int mstimeout)
> +{
> +       struct vop *vop = to_vop(crtc);
> +       unsigned long jiffies_left;
> +
> +       if (!crtc || !vop->is_enabled)
> +               return -ENODEV;
> +
> +       if (line_num > crtc->mode.vtotal || mstimeout <= 0)
> +               return -EINVAL;
> +
> +       if (vop_line_flag_irq_is_enabled(vop))
> +               return -EBUSY;
> +
> +       reinit_completion(&vop->line_flag_completion);
> +       vop_line_flag_irq_enable(vop, line_num);
> +
> +       jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion,
> +                                                  msecs_to_jiffies(mstimeout));
> +       vop_line_flag_irq_disable(vop);
> +
> +       if (jiffies_left == 0) {
> +               dev_err(vop->dev, "Timeout waiting for IRQ\n");
> +               return -ETIMEDOUT;
> +       }
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL(rockchip_drm_wait_line_flag);
> +
>  static int vop_bind(struct device *dev, struct device *master, void *data)
>  {
>         struct platform_device *pdev = to_platform_device(dev);
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> index ff4f52e..1dbc526 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> @@ -61,6 +61,8 @@ struct vop_ctrl {
>         struct vop_reg hpost_st_end;
>         struct vop_reg vpost_st_end;
>
> +       struct vop_reg line_flag_num[2];
> +
>         struct vop_reg cfg_done;
>  };
>
> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> index 6f42e56..eea8427 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> @@ -122,6 +122,7 @@ static const struct vop_ctrl rk3036_ctrl_data = {
>         .hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0),
>         .vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
>         .vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0),
> +       .line_flag_num[0] = VOP_REG(RK3036_INT_STATUS, 0xfff, 12),
>         .cfg_done = VOP_REG(RK3036_REG_CFG_DONE, 0x1, 0),
>  };
>
> @@ -221,6 +222,7 @@ static const struct vop_ctrl rk3288_ctrl_data = {
>         .vact_st_end = VOP_REG(RK3288_DSP_VACT_ST_END, 0x1fff1fff, 0),
>         .hpost_st_end = VOP_REG(RK3288_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
>         .vpost_st_end = VOP_REG(RK3288_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
> +       .line_flag_num[0] = VOP_REG(RK3288_INTR_CTRL0, 0x1fff, 12),
>         .cfg_done = VOP_REG(RK3288_REG_CFG_DONE, 0x1, 0),
>  };
>
> @@ -299,6 +301,8 @@ static const struct vop_ctrl rk3399_ctrl_data = {
>         .vact_st_end = VOP_REG(RK3399_DSP_VACT_ST_END, 0x1fff1fff, 0),
>         .hpost_st_end = VOP_REG(RK3399_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
>         .vpost_st_end = VOP_REG(RK3399_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
> +       .line_flag_num[0] = VOP_REG(RK3399_LINE_FLAG, 0xffff, 0),
> +       .line_flag_num[1] = VOP_REG(RK3399_LINE_FLAG, 0xffff, 16),
>         .cfg_done = VOP_REG_MASK(RK3399_REG_CFG_DONE, 0x1, 0),
>  };
>
> --
> 1.9.1
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


From seanpaul at chromium.org  Fri Jul 15 06:13:57 2016
From: seanpaul at chromium.org (Sean Paul)
Date: Fri, 15 Jul 2016 09:13:57 -0400
Subject: [PATCH v4.1 3/4] drm/bridge: analogix_dp: add the PSR function
 support
In-Reply-To: <1468580117-18697-1-git-send-email-ykk@rock-chips.com>
References: <1468469704-12440-1-git-send-email-ykk@rock-chips.com>
 <1468580117-18697-1-git-send-email-ykk@rock-chips.com>
Message-ID: <20160715131357.GA2893@seanpaul0.roam.corp.google.com>

On Fri, Jul 15, 2016 at 06:55:17PM +0800, Yakir Yang wrote:
> The full name of PSR is Panel Self Refresh, panel device could refresh
> itself with the hardware framebuffer in panel, this would make lots of
> sense to save the power consumption.
> 
> This patch have exported two symbols for platform driver to implement
> the PSR function in hardware side:
> - analogix_dp_active_psr()
> - analogix_dp_inactive_psr()
> 
> Signed-off-by: Yakir Yang <ykk at rock-chips.com>

Reviewed-by: Sean Paul <seanpaul at chromium.org>

> ---
> Changes in v4.1:
> - Take use of existing edp_psr_vsc struct to swap HBx and DBx setting. (Sean)
> - Remove PSR_VID_CRC_FLUSH setting analogix_dp_enable_psr_crc().
> - Add comment about PBx magic numbers. (Sean)
> 
> Changes in v4:
> - Downgrade the PSR version print message to debug level. (Sean)
> - Return 'void' instead of 'int' in analogix_dp_enable_sink_psr(). (Sean)
> - Delete the unused read dpcd operations in analogix_dp_enable_sink_psr(). (Sean)
> - Delete the arbitrary usleep_range in analogix_dp_enable_psr_crc. (Sean).
> - Clean up the hardcoded values in analogix_dp_send_psr_spd(). (Sean)
> - Rename "active/inactive" to "enable/disable". (Sean, Dominik)
> - Keep set the PSR_VID_CRC_FLUSH gate in analogix_dp_enable_psr_crc().
> 
> Changes in v3:
> - split analogix_dp_enable_psr(), make it more clearly
>     analogix_dp_detect_sink_psr()
>     analogix_dp_enable_sink_psr()
> - remove some nosie register setting comments
> 
> Changes in v2:
> - introduce in v2, splite the common Analogix DP changes out
> 
>  drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 81 ++++++++++++++++++++++
>  drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |  5 ++
>  drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  | 51 ++++++++++++++
>  drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h  | 34 +++++++++
>  include/drm/bridge/analogix_dp.h                   |  3 +
>  5 files changed, 174 insertions(+)
> 
> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> index 32715da..381b25e 100644
> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> @@ -97,6 +97,83 @@ static int analogix_dp_detect_hpd(struct analogix_dp_device *dp)
>  	return 0;
>  }
>  
> +int analogix_dp_enable_psr(struct device *dev)
> +{
> +	struct analogix_dp_device *dp = dev_get_drvdata(dev);
> +	struct edp_vsc_psr psr_vsc;
> +
> +	if (!dp->psr_support)
> +		return -EINVAL;
> +
> +	/* Prepare VSC packet as per EDP 1.4 spec, Table 6.9 */
> +	memset(&psr_vsc, 0, sizeof(psr_vsc));
> +	psr_vsc.sdp_header.HB0 = 0;
> +	psr_vsc.sdp_header.HB1 = 0x7;
> +	psr_vsc.sdp_header.HB2 = 0x2;
> +	psr_vsc.sdp_header.HB3 = 0x8;
> +
> +	psr_vsc.DB0 = 0;
> +	psr_vsc.DB1 = EDP_VSC_PSR_STATE_ACTIVE | EDP_VSC_PSR_CRC_VALUES_VALID;
> +
> +	analogix_dp_send_psr_spd(dp, &psr_vsc);
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(analogix_dp_enable_psr);
> +
> +int analogix_dp_disable_psr(struct device *dev)
> +{
> +	struct analogix_dp_device *dp = dev_get_drvdata(dev);
> +	struct edp_vsc_psr psr_vsc;
> +
> +	if (!dp->psr_support)
> +		return -EINVAL;
> +
> +	/* Prepare VSC packet as per EDP 1.4 spec, Table 6.9 */
> +	memset(&psr_vsc, 0, sizeof(psr_vsc));
> +	psr_vsc.sdp_header.HB0 = 0;
> +	psr_vsc.sdp_header.HB1 = 0x7;
> +	psr_vsc.sdp_header.HB2 = 0x2;
> +	psr_vsc.sdp_header.HB3 = 0x8;
> +
> +	psr_vsc.DB0 = 0;
> +	psr_vsc.DB1 = 0;
> +
> +	analogix_dp_send_psr_spd(dp, &psr_vsc);
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(analogix_dp_disable_psr);
> +
> +static bool analogix_dp_detect_sink_psr(struct analogix_dp_device *dp)
> +{
> +	unsigned char psr_version;
> +
> +	analogix_dp_read_byte_from_dpcd(dp, DP_PSR_SUPPORT, &psr_version);
> +	dev_dbg(dp->dev, "Panel PSR version : %x\n", psr_version);
> +
> +	return (psr_version & DP_PSR_IS_SUPPORTED) ? true : false;
> +}
> +
> +static void analogix_dp_enable_sink_psr(struct analogix_dp_device *dp)
> +{
> +	unsigned char psr_en;
> +
> +	/* Disable psr function */
> +	analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
> +	psr_en &= ~DP_PSR_ENABLE;
> +	analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
> +
> +	/* Main-Link transmitter remains active during PSR active states */
> +	psr_en = DP_PSR_MAIN_LINK_ACTIVE | DP_PSR_CRC_VERIFICATION;
> +	analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
> +
> +	/* Enable psr function */
> +	psr_en = DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE |
> +		 DP_PSR_CRC_VERIFICATION;
> +	analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
> +
> +	analogix_dp_enable_psr_crc(dp);
> +}
> +
>  static unsigned char analogix_dp_calc_edid_check_sum(unsigned char *edid_data)
>  {
>  	int i;
> @@ -921,6 +998,10 @@ static void analogix_dp_commit(struct analogix_dp_device *dp)
>  
>  	/* Enable video */
>  	analogix_dp_start_video(dp);
> +
> +	dp->psr_support = analogix_dp_detect_sink_psr(dp);
> +	if (dp->psr_support)
> +		analogix_dp_enable_sink_psr(dp);
>  }
>  
>  int analogix_dp_get_modes(struct drm_connector *connector)
> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
> index b456380..17fbcee 100644
> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
> @@ -177,6 +177,7 @@ struct analogix_dp_device {
>  	int			hpd_gpio;
>  	bool                    force_hpd;
>  	unsigned char           edid[EDID_BLOCK_LENGTH * 2];
> +	bool			psr_support;
>  
>  	struct analogix_dp_plat_data *plat_data;
>  };
> @@ -278,4 +279,8 @@ int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp);
>  void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp);
>  void analogix_dp_enable_scrambling(struct analogix_dp_device *dp);
>  void analogix_dp_disable_scrambling(struct analogix_dp_device *dp);
> +void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp);
> +void analogix_dp_send_psr_spd(struct analogix_dp_device *dp,
> +			      struct edp_vsc_psr *vsc);
> +
>  #endif /* _ANALOGIX_DP_CORE_H */
> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
> index 48030f0..52c1b6b 100644
> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
> @@ -1322,3 +1322,54 @@ void analogix_dp_disable_scrambling(struct analogix_dp_device *dp)
>  	reg |= SCRAMBLING_DISABLE;
>  	writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
>  }
> +
> +void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp)
> +{
> +	writel(PSR_VID_CRC_ENABLE, dp->reg_base + ANALOGIX_DP_CRC_CON);
> +}
> +
> +void analogix_dp_send_psr_spd(struct analogix_dp_device *dp,
> +			      struct edp_vsc_psr *vsc)
> +{
> +	unsigned int val;
> +
> +	/* don't send info frame */
> +	val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
> +	val &= ~IF_EN;
> +	writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
> +
> +	/* configure single frame update mode */
> +	writel(PSR_FRAME_UP_TYPE_BURST | PSR_CRC_SEL_HARDWARE,
> +	       dp->reg_base + ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL);
> +
> +	/* configure VSC HB0~HB3 */
> +	writel(vsc->sdp_header.HB0, dp->reg_base + ANALOGIX_DP_SPD_HB0);
> +	writel(vsc->sdp_header.HB1, dp->reg_base + ANALOGIX_DP_SPD_HB1);
> +	writel(vsc->sdp_header.HB2, dp->reg_base + ANALOGIX_DP_SPD_HB2);
> +	writel(vsc->sdp_header.HB3, dp->reg_base + ANALOGIX_DP_SPD_HB3);
> +
> +	/* configure reused VSC PB0~PB3, magic number from vendor */
> +	writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_PB0);
> +	writel(0x16, dp->reg_base + ANALOGIX_DP_SPD_PB1);
> +	writel(0xCE, dp->reg_base + ANALOGIX_DP_SPD_PB2);
> +	writel(0x5D, dp->reg_base + ANALOGIX_DP_SPD_PB3);
> +
> +	/* configure DB0 / DB1 values */
> +	writel(vsc->DB0, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB0);
> +	writel(vsc->DB1, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB1);
> +
> +	/* set reuse spd inforframe */
> +	val = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
> +	val |= REUSE_SPD_EN;
> +	writel(val, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
> +
> +	/* mark info frame update */
> +	val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
> +	val = (val | IF_UP) & ~IF_EN;
> +	writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
> +
> +	/* send info frame */
> +	val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
> +	val |= IF_EN;
> +	writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
> +}
> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
> index cdcc6c5..40200c6 100644
> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
> @@ -22,6 +22,8 @@
>  #define ANALOGIX_DP_VIDEO_CTL_8			0x3C
>  #define ANALOGIX_DP_VIDEO_CTL_10		0x44
>  
> +#define ANALOGIX_DP_SPDIF_AUDIO_CTL_0		0xD8
> +
>  #define ANALOGIX_DP_PLL_REG_1			0xfc
>  #define ANALOGIX_DP_PLL_REG_2			0x9e4
>  #define ANALOGIX_DP_PLL_REG_3			0x9e8
> @@ -30,6 +32,21 @@
>  
>  #define ANALOGIX_DP_PD				0x12c
>  
> +#define ANALOGIX_DP_IF_TYPE			0x244
> +#define ANALOGIX_DP_IF_PKT_DB1			0x254
> +#define ANALOGIX_DP_IF_PKT_DB2			0x258
> +#define ANALOGIX_DP_SPD_HB0			0x2F8
> +#define ANALOGIX_DP_SPD_HB1			0x2FC
> +#define ANALOGIX_DP_SPD_HB2			0x300
> +#define ANALOGIX_DP_SPD_HB3			0x304
> +#define ANALOGIX_DP_SPD_PB0			0x308
> +#define ANALOGIX_DP_SPD_PB1			0x30C
> +#define ANALOGIX_DP_SPD_PB2			0x310
> +#define ANALOGIX_DP_SPD_PB3			0x314
> +#define ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL	0x318
> +#define ANALOGIX_DP_VSC_SHADOW_DB0		0x31C
> +#define ANALOGIX_DP_VSC_SHADOW_DB1		0x320
> +
>  #define ANALOGIX_DP_LANE_MAP			0x35C
>  
>  #define ANALOGIX_DP_ANALOG_CTL_1		0x370
> @@ -103,6 +120,8 @@
>  
>  #define ANALOGIX_DP_SOC_GENERAL_CTL		0x800
>  
> +#define ANALOGIX_DP_CRC_CON			0x890
> +
>  /* ANALOGIX_DP_TX_SW_RESET */
>  #define RESET_DP_TX				(0x1 << 0)
>  
> @@ -151,6 +170,7 @@
>  #define VID_CHK_UPDATE_TYPE_SHIFT		(4)
>  #define VID_CHK_UPDATE_TYPE_1			(0x1 << 4)
>  #define VID_CHK_UPDATE_TYPE_0			(0x0 << 4)
> +#define REUSE_SPD_EN				(0x1 << 3)
>  
>  /* ANALOGIX_DP_VIDEO_CTL_8 */
>  #define VID_HRES_TH(x)				(((x) & 0xf) << 4)
> @@ -167,6 +187,12 @@
>  #define REF_CLK_27M				(0x0 << 0)
>  #define REF_CLK_MASK				(0x1 << 0)
>  
> +/* ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL */
> +#define PSR_FRAME_UP_TYPE_BURST			(0x1 << 0)
> +#define PSR_FRAME_UP_TYPE_SINGLE		(0x0 << 0)
> +#define PSR_CRC_SEL_HARDWARE			(0x1 << 1)
> +#define PSR_CRC_SEL_MANUALLY			(0x0 << 1)
> +
>  /* ANALOGIX_DP_LANE_MAP */
>  #define LANE3_MAP_LOGIC_LANE_0			(0x0 << 6)
>  #define LANE3_MAP_LOGIC_LANE_1			(0x1 << 6)
> @@ -376,4 +402,12 @@
>  #define VIDEO_MODE_SLAVE_MODE			(0x1 << 0)
>  #define VIDEO_MODE_MASTER_MODE			(0x0 << 0)
>  
> +/* ANALOGIX_DP_PKT_SEND_CTL */
> +#define IF_UP					(0x1 << 4)
> +#define IF_EN					(0x1 << 0)
> +
> +/* ANALOGIX_DP_CRC_CON */
> +#define PSR_VID_CRC_FLUSH			(0x1 << 2)
> +#define PSR_VID_CRC_ENABLE			(0x1 << 0)
> +
>  #endif /* _ANALOGIX_DP_REG_H */
> diff --git a/include/drm/bridge/analogix_dp.h b/include/drm/bridge/analogix_dp.h
> index 261b86d..9cd8838 100644
> --- a/include/drm/bridge/analogix_dp.h
> +++ b/include/drm/bridge/analogix_dp.h
> @@ -38,6 +38,9 @@ struct analogix_dp_plat_data {
>  			 struct drm_connector *);
>  };
>  
> +int analogix_dp_enable_psr(struct device *dev);
> +int analogix_dp_disable_psr(struct device *dev);
> +
>  int analogix_dp_resume(struct device *dev);
>  int analogix_dp_suspend(struct device *dev);
>  
> -- 
> 1.9.1
> 
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel


From elfring at users.sourceforge.net  Fri Jul 15 06:35:42 2016
From: elfring at users.sourceforge.net (SF Markus Elfring)
Date: Fri, 15 Jul 2016 15:35:42 +0200
Subject: [PATCH] drm/rockchip: Delete an unnecessary check before
 drm_gem_object_unreference_unlocked()
In-Reply-To: <5317A59D.4@users.sourceforge.net>
References: <5307CAA2.8060406@users.sourceforge.net>
 <alpine.DEB.2.02.1402212321410.2043@localhost6.localdomain6>
 <530A086E.8010901@users.sourceforge.net>
 <alpine.DEB.2.02.1402231635510.1985@localhost6.localdomain6>
 <530A72AA.3000601@users.sourceforge.net>
 <alpine.DEB.2.02.1402240658210.2090@localhost6.localdomain6>
 <530B5FB6.6010207@users.sourceforge.net>
 <alpine.DEB.2.10.1402241710370.2074@hadrien>
 <530C5E18.1020800@users.sourceforge.net>
 <alpine.DEB.2.10.1402251014170.2080@hadrien>
 <530CD2C4.4050903@users.sourceforge.net>
 <alpine.DEB.2.10.1402251840450.7035@hadrien>
 <530CF8FF.8080600@users.sourceforge.net>
 <alpine.DEB.2.02.1402252117150.2047@localhost6.localdomain6>
 <530DD06F.4090703@users.sourceforge.net>
 <alpine.DEB.2.02.1402262129250.2221@localhost6.localdomain6>
 <5317A59D.4@users.sourceforge.net>
Message-ID: <b66399ed-278b-b85d-4a21-b34164936ef6@users.sourceforge.net>

From: Markus Elfring <elfring at users.sourceforge.net>
Date: Fri, 15 Jul 2016 15:23:22 +0200

The drm_gem_object_unreference_unlocked() function tests whether
its argument is NULL and then returns immediately.
Thus the test around the call is not needed.

This issue was detected by using the Coccinelle software.

Signed-off-by: Markus Elfring <elfring at users.sourceforge.net>
---
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
index 20f12bc..6f64e79 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
@@ -43,14 +43,10 @@ struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb,
 static void rockchip_drm_fb_destroy(struct drm_framebuffer *fb)
 {
 	struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
-	struct drm_gem_object *obj;
 	int i;
 
-	for (i = 0; i < ROCKCHIP_MAX_FB_BUFFER; i++) {
-		obj = rockchip_fb->obj[i];
-		if (obj)
-			drm_gem_object_unreference_unlocked(obj);
-	}
+	for (i = 0; i < ROCKCHIP_MAX_FB_BUFFER; i++)
+		drm_gem_object_unreference_unlocked(rockchip_fb->obj[i]);
 
 	drm_framebuffer_cleanup(fb);
 	kfree(rockchip_fb);
-- 
2.9.1



From mbrugger at suse.com  Fri Jul 15 08:32:02 2016
From: mbrugger at suse.com (Matthias Brugger)
Date: Fri, 15 Jul 2016 17:32:02 +0200
Subject: iommu/rockchip: Fix bugs and enable on ARM64
In-Reply-To: <577EFB86.1060502@rock-chips.com>
References: <1466734413-7453-1-git-send-email-zhengsq@rock-chips.com>
 <20160627125751.GN11432@8bytes.org> <577EFB86.1060502@rock-chips.com>
Message-ID: <575f6cd7-6e03-e701-78ed-ea2fe9f7573c@suse.com>



On 08/07/16 03:01, Mark yao wrote:
> On 2016?06?27? 20:57, Joerg Roedel wrote:
>> On Fri, Jun 24, 2016 at 10:13:25AM +0800, Shunqian Zheng wrote:
>>>   drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 100 +++++++------
>>>   drivers/gpu/drm/rockchip/rockchip_drm_drv.h |   3 +
>>>   drivers/gpu/drm/rockchip/rockchip_drm_gem.c | 221
>>> ++++++++++++++++++++++++++--
>>>   drivers/gpu/drm/rockchip/rockchip_drm_gem.h |   9 ++
>>>   drivers/iommu/Kconfig                       |   2 +-
>>>   drivers/iommu/rockchip-iommu.c              | 181
>>> +++++++++++++++++------
>>>   6 files changed, 413 insertions(+), 103 deletions(-)
>> Applied the IOMMU patches, thanks.
>>
>>
>>     Joerg
>>
>>
>>
>>
>
> Hi Joerg
>
> The drm rockchip patches are dependent on iommu/rockchip patches, can
> you also apply these patches together? So that can avoid compile problem.
>

While at it. I don't see patch 8 (iommu/Kconfig) in linux-next.
I suppose you forgot to pick that one.

Regards,
Matthias


From joro at 8bytes.org  Fri Jul 15 09:16:51 2016
From: joro at 8bytes.org (Joerg Roedel)
Date: Fri, 15 Jul 2016 18:16:51 +0200
Subject: iommu/rockchip: Fix bugs and enable on ARM64
In-Reply-To: <575f6cd7-6e03-e701-78ed-ea2fe9f7573c@suse.com>
References: <1466734413-7453-1-git-send-email-zhengsq@rock-chips.com>
 <20160627125751.GN11432@8bytes.org>
 <577EFB86.1060502@rock-chips.com>
 <575f6cd7-6e03-e701-78ed-ea2fe9f7573c@suse.com>
Message-ID: <20160715161651.GS12639@8bytes.org>

On Fri, Jul 15, 2016 at 05:32:02PM +0200, Matthias Brugger wrote:
> >The drm rockchip patches are dependent on iommu/rockchip patches, can
> >you also apply these patches together? So that can avoid compile problem.
> >
> 
> While at it. I don't see patch 8 (iommu/Kconfig) in linux-next.
> I suppose you forgot to pick that one.

I picked it up first, but it causes compile errors, so I removed it for
now.


	Joerg



From ykk at rock-chips.com  Fri Jul 15 19:30:52 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Sat, 16 Jul 2016 10:30:52 +0800
Subject: [PATCH v4.1 1/4] drm/rockchip: vop: export line flag function
In-Reply-To: <CAOw6vbJE5OZro-nLae045+MkDpn6UhdekdYWfeXdEMM=+fUFjw@mail.gmail.com>
References: <1468469704-12440-1-git-send-email-ykk@rock-chips.com>
 <1468580107-18644-1-git-send-email-ykk@rock-chips.com>
 <CAOw6vbJE5OZro-nLae045+MkDpn6UhdekdYWfeXdEMM=+fUFjw@mail.gmail.com>
Message-ID: <57899C5C.7050403@rock-chips.com>

Sean,

On 07/15/2016 09:04 PM, Sean Paul wrote:
> On Fri, Jul 15, 2016 at 6:55 AM, Yakir Yang <ykk at rock-chips.com> wrote:
>> VOP have integrated a hardware counter which indicate the exact display
>> line that vop is scanning. And if we're interested in a specific line,
>> we can set the line number to vop line_flag register, and then vop would
>> generate a line_flag interrupt for it.
>>
>> For example eDP PSR function is interested in the vertical blanking
>> period, then driver could set the line number to zero.
>>
>> This patch have exported a symbol that allow other driver to listen the
>> line flag event with given timeout limit:
>> -  rockchip_drm_wait_line_flag()
>>
>> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
>
> Thanks for the update.
>
> Reviewed-by: Sean Paul <seanpaul at chromium.org>

Thanks for your reviewed  :-D

- Yakir

>> ---
>> Changes in v4.1:
>> - Remove the completion_done() check in irq handler (Sean)
>>
>> Changes in v4:
>> - Avoid the weird behavior in rockchip_drm_wait_line_flag(). (Sean)
>> - Make line_flag_num_x to an array. (Sean)
>> - Remove the unused vop_cfg_done() in vop_line_flag_irq_enable(). (Stephane, reviewed in Google gerrit)
>>      [https://chromium-review.googlesource.com/#/c/349084/33/drivers/gpu/drm/rockchip/rockchip_drm_vop.c at 466]
>>
>> Changes in v3:
>> - Export the 'rockchip_drm_wait_line_flag' symbol, and document it.
>> - Add 'line_flag_num_0' for RK3288/RK3036
>> - Remove the notify for waiting line_flag event (Daniel)
>>
>> Changes in v2:
>> - Introduce in v2, split VOP line flag changes out
>>
>>   drivers/gpu/drm/rockchip/rockchip_drm_drv.h |   3 +
>>   drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 117 ++++++++++++++++++++++++++++
>>   drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   2 +
>>   drivers/gpu/drm/rockchip/rockchip_vop_reg.c |   4 +
>>   4 files changed, 126 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>> index ea39329..239b830 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>> @@ -70,4 +70,7 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
>>                                     struct device *dev);
>>   void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
>>                                      struct device *dev);
>> +int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
>> +                               unsigned int mstimeout);
>> +
>>   #endif /* _ROCKCHIP_DRM_DRV_H_ */
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> index c8a62a8..8a4f36e 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> @@ -121,6 +121,8 @@ struct vop {
>>          /* protected by dev->event_lock */
>>          struct drm_pending_vblank_event *event;
>>
>> +       struct completion line_flag_completion;
>> +
>>          const struct vop_data *data;
>>
>>          uint32_t *regsbak;
>> @@ -431,6 +433,71 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop)
>>          spin_unlock_irqrestore(&vop->irq_lock, flags);
>>   }
>>
>> +/*
>> + * (1) each frame starts at the start of the Vsync pulse which is signaled by
>> + *     the "FRAME_SYNC" interrupt.
>> + * (2) the active data region of each frame ends at dsp_vact_end
>> + * (3) we should program this same number (dsp_vact_end) into dsp_line_frag_num,
>> + *      to get "LINE_FLAG" interrupt at the end of the active on screen data.
>> + *
>> + * VOP_INTR_CTRL0.dsp_line_frag_num = VOP_DSP_VACT_ST_END.dsp_vact_end
>> + * Interrupts
>> + * LINE_FLAG -------------------------------+
>> + * FRAME_SYNC ----+                         |
>> + *                |                         |
>> + *                v                         v
>> + *                | Vsync | Vbp |  Vactive  | Vfp |
>> + *                        ^     ^           ^     ^
>> + *                        |     |           |     |
>> + *                        |     |           |     |
>> + * dsp_vs_end ------------+     |           |     |   VOP_DSP_VTOTAL_VS_END
>> + * dsp_vact_start --------------+           |     |   VOP_DSP_VACT_ST_END
>> + * dsp_vact_end ----------------------------+     |   VOP_DSP_VACT_ST_END
>> + * dsp_total -------------------------------------+   VOP_DSP_VTOTAL_VS_END
>> + */
>> +static bool vop_line_flag_irq_is_enabled(struct vop *vop)
>> +{
>> +       uint32_t line_flag_irq;
>> +       unsigned long flags;
>> +
>> +       spin_lock_irqsave(&vop->irq_lock, flags);
>> +
>> +       line_flag_irq = VOP_INTR_GET_TYPE(vop, enable, LINE_FLAG_INTR);
>> +
>> +       spin_unlock_irqrestore(&vop->irq_lock, flags);
>> +
>> +       return !!line_flag_irq;
>> +}
>> +
>> +static void vop_line_flag_irq_enable(struct vop *vop, int line_num)
>> +{
>> +       unsigned long flags;
>> +
>> +       if (WARN_ON(!vop->is_enabled))
>> +               return;
>> +
>> +       spin_lock_irqsave(&vop->irq_lock, flags);
>> +
>> +       VOP_CTRL_SET(vop, line_flag_num[0], line_num);
>> +       VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1);
>> +
>> +       spin_unlock_irqrestore(&vop->irq_lock, flags);
>> +}
>> +
>> +static void vop_line_flag_irq_disable(struct vop *vop)
>> +{
>> +       unsigned long flags;
>> +
>> +       if (WARN_ON(!vop->is_enabled))
>> +               return;
>> +
>> +       spin_lock_irqsave(&vop->irq_lock, flags);
>> +
>> +       VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 0);
>> +
>> +       spin_unlock_irqrestore(&vop->irq_lock, flags);
>> +}
>> +
>>   static void vop_enable(struct drm_crtc *crtc)
>>   {
>>          struct vop *vop = to_vop(crtc);
>> @@ -1157,6 +1224,12 @@ static irqreturn_t vop_isr(int irq, void *data)
>>                  ret = IRQ_HANDLED;
>>          }
>>
>> +       if (active_irqs & LINE_FLAG_INTR) {
>> +               complete(&vop->line_flag_completion);
>> +               active_irqs &= ~LINE_FLAG_INTR;
>> +               ret = IRQ_HANDLED;
>> +       }
>> +
>>          if (active_irqs & FS_INTR) {
>>                  drm_crtc_handle_vblank(crtc);
>>                  vop_handle_vblank(vop);
>> @@ -1255,6 +1328,7 @@ static int vop_create_crtc(struct vop *vop)
>>
>>          init_completion(&vop->dsp_hold_completion);
>>          init_completion(&vop->wait_update_complete);
>> +       init_completion(&vop->line_flag_completion);
>>          crtc->port = port;
>>          rockchip_register_crtc_funcs(crtc, &private_crtc_funcs);
>>
>> @@ -1411,6 +1485,49 @@ static void vop_win_init(struct vop *vop)
>>          }
>>   }
>>
>> +/**
>> + * rockchip_drm_wait_line_flag - acqiure the give line flag event
>> + * @crtc: CRTC to enable line flag
>> + * @line_num: interested line number
>> + * @mstimeout: millisecond for timeout
>> + *
>> + * Driver would hold here until the interested line flag interrupt have
>> + * happened or timeout to wait.
>> + *
>> + * Returns:
>> + * Zero on success, negative errno on failure.
>> + */
>> +int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
>> +                               unsigned int mstimeout)
>> +{
>> +       struct vop *vop = to_vop(crtc);
>> +       unsigned long jiffies_left;
>> +
>> +       if (!crtc || !vop->is_enabled)
>> +               return -ENODEV;
>> +
>> +       if (line_num > crtc->mode.vtotal || mstimeout <= 0)
>> +               return -EINVAL;
>> +
>> +       if (vop_line_flag_irq_is_enabled(vop))
>> +               return -EBUSY;
>> +
>> +       reinit_completion(&vop->line_flag_completion);
>> +       vop_line_flag_irq_enable(vop, line_num);
>> +
>> +       jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion,
>> +                                                  msecs_to_jiffies(mstimeout));
>> +       vop_line_flag_irq_disable(vop);
>> +
>> +       if (jiffies_left == 0) {
>> +               dev_err(vop->dev, "Timeout waiting for IRQ\n");
>> +               return -ETIMEDOUT;
>> +       }
>> +
>> +       return 0;
>> +}
>> +EXPORT_SYMBOL(rockchip_drm_wait_line_flag);
>> +
>>   static int vop_bind(struct device *dev, struct device *master, void *data)
>>   {
>>          struct platform_device *pdev = to_platform_device(dev);
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>> index ff4f52e..1dbc526 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
>> @@ -61,6 +61,8 @@ struct vop_ctrl {
>>          struct vop_reg hpost_st_end;
>>          struct vop_reg vpost_st_end;
>>
>> +       struct vop_reg line_flag_num[2];
>> +
>>          struct vop_reg cfg_done;
>>   };
>>
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>> index 6f42e56..eea8427 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>> +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
>> @@ -122,6 +122,7 @@ static const struct vop_ctrl rk3036_ctrl_data = {
>>          .hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0),
>>          .vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
>>          .vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0),
>> +       .line_flag_num[0] = VOP_REG(RK3036_INT_STATUS, 0xfff, 12),
>>          .cfg_done = VOP_REG(RK3036_REG_CFG_DONE, 0x1, 0),
>>   };
>>
>> @@ -221,6 +222,7 @@ static const struct vop_ctrl rk3288_ctrl_data = {
>>          .vact_st_end = VOP_REG(RK3288_DSP_VACT_ST_END, 0x1fff1fff, 0),
>>          .hpost_st_end = VOP_REG(RK3288_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
>>          .vpost_st_end = VOP_REG(RK3288_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
>> +       .line_flag_num[0] = VOP_REG(RK3288_INTR_CTRL0, 0x1fff, 12),
>>          .cfg_done = VOP_REG(RK3288_REG_CFG_DONE, 0x1, 0),
>>   };
>>
>> @@ -299,6 +301,8 @@ static const struct vop_ctrl rk3399_ctrl_data = {
>>          .vact_st_end = VOP_REG(RK3399_DSP_VACT_ST_END, 0x1fff1fff, 0),
>>          .hpost_st_end = VOP_REG(RK3399_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
>>          .vpost_st_end = VOP_REG(RK3399_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
>> +       .line_flag_num[0] = VOP_REG(RK3399_LINE_FLAG, 0xffff, 0),
>> +       .line_flag_num[1] = VOP_REG(RK3399_LINE_FLAG, 0xffff, 16),
>>          .cfg_done = VOP_REG_MASK(RK3399_REG_CFG_DONE, 0x1, 0),
>>   };
>>
>> --
>> 1.9.1
>>
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
>> the body of a message to majordomo at vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>
>




From ykk at rock-chips.com  Fri Jul 15 19:31:12 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Sat, 16 Jul 2016 10:31:12 +0800
Subject: [PATCH v4.1 3/4] drm/bridge: analogix_dp: add the PSR function
 support
In-Reply-To: <20160715131357.GA2893@seanpaul0.roam.corp.google.com>
References: <1468469704-12440-1-git-send-email-ykk@rock-chips.com>
 <1468580117-18697-1-git-send-email-ykk@rock-chips.com>
 <20160715131357.GA2893@seanpaul0.roam.corp.google.com>
Message-ID: <57899C70.8010908@rock-chips.com>

Sean,

On 07/15/2016 09:13 PM, Sean Paul wrote:
> On Fri, Jul 15, 2016 at 06:55:17PM +0800, Yakir Yang wrote:
>> The full name of PSR is Panel Self Refresh, panel device could refresh
>> itself with the hardware framebuffer in panel, this would make lots of
>> sense to save the power consumption.
>>
>> This patch have exported two symbols for platform driver to implement
>> the PSR function in hardware side:
>> - analogix_dp_active_psr()
>> - analogix_dp_inactive_psr()
>>
>> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
> Reviewed-by: Sean Paul <seanpaul at chromium.org>

Thanks for your reviewed  :-D

- Yakir

>
>> ---
>> Changes in v4.1:
>> - Take use of existing edp_psr_vsc struct to swap HBx and DBx setting. (Sean)
>> - Remove PSR_VID_CRC_FLUSH setting analogix_dp_enable_psr_crc().
>> - Add comment about PBx magic numbers. (Sean)
>>
>> Changes in v4:
>> - Downgrade the PSR version print message to debug level. (Sean)
>> - Return 'void' instead of 'int' in analogix_dp_enable_sink_psr(). (Sean)
>> - Delete the unused read dpcd operations in analogix_dp_enable_sink_psr(). (Sean)
>> - Delete the arbitrary usleep_range in analogix_dp_enable_psr_crc. (Sean).
>> - Clean up the hardcoded values in analogix_dp_send_psr_spd(). (Sean)
>> - Rename "active/inactive" to "enable/disable". (Sean, Dominik)
>> - Keep set the PSR_VID_CRC_FLUSH gate in analogix_dp_enable_psr_crc().
>>
>> Changes in v3:
>> - split analogix_dp_enable_psr(), make it more clearly
>>      analogix_dp_detect_sink_psr()
>>      analogix_dp_enable_sink_psr()
>> - remove some nosie register setting comments
>>
>> Changes in v2:
>> - introduce in v2, splite the common Analogix DP changes out
>>
>>   drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 81 ++++++++++++++++++++++
>>   drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |  5 ++
>>   drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  | 51 ++++++++++++++
>>   drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h  | 34 +++++++++
>>   include/drm/bridge/analogix_dp.h                   |  3 +
>>   5 files changed, 174 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>> index 32715da..381b25e 100644
>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>> @@ -97,6 +97,83 @@ static int analogix_dp_detect_hpd(struct analogix_dp_device *dp)
>>   	return 0;
>>   }
>>   
>> +int analogix_dp_enable_psr(struct device *dev)
>> +{
>> +	struct analogix_dp_device *dp = dev_get_drvdata(dev);
>> +	struct edp_vsc_psr psr_vsc;
>> +
>> +	if (!dp->psr_support)
>> +		return -EINVAL;
>> +
>> +	/* Prepare VSC packet as per EDP 1.4 spec, Table 6.9 */
>> +	memset(&psr_vsc, 0, sizeof(psr_vsc));
>> +	psr_vsc.sdp_header.HB0 = 0;
>> +	psr_vsc.sdp_header.HB1 = 0x7;
>> +	psr_vsc.sdp_header.HB2 = 0x2;
>> +	psr_vsc.sdp_header.HB3 = 0x8;
>> +
>> +	psr_vsc.DB0 = 0;
>> +	psr_vsc.DB1 = EDP_VSC_PSR_STATE_ACTIVE | EDP_VSC_PSR_CRC_VALUES_VALID;
>> +
>> +	analogix_dp_send_psr_spd(dp, &psr_vsc);
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(analogix_dp_enable_psr);
>> +
>> +int analogix_dp_disable_psr(struct device *dev)
>> +{
>> +	struct analogix_dp_device *dp = dev_get_drvdata(dev);
>> +	struct edp_vsc_psr psr_vsc;
>> +
>> +	if (!dp->psr_support)
>> +		return -EINVAL;
>> +
>> +	/* Prepare VSC packet as per EDP 1.4 spec, Table 6.9 */
>> +	memset(&psr_vsc, 0, sizeof(psr_vsc));
>> +	psr_vsc.sdp_header.HB0 = 0;
>> +	psr_vsc.sdp_header.HB1 = 0x7;
>> +	psr_vsc.sdp_header.HB2 = 0x2;
>> +	psr_vsc.sdp_header.HB3 = 0x8;
>> +
>> +	psr_vsc.DB0 = 0;
>> +	psr_vsc.DB1 = 0;
>> +
>> +	analogix_dp_send_psr_spd(dp, &psr_vsc);
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(analogix_dp_disable_psr);
>> +
>> +static bool analogix_dp_detect_sink_psr(struct analogix_dp_device *dp)
>> +{
>> +	unsigned char psr_version;
>> +
>> +	analogix_dp_read_byte_from_dpcd(dp, DP_PSR_SUPPORT, &psr_version);
>> +	dev_dbg(dp->dev, "Panel PSR version : %x\n", psr_version);
>> +
>> +	return (psr_version & DP_PSR_IS_SUPPORTED) ? true : false;
>> +}
>> +
>> +static void analogix_dp_enable_sink_psr(struct analogix_dp_device *dp)
>> +{
>> +	unsigned char psr_en;
>> +
>> +	/* Disable psr function */
>> +	analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
>> +	psr_en &= ~DP_PSR_ENABLE;
>> +	analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
>> +
>> +	/* Main-Link transmitter remains active during PSR active states */
>> +	psr_en = DP_PSR_MAIN_LINK_ACTIVE | DP_PSR_CRC_VERIFICATION;
>> +	analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
>> +
>> +	/* Enable psr function */
>> +	psr_en = DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE |
>> +		 DP_PSR_CRC_VERIFICATION;
>> +	analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
>> +
>> +	analogix_dp_enable_psr_crc(dp);
>> +}
>> +
>>   static unsigned char analogix_dp_calc_edid_check_sum(unsigned char *edid_data)
>>   {
>>   	int i;
>> @@ -921,6 +998,10 @@ static void analogix_dp_commit(struct analogix_dp_device *dp)
>>   
>>   	/* Enable video */
>>   	analogix_dp_start_video(dp);
>> +
>> +	dp->psr_support = analogix_dp_detect_sink_psr(dp);
>> +	if (dp->psr_support)
>> +		analogix_dp_enable_sink_psr(dp);
>>   }
>>   
>>   int analogix_dp_get_modes(struct drm_connector *connector)
>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
>> index b456380..17fbcee 100644
>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
>> @@ -177,6 +177,7 @@ struct analogix_dp_device {
>>   	int			hpd_gpio;
>>   	bool                    force_hpd;
>>   	unsigned char           edid[EDID_BLOCK_LENGTH * 2];
>> +	bool			psr_support;
>>   
>>   	struct analogix_dp_plat_data *plat_data;
>>   };
>> @@ -278,4 +279,8 @@ int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp);
>>   void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp);
>>   void analogix_dp_enable_scrambling(struct analogix_dp_device *dp);
>>   void analogix_dp_disable_scrambling(struct analogix_dp_device *dp);
>> +void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp);
>> +void analogix_dp_send_psr_spd(struct analogix_dp_device *dp,
>> +			      struct edp_vsc_psr *vsc);
>> +
>>   #endif /* _ANALOGIX_DP_CORE_H */
>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
>> index 48030f0..52c1b6b 100644
>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
>> @@ -1322,3 +1322,54 @@ void analogix_dp_disable_scrambling(struct analogix_dp_device *dp)
>>   	reg |= SCRAMBLING_DISABLE;
>>   	writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
>>   }
>> +
>> +void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp)
>> +{
>> +	writel(PSR_VID_CRC_ENABLE, dp->reg_base + ANALOGIX_DP_CRC_CON);
>> +}
>> +
>> +void analogix_dp_send_psr_spd(struct analogix_dp_device *dp,
>> +			      struct edp_vsc_psr *vsc)
>> +{
>> +	unsigned int val;
>> +
>> +	/* don't send info frame */
>> +	val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>> +	val &= ~IF_EN;
>> +	writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>> +
>> +	/* configure single frame update mode */
>> +	writel(PSR_FRAME_UP_TYPE_BURST | PSR_CRC_SEL_HARDWARE,
>> +	       dp->reg_base + ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL);
>> +
>> +	/* configure VSC HB0~HB3 */
>> +	writel(vsc->sdp_header.HB0, dp->reg_base + ANALOGIX_DP_SPD_HB0);
>> +	writel(vsc->sdp_header.HB1, dp->reg_base + ANALOGIX_DP_SPD_HB1);
>> +	writel(vsc->sdp_header.HB2, dp->reg_base + ANALOGIX_DP_SPD_HB2);
>> +	writel(vsc->sdp_header.HB3, dp->reg_base + ANALOGIX_DP_SPD_HB3);
>> +
>> +	/* configure reused VSC PB0~PB3, magic number from vendor */
>> +	writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_PB0);
>> +	writel(0x16, dp->reg_base + ANALOGIX_DP_SPD_PB1);
>> +	writel(0xCE, dp->reg_base + ANALOGIX_DP_SPD_PB2);
>> +	writel(0x5D, dp->reg_base + ANALOGIX_DP_SPD_PB3);
>> +
>> +	/* configure DB0 / DB1 values */
>> +	writel(vsc->DB0, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB0);
>> +	writel(vsc->DB1, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB1);
>> +
>> +	/* set reuse spd inforframe */
>> +	val = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
>> +	val |= REUSE_SPD_EN;
>> +	writel(val, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
>> +
>> +	/* mark info frame update */
>> +	val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>> +	val = (val | IF_UP) & ~IF_EN;
>> +	writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>> +
>> +	/* send info frame */
>> +	val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>> +	val |= IF_EN;
>> +	writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
>> +}
>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
>> index cdcc6c5..40200c6 100644
>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
>> @@ -22,6 +22,8 @@
>>   #define ANALOGIX_DP_VIDEO_CTL_8			0x3C
>>   #define ANALOGIX_DP_VIDEO_CTL_10		0x44
>>   
>> +#define ANALOGIX_DP_SPDIF_AUDIO_CTL_0		0xD8
>> +
>>   #define ANALOGIX_DP_PLL_REG_1			0xfc
>>   #define ANALOGIX_DP_PLL_REG_2			0x9e4
>>   #define ANALOGIX_DP_PLL_REG_3			0x9e8
>> @@ -30,6 +32,21 @@
>>   
>>   #define ANALOGIX_DP_PD				0x12c
>>   
>> +#define ANALOGIX_DP_IF_TYPE			0x244
>> +#define ANALOGIX_DP_IF_PKT_DB1			0x254
>> +#define ANALOGIX_DP_IF_PKT_DB2			0x258
>> +#define ANALOGIX_DP_SPD_HB0			0x2F8
>> +#define ANALOGIX_DP_SPD_HB1			0x2FC
>> +#define ANALOGIX_DP_SPD_HB2			0x300
>> +#define ANALOGIX_DP_SPD_HB3			0x304
>> +#define ANALOGIX_DP_SPD_PB0			0x308
>> +#define ANALOGIX_DP_SPD_PB1			0x30C
>> +#define ANALOGIX_DP_SPD_PB2			0x310
>> +#define ANALOGIX_DP_SPD_PB3			0x314
>> +#define ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL	0x318
>> +#define ANALOGIX_DP_VSC_SHADOW_DB0		0x31C
>> +#define ANALOGIX_DP_VSC_SHADOW_DB1		0x320
>> +
>>   #define ANALOGIX_DP_LANE_MAP			0x35C
>>   
>>   #define ANALOGIX_DP_ANALOG_CTL_1		0x370
>> @@ -103,6 +120,8 @@
>>   
>>   #define ANALOGIX_DP_SOC_GENERAL_CTL		0x800
>>   
>> +#define ANALOGIX_DP_CRC_CON			0x890
>> +
>>   /* ANALOGIX_DP_TX_SW_RESET */
>>   #define RESET_DP_TX				(0x1 << 0)
>>   
>> @@ -151,6 +170,7 @@
>>   #define VID_CHK_UPDATE_TYPE_SHIFT		(4)
>>   #define VID_CHK_UPDATE_TYPE_1			(0x1 << 4)
>>   #define VID_CHK_UPDATE_TYPE_0			(0x0 << 4)
>> +#define REUSE_SPD_EN				(0x1 << 3)
>>   
>>   /* ANALOGIX_DP_VIDEO_CTL_8 */
>>   #define VID_HRES_TH(x)				(((x) & 0xf) << 4)
>> @@ -167,6 +187,12 @@
>>   #define REF_CLK_27M				(0x0 << 0)
>>   #define REF_CLK_MASK				(0x1 << 0)
>>   
>> +/* ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL */
>> +#define PSR_FRAME_UP_TYPE_BURST			(0x1 << 0)
>> +#define PSR_FRAME_UP_TYPE_SINGLE		(0x0 << 0)
>> +#define PSR_CRC_SEL_HARDWARE			(0x1 << 1)
>> +#define PSR_CRC_SEL_MANUALLY			(0x0 << 1)
>> +
>>   /* ANALOGIX_DP_LANE_MAP */
>>   #define LANE3_MAP_LOGIC_LANE_0			(0x0 << 6)
>>   #define LANE3_MAP_LOGIC_LANE_1			(0x1 << 6)
>> @@ -376,4 +402,12 @@
>>   #define VIDEO_MODE_SLAVE_MODE			(0x1 << 0)
>>   #define VIDEO_MODE_MASTER_MODE			(0x0 << 0)
>>   
>> +/* ANALOGIX_DP_PKT_SEND_CTL */
>> +#define IF_UP					(0x1 << 4)
>> +#define IF_EN					(0x1 << 0)
>> +
>> +/* ANALOGIX_DP_CRC_CON */
>> +#define PSR_VID_CRC_FLUSH			(0x1 << 2)
>> +#define PSR_VID_CRC_ENABLE			(0x1 << 0)
>> +
>>   #endif /* _ANALOGIX_DP_REG_H */
>> diff --git a/include/drm/bridge/analogix_dp.h b/include/drm/bridge/analogix_dp.h
>> index 261b86d..9cd8838 100644
>> --- a/include/drm/bridge/analogix_dp.h
>> +++ b/include/drm/bridge/analogix_dp.h
>> @@ -38,6 +38,9 @@ struct analogix_dp_plat_data {
>>   			 struct drm_connector *);
>>   };
>>   
>> +int analogix_dp_enable_psr(struct device *dev);
>> +int analogix_dp_disable_psr(struct device *dev);
>> +
>>   int analogix_dp_resume(struct device *dev);
>>   int analogix_dp_suspend(struct device *dev);
>>   
>> -- 
>> 1.9.1
>>
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel at lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>
>




From robh at kernel.org  Sat Jul 16 06:47:18 2016
From: robh at kernel.org (Rob Herring)
Date: Sat, 16 Jul 2016 08:47:18 -0500
Subject: [PATCH v1 4/6] drm/rockchip: dw_hdmi: add RK3399 HDMI support
In-Reply-To: <1468235146-29575-1-git-send-email-ykk@rock-chips.com>
References: <1468235079-29152-1-git-send-email-ykk@rock-chips.com>
 <1468235146-29575-1-git-send-email-ykk@rock-chips.com>
Message-ID: <20160716134718.GA3267@rob-hp-laptop>

On Mon, Jul 11, 2016 at 07:05:46PM +0800, Yakir Yang wrote:
> RK3399 and RK3288 shared the same HDMI IP controller, only some light
> difference with GRF configure.
> 
> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
> ---
>  .../devicetree/bindings/display/bridge/dw_hdmi.txt |  1 +
>  .../bindings/display/rockchip/dw_hdmi-rockchip.txt |  3 +-

Acked-by: Rob Herring <robh at kernel.org>

>  drivers/gpu/drm/bridge/dw-hdmi.c                   |  2 +-
>  drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c        | 45 ++++++++++++++++++----
>  include/drm/bridge/dw_hdmi.h                       |  6 +++
>  5 files changed, 48 insertions(+), 9 deletions(-)


From robh at kernel.org  Sat Jul 16 15:51:08 2016
From: robh at kernel.org (Rob Herring)
Date: Sat, 16 Jul 2016 17:51:08 -0500
Subject: [PATCH v7 2/5] usb: dwc3: add dis_u2_freeclk_exists_quirk
In-Reply-To: <1468486762-21960-3-git-send-email-william.wu@rock-chips.com>
References: <1468486762-21960-1-git-send-email-william.wu@rock-chips.com>
 <1468486762-21960-3-git-send-email-william.wu@rock-chips.com>
Message-ID: <20160716225107.GA27074@rob-hp-laptop>

On Thu, Jul 14, 2016 at 04:59:19PM +0800, William Wu wrote:
> Add a quirk to clear the GUSB2PHYCFG.U2_FREECLK_EXISTS bit,
> which specifies whether the USB2.0 PHY provides a free-running
> PHY clock, which is active when the clock control input is active.
> 
> Signed-off-by: William Wu <william.wu at rock-chips.com>
> ---
> Changes in v7:
> - None
> 
> Changes in v6:
> - use '-' instead of '_' in dts (Rob Herring)
> 
> Changes in v5:
> - None
> 
> Changes in v4:
> - rebase on top of balbi testing/next, remove pdata (balbi)
> 
> Changes in v3:
> - None
> 
> Changes in v2:
> - None
> 
>  Documentation/devicetree/bindings/usb/dwc3.txt | 3 +++
>  drivers/usb/dwc3/core.c                        | 5 +++++
>  drivers/usb/dwc3/core.h                        | 5 +++++
>  3 files changed, 13 insertions(+)

Acked-by: Rob Herring <robh at kernel.org>


From robh at kernel.org  Sat Jul 16 15:57:15 2016
From: robh at kernel.org (Rob Herring)
Date: Sat, 16 Jul 2016 17:57:15 -0500
Subject: [PATCH v7 3/5] usb: dwc3: make usb2 phy utmi interface
 configurable in DT
In-Reply-To: <1468486762-21960-4-git-send-email-william.wu@rock-chips.com>
References: <1468486762-21960-1-git-send-email-william.wu@rock-chips.com>
 <1468486762-21960-4-git-send-email-william.wu@rock-chips.com>
Message-ID: <20160716225715.GA27724@rob-hp-laptop>

On Thu, Jul 14, 2016 at 04:59:20PM +0800, William Wu wrote:
> Add snps,phyif-utmi-width devicetree property to configure
> the UTMI+ PHY with an 8- or 16-bit interface. UTMI+ PHY
> interface is a hardware property, and it's platform dependent.
> Normally,the PHYIF can be configured during coreconsultant.
           ^
space

> But for some specific USB cores(e.g. rk3399 SoC DWC3), the
> default PHYIF configuration value is fault, so we need to
> reconfigure it by software.
> 
> And refer to the DWC3 databook, the GUSB2PHYCFG.USBTRDTIM
> must be set to the corresponding value according to the
> UTMI+ PHY interface.
> 
> Signed-off-by: William Wu <william.wu at rock-chips.com>
> ---
> Changes in v7:
> - remove quirk and use only one property to configure utmi (Heiko, Rob Herring)
> 
> Changes in v6:
> - use '-' instead of '_' in dts (Rob Herring)
> 
> Changes in v5:
> - None
> 
> Changes in v4:
> - rebase on top of balbi testing/next, remove pdata (balbi)
> 
> Changes in v3:
> - None
> 
> Changes in v2:
> - add a quirk for phyif_utmi (balbi)
> 
>  Documentation/devicetree/bindings/usb/dwc3.txt |  3 +++
>  drivers/usb/dwc3/core.c                        | 25 +++++++++++++++++++++++++
>  drivers/usb/dwc3/core.h                        | 10 ++++++++++
>  3 files changed, 38 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt
> index 020b0e9..00cc541 100644
> --- a/Documentation/devicetree/bindings/usb/dwc3.txt
> +++ b/Documentation/devicetree/bindings/usb/dwc3.txt
> @@ -47,6 +47,9 @@ Optional properties:
>   - snps,hird-threshold: HIRD threshold
>   - snps,hsphy_interface: High-Speed PHY interface selection between "utmi" for
>     UTMI+ and "ulpi" for ULPI when the DWC_USB3_HSPHY_INTERFACE has value 3.
> + - snps,phyif-utmi-width: the value to configure the core to support a UTMI+ PHY
> +			with an 8- or 16-bit interface. Value 8 select 8-bit
> +			interface, value 16 select 16-bit interface.

Is 'phy_type = "utmi_wide"' not the same as 16-bit width?

Again, I think this should be common.

Rob


From robh at kernel.org  Sat Jul 16 15:58:16 2016
From: robh at kernel.org (Rob Herring)
Date: Sat, 16 Jul 2016 17:58:16 -0500
Subject: [PATCH v7 4/5] usb: dwc3: add dis_del_phy_power_chg_quirk
In-Reply-To: <1468486762-21960-5-git-send-email-william.wu@rock-chips.com>
References: <1468486762-21960-1-git-send-email-william.wu@rock-chips.com>
 <1468486762-21960-5-git-send-email-william.wu@rock-chips.com>
Message-ID: <20160716225816.GA31226@rob-hp-laptop>

On Thu, Jul 14, 2016 at 04:59:21PM +0800, William Wu wrote:
> Add a quirk to clear the GUSB3PIPECTL.DELAYP1TRANS bit,
> which specifies whether disable delay PHY power change
> from P0 to P1/P2/P3 when link state changing from U0
> to U1/U2/U3 respectively.
> 
> Signed-off-by: William Wu <william.wu at rock-chips.com>
> ---
> Changes in v7:
> - None
> 
> Changes in v6:
> - use '-' instead of '_' in dts (Rob Herring)
> 
> Changes in v5:
> - None
> 
> Changes in v4:
> - rebase on top of balbi testing/next, remove pdata (balbi)
> 
> Changes in v3:
> - None
> 
> Changes in v2:
> - None
> 
>  Documentation/devicetree/bindings/usb/dwc3.txt | 2 ++
>  drivers/usb/dwc3/core.c                        | 5 +++++
>  drivers/usb/dwc3/core.h                        | 3 +++
>  3 files changed, 10 insertions(+)

Acked-by: Rob Herring <robh at kernel.org>


From heiko at sntech.de  Sun Jul 17 03:28:22 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Sun, 17 Jul 2016 12:28:22 +0200
Subject: [PATCH v7 3/5] usb: dwc3: make usb2 phy utmi interface
 configurable in DT
In-Reply-To: <20160716225715.GA27724@rob-hp-laptop>
References: <1468486762-21960-1-git-send-email-william.wu@rock-chips.com>
 <1468486762-21960-4-git-send-email-william.wu@rock-chips.com>
 <20160716225715.GA27724@rob-hp-laptop>
Message-ID: <1738306.cydkc9Pb90@diego>

Am Samstag, 16. Juli 2016, 17:57:15 schrieb Rob Herring:
> On Thu, Jul 14, 2016 at 04:59:20PM +0800, William Wu wrote:
> > Add snps,phyif-utmi-width devicetree property to configure
> > the UTMI+ PHY with an 8- or 16-bit interface. UTMI+ PHY
> > interface is a hardware property, and it's platform dependent.
> > Normally,the PHYIF can be configured during coreconsultant.
> 
>            ^
> space
> 
> > But for some specific USB cores(e.g. rk3399 SoC DWC3), the
> > default PHYIF configuration value is fault, so we need to
> > reconfigure it by software.
> > 
> > And refer to the DWC3 databook, the GUSB2PHYCFG.USBTRDTIM
> > must be set to the corresponding value according to the
> > UTMI+ PHY interface.
> > 
> > Signed-off-by: William Wu <william.wu at rock-chips.com>
> > ---
> > Changes in v7:
> > - remove quirk and use only one property to configure utmi (Heiko, Rob
> > Herring)
> > 
> > Changes in v6:
> > - use '-' instead of '_' in dts (Rob Herring)
> > 
> > Changes in v5:
> > - None
> > 
> > Changes in v4:
> > - rebase on top of balbi testing/next, remove pdata (balbi)
> > 
> > Changes in v3:
> > - None
> > 
> > Changes in v2:
> > - add a quirk for phyif_utmi (balbi)
> > 
> >  Documentation/devicetree/bindings/usb/dwc3.txt |  3 +++
> >  drivers/usb/dwc3/core.c                        | 25
> >  +++++++++++++++++++++++++ drivers/usb/dwc3/core.h                       
> >  | 10 ++++++++++
> >  3 files changed, 38 insertions(+)
> > 
> > diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt
> > b/Documentation/devicetree/bindings/usb/dwc3.txt index 020b0e9..00cc541
> > 100644
> > --- a/Documentation/devicetree/bindings/usb/dwc3.txt
> > +++ b/Documentation/devicetree/bindings/usb/dwc3.txt
> > 
> > @@ -47,6 +47,9 @@ Optional properties:
> >   - snps,hird-threshold: HIRD threshold
> >   - snps,hsphy_interface: High-Speed PHY interface selection between
> >   "utmi" for>   
> >     UTMI+ and "ulpi" for ULPI when the DWC_USB3_HSPHY_INTERFACE has value
> >     3.
> > 
> > + - snps,phyif-utmi-width: the value to configure the core to support a
> > UTMI+ PHY +			with an 8- or 16-bit interface. Value 8 select 8-bit
> > +			interface, value 16 select 16-bit interface.
> 
> Is 'phy_type = "utmi_wide"' not the same as 16-bit width?
> 
> Again, I think this should be common.

after knowing that I need to look for that "utmi_wide", I think I'd agree.

I found mention of that in usb/ci-hdrc-usb2.txt and usb/fsl-usb.txt and from
the coresponding code, I can see that they really mean the 16bit interface,
the Rockchip TRM as well as the spec [0] seems to call it UTMI+ but really
looks the same as utmi_wide.

Interestingly, there is already generic code in drivers/usb/phy/of.c so that 
property should probably move to devicetree/bindings/usb/generic.txt
as well.


Heiko

[0] http://cache.nxp.com/files/corporate/doc/support_info/UTMI-PLUS-SPECIFICATION.pdf



From ayaka at soulik.info  Sun Jul 17 08:38:56 2016
From: ayaka at soulik.info (Randy Li)
Date: Sun, 17 Jul 2016 23:38:56 +0800
Subject: [PATCH] ARM: dts: the dts support for rk3288 firefly reload
In-Reply-To: <5614243.tqTyZoc945@phil>
References: <5614243.tqTyZoc945@phil>
Message-ID: <1468769937-23387-1-git-send-email-ayaka@soulik.info>

This version is split into a reload core dtsi for the SoC module
part and a baseboard dts for the reload baseboard. Thanks to
the Heiko's advise.

Randy Li (1):
  ARM: dts: add rk3288-firefly-reload

 Documentation/devicetree/bindings/arm/rockchip.txt |   4 +
 arch/arm/boot/dts/Makefile                         |   1 +
 arch/arm/boot/dts/rk3288-firefly-reload-core.dtsi  | 304 ++++++++++++++++++++
 arch/arm/boot/dts/rk3288-firefly-reload.dts        | 320 +++++++++++++++++++++
 4 files changed, 629 insertions(+)
 create mode 100644 arch/arm/boot/dts/rk3288-firefly-reload-core.dtsi
 create mode 100644 arch/arm/boot/dts/rk3288-firefly-reload.dts

-- 
2.7.4



From ayaka at soulik.info  Sun Jul 17 08:38:57 2016
From: ayaka at soulik.info (Randy Li)
Date: Sun, 17 Jul 2016 23:38:57 +0800
Subject: [PATCH] ARM: dts: add rk3288-firefly-reload
In-Reply-To: <1468769937-23387-1-git-send-email-ayaka@soulik.info>
References: <5614243.tqTyZoc945@phil>
 <1468769937-23387-1-git-send-email-ayaka@soulik.info>
Message-ID: <1468769937-23387-2-git-send-email-ayaka@soulik.info>

The Firefly RK3288 Reload is a combination Firefly rk3288 core board
with the Reload baseboard. Add a dtsi for the Firefly rk3288 core
which can be included into the dts for the various baseboards
in the future and dts for Reload base board.

Currently supported are serial console, wired networking, eMMC and
SD storage, SPFIF, IR receiver, LEDs, SDIO wifi and USB. But only
the OTG could work on the host mode now, the other USB host can't
work now, additional patches are required.

Signed-off-by: Randy Li <ayaka at soulik.info>
---
 Documentation/devicetree/bindings/arm/rockchip.txt |   4 +
 arch/arm/boot/dts/Makefile                         |   1 +
 arch/arm/boot/dts/rk3288-firefly-reload-core.dtsi  | 304 ++++++++++++++++++++
 arch/arm/boot/dts/rk3288-firefly-reload.dts        | 320 +++++++++++++++++++++
 4 files changed, 629 insertions(+)
 create mode 100644 arch/arm/boot/dts/rk3288-firefly-reload-core.dtsi
 create mode 100644 arch/arm/boot/dts/rk3288-firefly-reload.dts

diff --git a/Documentation/devicetree/bindings/arm/rockchip.txt b/Documentation/devicetree/bindings/arm/rockchip.txt
index 715d960..3380212c 100644
--- a/Documentation/devicetree/bindings/arm/rockchip.txt
+++ b/Documentation/devicetree/bindings/arm/rockchip.txt
@@ -31,6 +31,10 @@ Rockchip platforms device tree bindings
     or
       - compatible = "firefly,firefly-rk3288-beta", "rockchip,rk3288";
 
+- Firefly Firefly-RK3288 Reload board:
+    Required root node properties:
+      - compatible = "firefly,firefly-rk3288-reload", "rockchip,rk3288";
+
 - ChipSPARK PopMetal-RK3288 board:
     Required root node properties:
       - compatible = "chipspark,popmetal-rk3288", "rockchip,rk3288";
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index 414b427..8173e77 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -606,6 +606,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += \
 	rk3288-evb-rk808.dtb \
 	rk3288-firefly-beta.dtb \
 	rk3288-firefly.dtb \
+	rk3288-firefly-reload.dtb \
 	rk3288-miqi.dtb \
 	rk3288-popmetal.dtb \
 	rk3288-r89.dtb \
diff --git a/arch/arm/boot/dts/rk3288-firefly-reload-core.dtsi b/arch/arm/boot/dts/rk3288-firefly-reload-core.dtsi
new file mode 100644
index 0000000..039ae16
--- /dev/null
+++ b/arch/arm/boot/dts/rk3288-firefly-reload-core.dtsi
@@ -0,0 +1,304 @@
+/*
+ * Device tree file for Firefly Rockchip RK3288 Core board
+ * Copyright (c) 2016 Randy Li <ayaka at soulik.info>
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <dt-bindings/input/input.h>
+#include "rk3288.dtsi"
+
+/ {
+	memory {
+		device_type = "memory";
+		reg = <0 0x80000000>;
+	};
+
+	dovdd_1v8: dovdd-1v8-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "dovdd_1v8";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+		vin-supply = <&vcc28_dvp>;
+	};
+
+	ext_gmac: external-gmac-clock {
+		compatible = "fixed-clock";
+		#clock-cells = <0>;
+		clock-frequency = <125000000>;
+		clock-output-names = "ext_gmac";
+	};
+
+	io_domains: io-domains {
+		compatible = "rockchip,rk3288-io-voltage-domain";
+		rockchip,grf = <&grf>;
+
+		audio-supply = <&vcca_33>;
+		bb-supply = <&vcc_io>;
+		dvp-supply = <&dovdd_1v8>;
+		flash0-supply = <&vcc_flash>;
+		flash1-supply = <&vcc_lan>;
+		gpio30-supply = <&vcc_io>;
+		gpio1830-supply = <&vcc_io>;
+		lcdc-supply = <&vcc_io>;
+		sdcard-supply = <&vccio_sd>;
+		wifi-supply = <&vccio_wl>;
+	};
+
+	vbat_wl: vcc_sys: vsys-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_sys";
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+		regulator-always-on;
+		regulator-boot-on;
+	};
+
+	vcc_flash: flash-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_flash";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+		vin-supply = <&vcc_io>;
+	};
+
+	vcc_5v: usb-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_5v";
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+		regulator-always-on;
+		regulator-boot-on;
+		vin-supply = <&vcc_sys>;
+	};
+
+};
+
+&cpu0 {
+	cpu0-supply = <&vdd_cpu>;
+};
+
+&emmc {
+	bus-width = <8>;
+	cap-mmc-highspeed;
+	mmc-ddr-1_8v;
+	mmc-hs200-1_8v;
+	disable-wp;
+	non-removable;
+	num-slots = <1>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&emmc_clk>, <&emmc_cmd>, <&emmc_pwr>, <&emmc_bus8>;
+	vmmc-supply = <&vcc_io>;
+	vqmmc-supply = <&vcc_flash>;
+	status = "okay";
+};
+
+&gmac {
+	assigned-clocks = <&cru SCLK_MAC>;
+	assigned-clock-parents = <&ext_gmac>;
+	clock_in_out = "input";
+	pinctrl-names = "default";
+	pinctrl-0 = <&rgmii_pins>, <&phy_rst>, <&phy_pmeb>, <&phy_int>;
+	phy-supply = <&vcc_lan>;
+	phy-mode = "rgmii";
+	snps,reset-active-low;
+	snps,reset-delays-us = <0 10000 1000000>;
+	snps,reset-gpio = <&gpio4 8 GPIO_ACTIVE_LOW>;
+	tx_delay = <0x30>;
+	rx_delay = <0x10>;
+	status = "ok";
+};
+
+&i2c0 {
+	clock-frequency = <400000>;
+	status = "okay";
+
+	vdd_cpu: syr827 at 40 {
+		compatible = "silergy,syr827";
+		fcs,suspend-voltage-selector = <1>;
+		reg = <0x40>;
+		regulator-name = "vdd_cpu";
+		regulator-min-microvolt = <850000>;
+		regulator-max-microvolt = <1350000>;
+		regulator-always-on;
+		regulator-boot-on;
+		regulator-enable-ramp-delay = <300>;
+		regulator-ramp-delay = <8000>;
+		vin-supply = <&vcc_sys>;
+	};
+
+	vdd_gpu: syr828 at 41 {
+		compatible = "silergy,syr828";
+		fcs,suspend-voltage-selector = <1>;
+		reg = <0x41>;
+		regulator-name = "vdd_gpu";
+		regulator-min-microvolt = <850000>;
+		regulator-max-microvolt = <1350000>;
+		regulator-always-on;
+		vin-supply = <&vcc_sys>;
+	};
+
+	act8846: act8846 at 5a {
+		compatible = "active-semi,act8846";
+		reg = <0x5a>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pmic_vsel>, <&pwr_hold>;
+		system-power-controller;
+
+		vp1-supply = <&vcc_sys>;
+		vp2-supply = <&vcc_sys>;
+		vp3-supply = <&vcc_sys>;
+		vp4-supply = <&vcc_sys>;
+		inl1-supply = <&vcc_sys>;
+		inl2-supply = <&vcc_sys>;
+		inl3-supply = <&vcc_20>;
+
+		regulators {
+			vcc_ddr: REG1 {
+				regulator-name = "vcc_ddr";
+				regulator-min-microvolt = <1200000>;
+				regulator-max-microvolt = <1200000>;
+				regulator-always-on;
+			};
+
+			vcc_io: REG2 {
+				regulator-name = "vcc_io";
+				regulator-min-microvolt = <3300000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-always-on;
+			};
+
+			vdd_log: REG3 {
+				regulator-name = "vdd_log";
+				regulator-min-microvolt = <1100000>;
+				regulator-max-microvolt = <1100000>;
+				regulator-always-on;
+			};
+
+			vcc_20: REG4 {
+				regulator-name = "vcc_20";
+				regulator-min-microvolt = <2000000>;
+				regulator-max-microvolt = <2000000>;
+				regulator-always-on;
+			};
+
+			vccio_sd: REG5 {
+				regulator-name = "vccio_sd";
+				regulator-min-microvolt = <3300000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-always-on;
+			};
+
+			vdd10_lcd: REG6 {
+				regulator-name = "vdd10_lcd";
+				regulator-min-microvolt = <1000000>;
+				regulator-max-microvolt = <1000000>;
+				regulator-always-on;
+			};
+
+			vcca_33: REG7  {
+				regulator-name = "vcca_33";
+				regulator-min-microvolt = <3300000>;
+				regulator-max-microvolt = <3300000>;
+			};
+
+			vcc_lan: REG8 {
+				regulator-name = "vcca_lan";
+				regulator-min-microvolt = <3300000>;
+				regulator-max-microvolt = <3300000>;
+			};
+
+			vccio_pmu: REG9 {
+				regulator-name = "vccio_pmu";
+				regulator-min-microvolt = <3300000>;
+				regulator-max-microvolt = <3300000>;
+			};
+
+			vdd_10: REG10 {
+				regulator-name = "vdd_10";
+				regulator-min-microvolt = <1000000>;
+				regulator-max-microvolt = <1000000>;
+				regulator-always-on;
+			};
+
+			vccio_wl: vcc_18: REG11 {
+				regulator-name = "vcc_18";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-always-on;
+			};
+
+			vcc18_lcd: REG12 {
+				regulator-name = "vcc18_lcd";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-always-on;
+			};
+		};
+	};
+};
+
+&pinctrl {
+	pcfg_output_high: pcfg-output-high {
+		output-high;
+	};
+
+	pcfg_output_low: pcfg-output-low {
+		output-low;
+	};
+
+	pcfg_pull_up_drv_12ma: pcfg-pull-up-drv-12ma {
+		bias-pull-up;
+		drive-strength = <12>;
+	};
+
+	act8846 {
+		pwr_hold: pwr-hold {
+			rockchip,pins = <0 1 RK_FUNC_GPIO &pcfg_output_high>;
+		};
+
+		pmic_vsel: pmic-vsel {
+			rockchip,pins = <7 14 RK_FUNC_GPIO &pcfg_output_low>;
+		};
+	};
+
+	gmac {
+		phy_int: phy-int {
+			rockchip,pins = <0 9 RK_FUNC_GPIO &pcfg_pull_up>;
+		};
+
+		phy_pmeb: phy-pmeb {
+			rockchip,pins = <0 8 RK_FUNC_GPIO &pcfg_pull_up>;
+		};
+
+		phy_rst: phy-rst {
+			rockchip,pins = <4 8 RK_FUNC_GPIO &pcfg_output_high>;
+		};
+	};
+};
+
+&tsadc {
+	rockchip,hw-tshut-mode = <0>;
+	rockchip,hw-tshut-polarity = <0>;
+	status = "okay";
+};
+
+&vopb {
+	status = "okay";
+};
+
+&vopb_mmu {
+	status = "okay";
+};
+
+&vopl {
+	status = "okay";
+};
+
+&vopl_mmu {
+	status = "okay";
+};
+
+&wdt {
+	status = "okay";
+};
diff --git a/arch/arm/boot/dts/rk3288-firefly-reload.dts b/arch/arm/boot/dts/rk3288-firefly-reload.dts
new file mode 100644
index 0000000..8adda0a
--- /dev/null
+++ b/arch/arm/boot/dts/rk3288-firefly-reload.dts
@@ -0,0 +1,320 @@
+/*
+ * Device tree file for Firefly Rockchip RK3288 Reload base board
+ * Copyright (c) 2016 Randy Li <ayaka at soulik.info>
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+/dts-v1/;
+#include "rk3288-firefly-reload-core.dtsi"
+
+/ {
+	model = "Firefly-RK3288-reload";
+	compatible = "firefly,firefly-rk3288-reload", "rockchip,rk3288";
+
+	leds {
+		compatible = "gpio-leds";
+
+		power {
+			gpios = <&gpio8 2 GPIO_ACTIVE_LOW>;
+			label = "firefly:blue:power";
+			pinctrl-names = "default";
+			pinctrl-0 = <&power_led>;
+			panic-indicator;
+		};
+
+		work {
+			gpios = <&gpio8 1 GPIO_ACTIVE_LOW>;
+			label = "firefly:blue:user";
+			linux,default-trigger = "rc-feedback";
+			pinctrl-names = "default";
+			pinctrl-0 = <&work_led>;
+		};
+	};
+	
+	vcc_sd: sdmmc-regulator {
+		compatible = "regulator-fixed";
+		gpio = <&gpio7 11 GPIO_ACTIVE_LOW>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&sdmmc_pwr>;
+		regulator-name = "vcc_sd";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		startup-delay-us = <100000>;
+		vin-supply = <&vcc_io>;
+	};
+
+	sdio_pwrseq: sdio-pwrseq {
+		compatible = "mmc-pwrseq-simple";
+		clocks = <&hym8563>;
+		clock-names = "ext_clock";
+		pinctrl-names = "default";
+		pinctrl-0 = <&wifi_enable>;
+		reset-gpios = <&gpio4 28 GPIO_ACTIVE_LOW>;
+	};
+
+	vcc_host_5v: usb-host-regulator {
+		compatible = "regulator-fixed";
+		enable-active-high;
+		gpio = <&gpio0 14 GPIO_ACTIVE_HIGH>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&host_vbus_drv>;
+		regulator-name = "vcc_host_5v";
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+		regulator-always-on;
+		vin-supply = <&vcc_5v>;
+	};
+
+	vcc_otg_5v: usb-otg-regulator {
+		compatible = "regulator-fixed";
+		enable-active-high;
+		gpio = <&gpio0 12 GPIO_ACTIVE_HIGH>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&otg_vbus_drv>;
+		regulator-name = "vcc_otg_5v";
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+		regulator-always-on;
+		vin-supply = <&vcc_5v>;
+	};
+
+	/*
+	 * A TT8142 creates both dovdd_1v8 and vcc28_dvp, controlled
+	 * by the dvp_pwr pin.
+	 */
+	vcc28_dvp: vcc28-dvp-regulator {
+		compatible = "regulator-fixed";
+		enable-active-high;
+		gpio = <&gpio0 11 GPIO_ACTIVE_HIGH>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&dvp_pwr>;
+		regulator-name = "vcc28_dvp";
+		regulator-min-microvolt = <2800000>;
+		regulator-max-microvolt = <2800000>;
+		regulator-always-on;
+		vin-supply = <&vcc_io>;
+	};
+
+	ir-receiver {
+		compatible = "gpio-ir-receiver";
+		gpios = <&gpio7 0 GPIO_ACTIVE_LOW>;
+	};
+
+	gpio-keys {
+		compatible = "gpio-keys";
+
+		power {
+			wakeup-source;
+			gpios = <&gpio0 5 GPIO_ACTIVE_LOW>;
+			label = "GPIO Power";
+			linux,code = <KEY_POWER>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&pwr_key>;
+		};
+	};
+
+	sound {
+		compatible = "simple-audio-card";
+		simple-audio-card,name = "SPDIF";
+		simple-audio-card,dai-link at 1 {  /* S/PDIF - S/PDIF */
+			cpu { sound-dai = <&spdif>; };
+			codec { sound-dai = <&spdif_out>; };
+		};
+	};
+
+	spdif_out: spdif-out {
+		compatible = "linux,spdif-dit";
+		#sound-dai-cells = <0>;
+	};
+};
+
+&i2c0 {
+	hym8563: hym8563 at 51 {
+		compatible = "haoyu,hym8563";
+		reg = <0x51>;
+		#clock-cells = <0>;
+		clock-frequency = <32768>;
+		clock-output-names = "xin32k";
+		interrupt-parent = <&gpio7>;
+		interrupts = <4 IRQ_TYPE_EDGE_FALLING>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&rtc_int>;
+	};
+};
+
+&i2c2 {
+	status = "okay";
+
+	codec: es8328 at 10 {
+		compatible = "everest,es8328";
+		DVDD-supply = <&vcca_33>;
+		AVDD-supply = <&vcca_33>;
+		PVDD-supply = <&vcca_33>;
+		HPVDD-supply = <&vcca_33>;
+		clocks = <&cru HCLK_I2S0>, <&cru SCLK_I2S0>;
+		clock-names = "i2s_hclk", "i2s_clk";
+		reg = <0x10>;
+	};
+};
+
+&i2s {
+	status = "okay";
+};
+
+&sdmmc {
+	bus-width = <4>;
+	cap-mmc-highspeed;
+	cap-sd-highspeed;
+	sd-uhs-sdr12;
+	sd-uhs-sdr25;
+	sd-uhs-sdr50;
+	sd-uhs-sdr104;
+	sd-uhs-ddr50;
+	card-detect-delay = <200>;
+	disable-wp;
+	num-slots = <1>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&sdmmc_clk>, <&sdmmc_cmd>, <&sdmmc_cd>, <&sdmmc_bus4>;
+	vmmc-supply = <&vcc_sd>;
+	vqmmc-supply = <&vccio_sd>;
+	status = "okay";
+};
+
+&sdio0 {
+	bus-width = <4>;
+	cap-sd-highspeed;
+	sd-uhs-sdr12;
+	sd-uhs-sdr25;
+	sd-uhs-sdr50;
+	sd-uhs-ddr50;
+	cap-sdio-irq;
+	disable-wp;
+	mmc-pwrseq = <&sdio_pwrseq>;
+	non-removable;
+	num-slots = <1>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&sdio0_bus4>, <&sdio0_cmd>, <&sdio0_clk>, <&sdio0_int>;
+	vmmc-supply = <&vbat_wl>;
+	vqmmc-supply = <&vccio_wl>;
+	status = "okay";
+};
+
+&uart0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart0_xfer>, <&uart0_cts>, <&uart0_rts>;
+	status = "okay";
+};
+
+&uart1 {
+	status = "okay";
+};
+
+&uart2 {
+	status = "okay";
+};
+
+&uart3 {
+	status = "okay";
+};
+
+&usbphy {
+	status = "okay";
+};
+
+&usb_host1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&usbhub_rst>;
+	status = "okay";
+};
+
+&usb_otg {
+	status = "okay";
+};
+
+&spdif {
+	status = "okay";
+};
+
+&pinctrl {
+	ir {
+		ir_int: ir-int {
+			rockchip,pins = <7 0 RK_FUNC_GPIO &pcfg_pull_up>;
+		};
+	};
+
+	dvp {
+		dvp_pwr: dvp-pwr {
+			rockchip,pins = <0 11 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+
+	hym8563 {
+		rtc_int: rtc-int {
+			rockchip,pins = <7 4 RK_FUNC_GPIO &pcfg_pull_up>;
+		};
+	};
+
+	keys {
+		pwr_key: pwr-key {
+			rockchip,pins = <0 5 RK_FUNC_GPIO &pcfg_pull_up>;
+		};
+	};
+
+	leds {
+		power_led: power-led {
+			rockchip,pins = <8 2 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+
+		work_led: work-led {
+			rockchip,pins = <8 1 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+
+	sdmmc {
+		/*
+		 * Default drive strength isn't enough to achieve even
+		 * high-speed mode on firefly board so bump up to 12ma.
+		 */
+		sdmmc_bus4: sdmmc-bus4 {
+			rockchip,pins = <6 16 RK_FUNC_1 &pcfg_pull_up_drv_12ma>,
+					<6 17 RK_FUNC_1 &pcfg_pull_up_drv_12ma>,
+					<6 18 RK_FUNC_1 &pcfg_pull_up_drv_12ma>,
+					<6 19 RK_FUNC_1 &pcfg_pull_up_drv_12ma>;
+		};
+
+		sdmmc_clk: sdmmc-clk {
+			rockchip,pins = <6 20 RK_FUNC_1 &pcfg_pull_none_12ma>;
+		};
+
+		sdmmc_cmd: sdmmc-cmd {
+			rockchip,pins = <6 21 RK_FUNC_1 &pcfg_pull_up_drv_12ma>;
+		};
+
+		sdmmc_pwr: sdmmc-pwr {
+			rockchip,pins = <7 11 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+
+	sdio {
+		wifi_enable: wifi-enable {
+			rockchip,pins = <4 28 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+
+	usb_host {
+		host_vbus_drv: host-vbus-drv {
+			rockchip,pins = <0 14 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+
+		usbhub_rst: usbhub-rst {
+			rockchip,pins = <8 3 RK_FUNC_GPIO &pcfg_output_high>;
+		};
+	};
+
+	usb_otg {
+		otg_vbus_drv: otg-vbus-drv {
+			rockchip,pins = <0 12 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+};
-- 
2.7.4



From robh at kernel.org  Sun Jul 17 13:10:19 2016
From: robh at kernel.org (Rob Herring)
Date: Sun, 17 Jul 2016 15:10:19 -0500
Subject: [PATCH v3] arm64: dts: rockchip: Add basic support for orion-r68
In-Reply-To: <1468577391-5096-1-git-send-email-matthias.bgg@gmail.com>
References: <1468577391-5096-1-git-send-email-matthias.bgg@gmail.com>
Message-ID: <20160717201019.GA30063@rob-hp-laptop>

On Fri, Jul 15, 2016 at 12:09:51PM +0200, Matthias Brugger wrote:
> This patch adds basic support for the Tronsmart orion r86 set-top-box.
> 
> Signed-off-by: Matthias Brugger <mbrugger at suse.com>
> ---
> Changes since v2:
> - gpio-keys cleanpup
> - set sdmmc properties correctly
> 
>  Documentation/devicetree/bindings/arm/rockchip.txt |   4 +
>  arch/arm64/boot/dts/rockchip/Makefile              |   1 +
>  .../boot/dts/rockchip/rk3368-orion-r68-meta.dts    | 385 +++++++++++++++++++++
>  3 files changed, 390 insertions(+)
>  create mode 100644 arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts

Acked-by: Rob Herring <robh at kernel.org>


From robh at kernel.org  Sun Jul 17 14:01:34 2016
From: robh at kernel.org (Rob Herring)
Date: Sun, 17 Jul 2016 16:01:34 -0500
Subject: [PATCH] ARM: dts: add rk3288-firefly-reload
In-Reply-To: <1468769937-23387-2-git-send-email-ayaka@soulik.info>
References: <5614243.tqTyZoc945@phil>
 <1468769937-23387-1-git-send-email-ayaka@soulik.info>
 <1468769937-23387-2-git-send-email-ayaka@soulik.info>
Message-ID: <20160717210134.GA25977@rob-hp-laptop>

On Sun, Jul 17, 2016 at 11:38:57PM +0800, Randy Li wrote:
> The Firefly RK3288 Reload is a combination Firefly rk3288 core board
> with the Reload baseboard. Add a dtsi for the Firefly rk3288 core
> which can be included into the dts for the various baseboards
> in the future and dts for Reload base board.
> 
> Currently supported are serial console, wired networking, eMMC and
> SD storage, SPFIF, IR receiver, LEDs, SDIO wifi and USB. But only
> the OTG could work on the host mode now, the other USB host can't
> work now, additional patches are required.
> 
> Signed-off-by: Randy Li <ayaka at soulik.info>
> ---
>  Documentation/devicetree/bindings/arm/rockchip.txt |   4 +
>  arch/arm/boot/dts/Makefile                         |   1 +
>  arch/arm/boot/dts/rk3288-firefly-reload-core.dtsi  | 304 ++++++++++++++++++++
>  arch/arm/boot/dts/rk3288-firefly-reload.dts        | 320 +++++++++++++++++++++
>  4 files changed, 629 insertions(+)
>  create mode 100644 arch/arm/boot/dts/rk3288-firefly-reload-core.dtsi
>  create mode 100644 arch/arm/boot/dts/rk3288-firefly-reload.dts

Acked-by: Rob Herring <robh at kernel.org>


From shawn.lin at rock-chips.com  Sun Jul 17 17:42:12 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Mon, 18 Jul 2016 08:42:12 +0800
Subject: [PATCH v7 1/2] Documentation: bindings: add dt doc for Rockchip PCIe
 controller
Message-ID: <1468802533-17699-1-git-send-email-shawn.lin@rock-chips.com>

This patch adds a binding that describes the Rockchip PCIe controller
found on Rockchip SoCs PCIe interface.

Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>

Acked-by: Rob Herring <robh at kernel.org>
---

Changes in v7:
- move optional properties ahead of interrupt subnode
- use 0-base index for INTx

Changes in v6:
- add ack tag from Rob

Changes in v5:
- fix wrong example reported by Marc
- add seperate section to describe the interrupt controller child
  node

Changes in v4:
- fix example of adding intermediate interrupt controller for pcie
  legacy interrrupt

Changes in v3:
- fix example dts code suggested by Rob and Marc
- remove driver's behaviour of regulator

Changes in v2:
- fix lots clk/reset stuff suggested by Heiko
- remove msi-parent and add msi-map suggested by Marc
- drop phy related stuff
- some others minor fixes

 .../devicetree/bindings/pci/rockchip-pcie.txt      | 105 +++++++++++++++++++++
 1 file changed, 105 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/rockchip-pcie.txt

diff --git a/Documentation/devicetree/bindings/pci/rockchip-pcie.txt b/Documentation/devicetree/bindings/pci/rockchip-pcie.txt
new file mode 100644
index 0000000..8ab0ddb
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/rockchip-pcie.txt
@@ -0,0 +1,105 @@
+* Rockchip AXI PCIe Root Port Bridge DT description
+
+Required properties:
+- #address-cells: Address representation for root ports, set to <3>
+- #size-cells: Size representation for root ports, set to <2>
+- #interrupt-cells: specifies the number of cells needed to encode an
+		interrupt source. The value must be 1.
+- compatible: Should contain "rockchip,rk3399-pcie"
+- reg: Two register ranges as listed in the reg-names property
+- reg-names: Must include the following names
+	- "axi-base"
+	- "apb-base"
+- clocks: Must contain an entry for each entry in clock-names.
+		See ../clocks/clock-bindings.txt for details.
+- clock-names: Must include the following entries:
+	- "aclk"
+	- "aclk-perf"
+	- "hclk"
+	- "pm"
+- msi-map: Maps a Requester ID to an MSI controller and associated.
+		See ./pci-msi.txt
+- phys: From PHY bindings: Phandle for the Generic PHY for PCIe.
+- phy-names:  MUST be "pcie-phy".
+- interrupts: Three interrupt entries must be specified.
+- interrupt-names: Must include the following names
+	- "sys"
+	- "legacy"
+	- "client"
+- resets: Must contain five entries for each entry in reset-names.
+	   See ../reset/reset.txt for details.
+- reset-names: Must include the following names
+	- "core"
+	- "mgmt"
+	- "mgmt-sticky"
+	- "pipe"
+- pinctrl-names : The pin control state names
+- pinctrl-0: The "default" pinctrl state
+- #interrupt-cells: specifies the number of cells needed to encode an
+	interrupt source. The value must be 1.
+- interrupt-map-mask and interrupt-map: standard PCI properties
+
+Optional Property:
+- ep-gpios: contain the entry for pre-reset gpio
+- num-lanes: number of lanes to use
+- vpcie3v3-supply: The phandle to the 3.3v regulator to use for pcie.
+- vpcie1v8-supply: The phandle to the 1.8v regulator to use for pcie.
+- vpcie0v9-supply: The phandle to the 0.9v regulator to use for pcie.
+
+*Interrupt controller child node*
+The core controller provides a single interrupt for legacy INTx. The pcie node
+should contain an interrupt controller node as a target for the PCI
+'interrupt-map' property. This node represents the domain at which the four
+INTx interrupts are decoded and routed.
+
+
+Required properties for Interrupt controller child node:
+- interrupt-controller: identifies the node as an interrupt controller
+- #address-cells: specifies the number of cells needed to encode an
+	address. The value must be 0.
+- #interrupt-cells: specifies the number of cells needed to encode an
+	interrupt source. The value must be 1.
+
+Example:
+
+pcie0: pcie at f8000000 {
+	compatible = "rockchip,rk3399-pcie";
+	#address-cells = <3>;
+	#size-cells = <2>;
+	clocks = <&cru ACLK_PCIE>, <&cru ACLK_PERF_PCIE>,
+		 <&cru PCLK_PCIE>, <&cru SCLK_PCIE_PM>;
+	clock-names = "aclk", "aclk-perf",
+		      "hclk", "pm";
+	bus-range = <0x0 0x1>;
+	interrupts = <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 50 IRQ_TYPE_LEVEL_HIGH>,
+		     <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>;
+	interrupt-names = "sys", "legacy", "client";
+	assigned-clocks = <&cru SCLK_PCIEPHY_REF>;
+	assigned-clock-parents = <&cru SCLK_PCIEPHY_REF100M>;
+	assigned-clock-rates = <100000000>;
+	ep-gpios = <&gpio3 13 GPIO_ACTIVE_HIGH>;
+	ranges = <0x83000000 0x0 0xfa000000 0x0 0xfa000000 0x0 0x600000
+		  0x81000000 0x0 0xfa600000 0x0 0xfa600000 0x0 0x100000>;
+	num-lanes = <4>;
+	msi-map = <0x0 &its 0x0 0x1000>;
+	reg = <0x0 0xf8000000 0x0 0x2000000>, <0x0 0xfd000000 0x0 0x1000000>;
+	reg-names = "axi-base", "apb-base";
+	resets = <&cru SRST_PCIE_CORE>, <&cru SRST_PCIE_MGMT>,
+		 <&cru SRST_PCIE_MGMT_STICKY>, <&cru SRST_PCIE_PIPE>;
+	reset-names = "core", "mgmt", "mgmt-sticky", "pipe";
+	phys = <&pcie_phy>;
+	phy-names = "pcie-phy";
+	pinctrl-names = "default";
+	pinctrl-0 = <&pcie_clkreq>;
+	#interrupt-cells = <1>;
+	interrupt-map-mask = <0 0 0 7>;
+	interrupt-map = <0 0 0 1 &pcie0_intc 0>,
+			<0 0 0 2 &pcie0_intc 1>,
+			<0 0 0 3 &pcie0_intc 2>,
+			<0 0 0 4 &pcie0_intc 3>;
+	pcie0_intc: interrupt-controller {
+		interrupt-controller;
+		#address-cells = <0>;
+		#interrupt-cells = <1>;
+	};
+};
-- 
2.3.7




From shawn.lin at rock-chips.com  Sun Jul 17 17:42:13 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Mon, 18 Jul 2016 08:42:13 +0800
Subject: [PATCH v7 2/2] PCI: Rockchip: Add Rockchip PCIe controller support
In-Reply-To: <1468802533-17699-1-git-send-email-shawn.lin@rock-chips.com>
References: <1468802533-17699-1-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <1468802533-17699-2-git-send-email-shawn.lin@rock-chips.com>

This patch adds Rockchip PCIe controller support found
on RK3399 Soc platform.

Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>

---

Changes in v7:
- make it as a build-in driver
- improve gen1/2 training timeout checking
- only clear known interrupt
- fix INTx for 0-base index

Changes in v6:
- use "depends on PCI_MSI_IRQ_DOMAIN" suggested by Arnd

Changes in v5:
- handle multiple pending INTx at the same time
  suggested by Marc

Changes in v4:
- address the comments from Brain

Changes in v3:
- remove header file
- remove struct msi_controller and move most of variables
  of rockchip_pcie_port to become the local ones.
- allow legacy int even if enabling MSI
- drop regulator set voltage operation suggested by Doug

Changes in v2:
- remove phy related stuff and call phy API
- add new head file and define lots of macro to make
  the code more readable
- remove lots msi related code suggested by Marc
- add IO window address translation
- init_port and parse_dt reconstruction suggested by Bharat
- improve wr_own_conf suggested by Arnd
- make pcie as an interrupt controller and fix wrong int handler
  suggested by Marc
- remove PCI_PROBE_ONLY suggested by Lorenzo

 drivers/pci/host/Kconfig         |   11 +
 drivers/pci/host/Makefile        |    1 +
 drivers/pci/host/pcie-rockchip.c | 1233 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 1245 insertions(+)
 create mode 100644 drivers/pci/host/pcie-rockchip.c

diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index c917e2f..4d714d2 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -264,4 +264,15 @@ config PCIE_ARTPEC6
 	  Say Y here to enable PCIe controller support on Axis ARTPEC-6
 	  SoCs.  This PCIe controller uses the DesignWare core.
 
+config PCIE_ROCKCHIP
+	bool "Rockchip PCIe controller"
+	depends on ARM64 && ARCH_ROCKCHIP
+	depends on OF
+	depends on PCI_MSI_IRQ_DOMAIN
+	select MFD_SYSCON
+	help
+	  Say Y here if you want internal PCI support on Rockchip SoC.
+	  There is 1 internal PCIe port available to support GEN2 with
+	  4 slots.
+
 endmenu
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index 5bc0af2..695f716 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -30,3 +30,4 @@ obj-$(CONFIG_PCI_HOST_THUNDER_ECAM) += pci-thunder-ecam.o
 obj-$(CONFIG_PCI_HOST_THUNDER_PEM) += pci-thunder-pem.o
 obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o
 obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o
+obj-$(CONFIG_PCIE_ROCKCHIP) += pcie-rockchip.o
diff --git a/drivers/pci/host/pcie-rockchip.c b/drivers/pci/host/pcie-rockchip.c
new file mode 100644
index 0000000..8a1fef1
--- /dev/null
+++ b/drivers/pci/host/pcie-rockchip.c
@@ -0,0 +1,1233 @@
+/*
+ * Rockchip AXI PCIe host controller driver
+ *
+ * Copyright (c) 2016 Rockchip, Inc.
+ *
+ * Author: Shawn Lin <shawn.lin at rock-chips.com>
+ *         Wenrui Li <wenrui.li at rock-chips.com>
+ *
+ * Bits taken from Synopsys Designware Host controller driver and
+ * ARM PCI Host generic driver.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_pci.h>
+#include <linux/of_platform.h>
+#include <linux/of_irq.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/regmap.h>
+
+#define PCIE_CLIENT_BASE                0x0
+#define PCIE_RC_CONFIG_NORMAL_BASE      0x800000
+#define PCIE_RC_CONFIG_BASE             0xa00000
+#define PCIE_CORE_LINK_CTRL_STATUS      0x8000d0
+#define PCIE_CORE_CTRL_MGMT_BASE        0x900000
+#define PCIE_CORE_AXI_CONF_BASE         0xc00000
+#define PCIE_CORE_AXI_INBOUND_BASE      0xc00800
+#define PCIE_CLIENT_BASIC_STATUS0       0x44
+#define PCIE_CLIENT_BASIC_STATUS1       0x48
+#define PCIE_CLIENT_INT_MASK            0x4c
+#define PCIE_CLIENT_INT_STATUS          0x50
+#define PCIE_RC_CONFIG_RID_CCR          0x8
+#define PCIE_RC_CONFIG_LCS              0xd0
+#define PCIE_RC_BAR_CONF                0x300
+#define PCIE_CORE_OB_REGION_ADDR1       0x4
+#define PCIE_CORE_OB_REGION_DESC0       0x8
+#define PCIE_CORE_OB_REGION_DESC1       0xc
+#define PCIE_RP_IB_ADDR_TRANS           0x4
+#define PCIE_CORE_INT_MASK              0x900210
+#define PCIE_CORE_INT_STATUS            0x90020c
+
+/* Size of one AXI Region (not Region 0) */
+#define AXI_REGION_SIZE                 BIT(20)
+/* Size of Region 0, equal to sum of sizes of other regions */
+#define AXI_REGION_0_SIZE              (32 * (0x1 << 20))
+#define OB_REG_SIZE_SHIFT               5
+#define IB_ROOT_PORT_REG_SIZE_SHIFT     3
+#define AXI_WRAPPER_IO_WRITE            0x6
+#define AXI_WRAPPER_MEM_WRITE           0x2
+#define MAX_AXI_IB_ROOTPORT_REGION_NUM  3
+#define MIN_AXI_ADDR_BITS_PASSED        8
+#define ROCKCHIP_VENDOR_ID              0x1d87
+#define PCIE_ECAM_BUS(x)		(((x) & 0xFF) << 20)
+#define PCIE_ECAM_DEV(x)		(((x) & 0x1F) << 15)
+#define PCIE_ECAM_FUNC(x)		(((x) & 0x7) << 12)
+#define PCIE_ECAM_REG(x)		(((x) & 0xFFF) << 0)
+#define PCIE_ECAM_ADDR(bus, dev, func, reg) \
+	  (PCIE_ECAM_BUS(bus) | PCIE_ECAM_DEV(dev) | \
+	   PCIE_ECAM_FUNC(func) | PCIE_ECAM_REG(reg))
+
+/*
+  * The higher 16-bit of this register is used for write protection
+  * only if BIT(x + 16) set to 1 the BIT(x) can be written.
+  */
+#define HIWORD_UPDATE(val, mask, shift) \
+	((val) << (shift) | (mask) << ((shift) + 16))
+
+#define RC_REGION_0_ADDR_TRANS_H        0x00000000
+#define RC_REGION_0_ADDR_TRANS_L        0x00000000
+#define RC_REGION_0_PASS_BITS           (25 - 1)
+#define RC_REGION_1_ADDR_TRANS_H        0x00000000
+#define RC_REGION_1_ADDR_TRANS_L        0x00400000
+#define RC_REGION_1_PASS_BITS           (20 - 1)
+#define MAX_AXI_WRAPPER_REGION_NUM      33
+#define PCIE_CORE_LCSR_RETRAIN_LINK     BIT(5)
+#define PCIE_CLIENT_CONF_ENABLE         1
+#define PCIE_CLIENT_CONF_ENABLE_SHIFT   0
+#define PCIE_CLIENT_CONF_ENABLE_MASK    0x1
+#define PCIE_CLIENT_LINK_TRAIN_ENABLE   1
+#define PCIE_CLIENT_LINK_TRAIN_SHIFT    1
+#define PCIE_CLIENT_LINK_TRAIN_MASK     0x1
+#define PCIE_CLIENT_ARI_ENABLE          1
+#define PCIE_CLIENT_ARI_ENABLE_SHIFT    3
+#define PCIE_CLIENT_ARI_ENABLE_MASK     0x1
+#define PCIE_CLIENT_CONF_LANE_NUM(x)    (x / 2)
+#define PCIE_CLIENT_CONF_LANE_NUM_SHIFT 4
+#define PCIE_CLIENT_CONF_LANE_NUM_MASK  0x3
+#define PCIE_CLIENT_MODE_RC             1
+#define PCIE_CLIENT_MODE_SHIFT          6
+#define PCIE_CLIENT_MODE_MASK           0x1
+#define PCIE_CLIENT_GEN_SEL_2           1
+#define PCIE_CLIENT_GEN_SEL_1           0
+#define PCIE_CLIENT_GEN_SEL_SHIFT       7
+#define PCIE_CLIENT_GEN_SEL_MASK        0x1
+#define PCIE_CLIENT_LINK_STATUS_UP      0x3
+#define PCIE_CLIENT_LINK_STATUS_SHIFT   20
+#define PCIE_CLIENT_LINK_STATUS_MASK    0x3
+#define PCIE_CORE_PL_CONF_SPEED_2_5G     0x0
+#define PCIE_CORE_PL_CONF_SPEED_5G     0x1
+#define PCIE_CORE_PL_CONF_SPEED_8G     0x2
+#define PCIE_CORE_PL_CONF_SPEED_SHIFT   3
+#define PCIE_CORE_PL_CONF_SPEED_MASK    0x3
+#define PCIE_CORE_PL_CONF_LANE_SHIFT    1
+#define PCIE_CORE_PL_CONF_LANE_MASK     0x3
+#define PCIE_CORE_RC_CONF_SCC_SHIFT     16
+
+/* PCIE_CLIENT_INT_STATUS */
+#define PCIE_CLIENT_INT_LEGACY_DONE     BIT(15)
+#define PCIE_CLIENT_INT_MSG             BIT(14)
+#define PCIE_CLIENT_INT_HOT_RST         BIT(13)
+#define PCIE_CLIENT_INT_DPA             BIT(12)
+#define PCIE_CLIENT_INT_FATAL_ERR       BIT(11)
+#define PCIE_CLIENT_INT_NFATAL_ERR      BIT(10)
+#define PCIE_CLIENT_INT_CORR_ERR        BIT(9)
+#define PCIE_CLIENT_INT_INTD            BIT(8)
+#define PCIE_CLIENT_INT_INTC            BIT(7)
+#define PCIE_CLIENT_INT_INTB            BIT(6)
+#define PCIE_CLIENT_INT_INTA            BIT(5)
+#define PCIE_CLIENT_INT_LOCAL           BIT(4)
+#define PCIE_CLIENT_INT_UDMA            BIT(3)
+#define PCIE_CLIENT_INT_PHY             BIT(2)
+#define PCIE_CLIENT_INT_HOT_PLUG        BIT(1)
+#define PCIE_CLIENT_INT_PWR_STCG        BIT(0)
+#define PCIE_CORE_INT_PRFPE             BIT(0)
+#define PCIE_CORE_INT_CRFPE             BIT(1)
+#define PCIE_CORE_INT_RRPE              BIT(2)
+#define PCIE_CORE_INT_PRFO              BIT(3)
+#define PCIE_CORE_INT_CRFO              BIT(4)
+#define PCIE_CORE_INT_RT                BIT(5)
+#define PCIE_CORE_INT_RTR               BIT(6)
+#define PCIE_CORE_INT_PE                BIT(7)
+#define PCIE_CORE_INT_MTR               BIT(8)
+#define PCIE_CORE_INT_UCR               BIT(9)
+#define PCIE_CORE_INT_FCE               BIT(10)
+#define PCIE_CORE_INT_CT                BIT(11)
+#define PCIE_CORE_INT_UTC               BIT(18)
+#define PCIE_CORE_INT_MMVC              BIT(19)
+
+#define ROCKCHIP_PCIE_RPIFR1_INTR_MASK  GENMASK(8, 5)
+#define ROCKCHIP_PCIE_RPIFR1_INTR_SHIFT 5
+
+#define PCIE_CORE_INT \
+		(PCIE_CORE_INT_PRFPE | PCIE_CORE_INT_CRFPE | \
+		 PCIE_CORE_INT_RRPE | PCIE_CORE_INT_CRFO | \
+		 PCIE_CORE_INT_RT | PCIE_CORE_INT_RTR | \
+		 PCIE_CORE_INT_PE | PCIE_CORE_INT_MTR | \
+		 PCIE_CORE_INT_UCR | PCIE_CORE_INT_FCE | \
+		 PCIE_CORE_INT_CT | PCIE_CORE_INT_UTC | \
+		 PCIE_CORE_INT_MMVC)
+
+#define PCIE_CLIENT_INT_SUBSYSTEM \
+	(PCIE_CLIENT_INT_PWR_STCG | PCIE_CLIENT_INT_HOT_PLUG | \
+	PCIE_CLIENT_INT_PHY | PCIE_CLIENT_INT_UDMA | \
+	PCIE_CLIENT_INT_LOCAL)
+
+#define PCIE_CLIENT_INT_LEGACY \
+	(PCIE_CLIENT_INT_INTA | PCIE_CLIENT_INT_INTB | \
+	PCIE_CLIENT_INT_INTC | PCIE_CLIENT_INT_INTD)
+
+#define PCIE_CLIENT_INT_CLI \
+	(PCIE_CLIENT_INT_CORR_ERR | PCIE_CLIENT_INT_NFATAL_ERR | \
+	PCIE_CLIENT_INT_FATAL_ERR | PCIE_CLIENT_INT_DPA | \
+	PCIE_CLIENT_INT_HOT_RST | PCIE_CLIENT_INT_MSG | \
+	PCIE_CLIENT_INT_LEGACY_DONE | PCIE_CLIENT_INT_LEGACY)
+
+struct rockchip_pcie_port {
+	void	__iomem *reg_base;
+	void	__iomem *apb_base;
+	struct	phy *phy;
+	struct	reset_control *core_rst;
+	struct	reset_control *mgmt_rst;
+	struct	reset_control *mgmt_sticky_rst;
+	struct	reset_control *pipe_rst;
+	struct	clk *aclk_pcie;
+	struct	clk *aclk_perf_pcie;
+	struct	clk *hclk_pcie;
+	struct	clk *clk_pcie_pm;
+	struct	regulator *vpcie3v3; /* 3.3V power supply */
+	struct	regulator *vpcie1v8; /* 1.8V power supply */
+	struct	regulator *vpcie0v9; /* 0.9V power supply */
+	struct	gpio_desc *ep_gpio;
+	u32	lanes;
+	u8	root_bus_nr;
+	struct	device *dev;
+	struct	irq_domain *irq_domain;
+};
+
+static irqreturn_t rockchip_pcie_subsys_irq_handler(int irq, void *arg);
+static irqreturn_t rockchip_pcie_client_irq_handler(int irq, void *arg);
+static void rockchip_pcie_legacy_int_handler(struct irq_desc *desc);
+
+static inline u32 pcie_read(struct rockchip_pcie_port *port, u32 reg)
+{
+	return readl(port->apb_base + reg);
+}
+
+static inline void pcie_write(struct rockchip_pcie_port *port,
+			      u32 val, u32 reg)
+{
+	writel(val, port->apb_base + reg);
+}
+
+static int rockchip_pcie_valid_config(struct rockchip_pcie_port *pp,
+				      struct pci_bus *bus, int dev)
+{
+	/* access only one slot on each root port */
+	if (bus->number == pp->root_bus_nr && dev > 0)
+		return 0;
+
+	/*
+	 * do not read more than one device on the bus directly attached
+	 * to RC's (Virtual Bridge's) DS side.
+	 */
+	if (bus->primary == pp->root_bus_nr && dev > 0)
+		return 0;
+
+	return 1;
+}
+
+static int rockchip_pcie_rd_own_conf(struct rockchip_pcie_port *pp,
+				     int where, int size,
+				     u32 *val)
+{
+	void __iomem *addr = pp->apb_base + PCIE_RC_CONFIG_BASE + where;
+
+	if (!IS_ALIGNED((uintptr_t)addr, size)) {
+		*val = 0;
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	}
+
+	if (size == 4) {
+		*val = readl(addr);
+	} else if (size == 2) {
+		*val = readw(addr);
+	} else if (size == 1) {
+		*val = readb(addr);
+	} else {
+		*val = 0;
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	}
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int rockchip_pcie_wr_own_conf(struct rockchip_pcie_port *pp,
+				     int where, int size, u32 val)
+{
+	u32 mask, tmp, offset;
+
+	offset = (where & (~0x3));
+
+	if (size == 4) {
+		writel(val, pp->apb_base + PCIE_RC_CONFIG_BASE + offset);
+		return PCIBIOS_SUCCESSFUL;
+	}
+
+	mask = ~(((1 << (size * 8)) - 1) << ((where & 0x3) * 8));
+
+	tmp = readl(pp->apb_base + PCIE_RC_CONFIG_BASE + offset) & mask;
+	tmp |= val << ((where & 0x3) * 8);
+	writel(tmp, pp->apb_base + PCIE_RC_CONFIG_BASE + offset);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int rockchip_pcie_rd_other_conf(struct rockchip_pcie_port *pp,
+				       struct pci_bus *bus, u32 devfn,
+				       int where, int size, u32 *val)
+{
+	u32 busdev;
+
+	busdev = PCIE_ECAM_ADDR(bus->number, PCI_SLOT(devfn),
+				PCI_FUNC(devfn), where);
+
+	if (!IS_ALIGNED(busdev, size)) {
+		*val = 0;
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	}
+
+	if (size == 4) {
+		*val = readl(pp->reg_base + busdev);
+	} else if (size == 2) {
+		*val = readw(pp->reg_base + busdev);
+	} else if (size == 1) {
+		*val = readb(pp->reg_base + busdev);
+	} else {
+		*val = 0;
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	}
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int rockchip_pcie_wr_other_conf(struct rockchip_pcie_port *pp,
+				       struct pci_bus *bus, u32 devfn,
+				       int where, int size, u32 val)
+{
+	u32 busdev;
+
+	busdev = PCIE_ECAM_ADDR(bus->number, PCI_SLOT(devfn),
+				PCI_FUNC(devfn), where);
+	if (!IS_ALIGNED(busdev, size))
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+
+	if (size == 4)
+		writel(val, pp->reg_base + busdev);
+	else if (size == 2)
+		writew(val, pp->reg_base + busdev);
+	else if (size == 1)
+		writeb(val, pp->reg_base + busdev);
+	else
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int rockchip_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
+				 int size, u32 *val)
+{
+	struct rockchip_pcie_port *pp = bus->sysdata;
+	int ret;
+
+	if (rockchip_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) {
+		*val = 0xffffffff;
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	}
+
+	if (bus->number != pp->root_bus_nr)
+		ret = rockchip_pcie_rd_other_conf(pp, bus, devfn,
+						  where, size, val);
+	else
+		ret = rockchip_pcie_rd_own_conf(pp, where, size, val);
+
+	return ret;
+}
+
+static int rockchip_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
+				 int where, int size, u32 val)
+{
+	struct rockchip_pcie_port *pp = bus->sysdata;
+	int ret;
+
+	if (rockchip_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	if (bus->number != pp->root_bus_nr)
+		ret = rockchip_pcie_wr_other_conf(pp, bus, devfn,
+						  where, size, val);
+	else
+		ret = rockchip_pcie_wr_own_conf(pp, where, size, val);
+
+	return ret;
+}
+
+static struct pci_ops rockchip_pcie_ops = {
+	.read = rockchip_pcie_rd_conf,
+	.write = rockchip_pcie_wr_conf,
+};
+
+/**
+ * rockchip_pcie_init_port - Initialize hardware
+ * @port: PCIe port information
+ */
+static int rockchip_pcie_init_port(struct rockchip_pcie_port *port)
+{
+	int err;
+	u32 status;
+	unsigned long timeout;
+
+	gpiod_set_value(port->ep_gpio, 0);
+
+	err = phy_init(port->phy);
+	if (err < 0) {
+		dev_err(port->dev, "fail to init phy, err %d\n", err);
+		return err;
+	}
+
+	err = reset_control_assert(port->core_rst);
+	if (err) {
+		dev_err(port->dev, "assert core_rst err %d\n", err);
+		return err;
+	}
+
+	err = reset_control_assert(port->mgmt_rst);
+	if (err) {
+		dev_err(port->dev, "assert mgmt_rst err %d\n", err);
+		return err;
+	}
+
+	err = reset_control_assert(port->mgmt_sticky_rst);
+	if (err) {
+		dev_err(port->dev, "assert mgmt_sticky_rst err %d\n", err);
+		return err;
+	}
+
+	err = reset_control_assert(port->pipe_rst);
+	if (err) {
+		dev_err(port->dev, "assert pipe_rst err %d\n", err);
+		return err;
+	}
+
+	pcie_write(port,
+		   HIWORD_UPDATE(PCIE_CLIENT_CONF_ENABLE,
+				 PCIE_CLIENT_CONF_ENABLE_MASK,
+				 PCIE_CLIENT_CONF_ENABLE_SHIFT) |
+		   HIWORD_UPDATE(PCIE_CLIENT_CONF_LANE_NUM(port->lanes),
+				 PCIE_CLIENT_CONF_LANE_NUM_MASK,
+				 PCIE_CLIENT_CONF_LANE_NUM_SHIFT) |
+		   HIWORD_UPDATE(PCIE_CLIENT_MODE_RC,
+				 PCIE_CLIENT_MODE_MASK,
+				 PCIE_CLIENT_MODE_SHIFT) |
+		   HIWORD_UPDATE(PCIE_CLIENT_ARI_ENABLE,
+				 PCIE_CLIENT_ARI_ENABLE_MASK,
+				 PCIE_CLIENT_ARI_ENABLE_SHIFT) |
+		   HIWORD_UPDATE(PCIE_CLIENT_GEN_SEL_2,
+				 PCIE_CLIENT_GEN_SEL_MASK,
+				 PCIE_CLIENT_GEN_SEL_SHIFT),
+		   PCIE_CLIENT_BASE);
+
+	err = phy_power_on(port->phy);
+	if (err) {
+		dev_err(port->dev, "fail to power on phy, err %d\n", err);
+		return err;
+	}
+
+	err = reset_control_deassert(port->core_rst);
+	if (err) {
+		dev_err(port->dev, "deassert core_rst err %d\n", err);
+		return err;
+	}
+
+	err = reset_control_deassert(port->mgmt_rst);
+	if (err) {
+		dev_err(port->dev, "deassert mgmt_rst err %d\n", err);
+		return err;
+	}
+
+	err = reset_control_deassert(port->mgmt_sticky_rst);
+	if (err) {
+		dev_err(port->dev, "deassert mgmt_sticky_rst err %d\n", err);
+		return err;
+	}
+
+	err = reset_control_deassert(port->pipe_rst);
+	if (err) {
+		dev_err(port->dev, "deassert pipe_rst err %d\n", err);
+		return err;
+	}
+
+	/* Enable Gen1 training */
+	pcie_write(port,
+		   HIWORD_UPDATE(PCIE_CLIENT_LINK_TRAIN_ENABLE,
+				 PCIE_CLIENT_LINK_TRAIN_MASK,
+				 PCIE_CLIENT_LINK_TRAIN_SHIFT),
+		   PCIE_CLIENT_BASE);
+
+	gpiod_set_value(port->ep_gpio, 1);
+
+	/* 500ms timeout value should be enough for Gen1/2 training */
+	timeout = jiffies + msecs_to_jiffies(500);
+
+	for (;;) {
+		status = pcie_read(port, PCIE_CLIENT_BASIC_STATUS1);
+		if (((status >> PCIE_CLIENT_LINK_STATUS_SHIFT) &
+		      PCIE_CLIENT_LINK_STATUS_MASK) ==
+		      PCIE_CLIENT_LINK_STATUS_UP) {
+			dev_dbg(port->dev, "pcie link training gen1 pass!\n");
+			break;
+		}
+
+		msleep(20);
+
+		if (!time_before(jiffies, timeout)) {
+			err = -ETIMEDOUT;
+			break;
+		}
+
+	}
+
+	/* Double check gen1 training */
+	if (err) {
+		status = pcie_read(port, PCIE_CLIENT_BASIC_STATUS1);
+		err = (((status >> PCIE_CLIENT_LINK_STATUS_SHIFT) &
+			PCIE_CLIENT_LINK_STATUS_MASK) ==
+			PCIE_CLIENT_LINK_STATUS_UP) ? 0 : -ETIMEDOUT;
+		if (err) {
+			dev_err(port->dev, "pcie link training gen1 timeout!\n");
+			return err;
+		}
+	}
+
+	/*
+	 * Enable retrain for gen2. This should be configured only after
+	 * gen1 finished.
+	 */
+	status = pcie_read(port,
+			   PCIE_RC_CONFIG_LCS + PCIE_RC_CONFIG_BASE);
+	status |= PCIE_CORE_LCSR_RETRAIN_LINK;
+	pcie_write(port, status,
+		   PCIE_RC_CONFIG_LCS + PCIE_RC_CONFIG_BASE);
+
+	timeout = jiffies + msecs_to_jiffies(500);
+	for (;;) {
+		status = pcie_read(port, PCIE_CORE_CTRL_MGMT_BASE);
+		if (((status >> PCIE_CORE_PL_CONF_SPEED_SHIFT) &
+		     PCIE_CORE_PL_CONF_SPEED_MASK) ==
+		     PCIE_CORE_PL_CONF_SPEED_5G) {
+			dev_dbg(port->dev, "pcie link training gen2 pass!\n");
+			break;
+		}
+
+		msleep(20);
+
+		if (!time_before(jiffies, timeout)) {
+			err = -ETIMEDOUT;
+			break;
+		}
+	}
+
+	/* Double check gen2 training */
+	if (err) {
+		status = pcie_read(port, PCIE_CORE_CTRL_MGMT_BASE);
+		err = (((status >> PCIE_CORE_PL_CONF_SPEED_SHIFT) &
+			PCIE_CORE_PL_CONF_SPEED_MASK) ==
+			PCIE_CORE_PL_CONF_SPEED_5G) ? 0 : -ETIMEDOUT;
+		if (err)
+			dev_dbg(port->dev, "pcie link training gen2 timeout, fall back to gen1!\n");
+	}
+
+	/* Check the final link with from negotiated lane counter from MGMT */
+	status = pcie_read(port, PCIE_CORE_CTRL_MGMT_BASE);
+	status =  0x1 << ((status >> PCIE_CORE_PL_CONF_LANE_SHIFT) &
+			   PCIE_CORE_PL_CONF_LANE_MASK);
+	dev_dbg(port->dev, "current link width is x%d\n", status);
+
+	pcie_write(port, ROCKCHIP_VENDOR_ID, PCIE_RC_CONFIG_BASE);
+	pcie_write(port, PCI_CLASS_BRIDGE_PCI << PCIE_CORE_RC_CONF_SCC_SHIFT,
+		   PCIE_RC_CONFIG_BASE + PCIE_RC_CONFIG_RID_CCR);
+	pcie_write(port, 0x0, PCIE_CORE_CTRL_MGMT_BASE + PCIE_RC_BAR_CONF);
+
+	pcie_write(port, (RC_REGION_0_ADDR_TRANS_L + RC_REGION_0_PASS_BITS),
+		   PCIE_CORE_AXI_CONF_BASE);
+	pcie_write(port, RC_REGION_0_ADDR_TRANS_H,
+		   PCIE_CORE_AXI_CONF_BASE + PCIE_CORE_OB_REGION_ADDR1);
+	pcie_write(port, 0x0080000a,
+		   PCIE_CORE_AXI_CONF_BASE + PCIE_CORE_OB_REGION_DESC0);
+	pcie_write(port, 0x0,
+		   PCIE_CORE_AXI_CONF_BASE + PCIE_CORE_OB_REGION_DESC1);
+
+	return 0;
+}
+
+/**
+ * rockchip_pcie_parse_dt - Parse Device tree
+ * @port: PCIe port information
+ *
+ * Return: '0' on success and error value on failure
+ */
+static int rockchip_pcie_parse_dt(struct rockchip_pcie_port *port)
+{
+	struct device *dev = port->dev;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct device_node *node = dev->of_node;
+	struct resource *regs;
+	int irq;
+	int err = -ENODEV;
+
+	regs = platform_get_resource_byname(pdev,
+					    IORESOURCE_MEM,
+					    "axi-base");
+	if (!regs) {
+		dev_err(dev, "missing axi-base property\n");
+		return err;
+	}
+
+	port->reg_base = devm_ioremap_resource(dev, regs);
+	if (IS_ERR(port->reg_base))
+		return PTR_ERR(port->reg_base);
+
+	regs = platform_get_resource_byname(pdev,
+					    IORESOURCE_MEM,
+					    "apb-base");
+	if (!regs) {
+		dev_err(dev, "missing apb-base property\n");
+		return err;
+	}
+
+	port->apb_base = devm_ioremap_resource(dev, regs);
+	if (IS_ERR(port->apb_base))
+		return PTR_ERR(port->apb_base);
+
+	port->phy = devm_phy_get(dev, "pcie-phy");
+	if (IS_ERR(port->phy)) {
+		if (PTR_ERR(port->phy) != -EPROBE_DEFER)
+			dev_err(dev, "Missing phy\n");
+		return PTR_ERR(port->phy);
+	}
+
+	port->lanes = 1;
+	err = of_property_read_u32(node, "num-lanes", &port->lanes);
+	if (!err && ((port->lanes == 0) ||
+		     (port->lanes == 3) ||
+		     (port->lanes > 4))) {
+		dev_warn(dev, "invalid num-lanes, default use one lane\n");
+		port->lanes = 1;
+	}
+
+	port->core_rst = devm_reset_control_get(dev, "core");
+	if (IS_ERR(port->core_rst)) {
+		if (PTR_ERR(port->core_rst) != -EPROBE_DEFER)
+			dev_err(dev, "missing core rst property in node %s\n",
+				node->name);
+		return PTR_ERR(port->core_rst);
+	}
+
+	port->mgmt_rst = devm_reset_control_get(dev, "mgmt");
+	if (IS_ERR(port->mgmt_rst)) {
+		if (PTR_ERR(port->mgmt_rst) != -EPROBE_DEFER)
+			dev_err(dev, "missing mgmt rst property in node %s\n",
+				node->name);
+		return PTR_ERR(port->mgmt_rst);
+	}
+
+	port->mgmt_sticky_rst = devm_reset_control_get(dev, "mgmt-sticky");
+	if (IS_ERR(port->mgmt_sticky_rst)) {
+		if (PTR_ERR(port->mgmt_sticky_rst) != -EPROBE_DEFER)
+			dev_err(dev, "missing mgmt-sticky rst property in node %s\n",
+				node->name);
+		return PTR_ERR(port->mgmt_sticky_rst);
+	}
+
+	port->pipe_rst = devm_reset_control_get(dev, "pipe");
+	if (IS_ERR(port->pipe_rst)) {
+		if (PTR_ERR(port->pipe_rst) != -EPROBE_DEFER)
+			dev_err(dev, "missing pipe rst property in node %s\n",
+				node->name);
+		return PTR_ERR(port->pipe_rst);
+	}
+
+	port->ep_gpio = devm_gpiod_get(dev, "ep", GPIOD_OUT_HIGH);
+	if (IS_ERR(port->ep_gpio)) {
+		dev_err(dev, "missing ep-gpios property in node %s\n",
+			node->name);
+		return PTR_ERR(port->ep_gpio);
+	}
+
+	port->aclk_pcie = devm_clk_get(dev, "aclk");
+	if (IS_ERR(port->aclk_pcie)) {
+		dev_err(dev, "aclk clock not found.\n");
+		return PTR_ERR(port->aclk_pcie);
+	}
+
+	port->aclk_perf_pcie = devm_clk_get(dev, "aclk-perf");
+	if (IS_ERR(port->aclk_perf_pcie)) {
+		dev_err(dev, "aclk_perf clock not found.\n");
+		return PTR_ERR(port->aclk_perf_pcie);
+	}
+
+	port->hclk_pcie = devm_clk_get(dev, "hclk");
+	if (IS_ERR(port->hclk_pcie)) {
+		dev_err(dev, "hclk clock not found.\n");
+		return PTR_ERR(port->hclk_pcie);
+	}
+
+	port->clk_pcie_pm = devm_clk_get(dev, "pm");
+	if (IS_ERR(port->clk_pcie_pm)) {
+		dev_err(dev, "pm clock not found.\n");
+		return PTR_ERR(port->clk_pcie_pm);
+	}
+
+	irq = platform_get_irq_byname(pdev, "sys");
+	if (irq < 0) {
+		dev_err(dev, "missing pcie_sys IRQ resource\n");
+		return -EINVAL;
+	}
+
+	err = devm_request_irq(dev, irq, rockchip_pcie_subsys_irq_handler,
+			       IRQF_SHARED, "pcie-sys", port);
+	if (err) {
+		dev_err(dev, "failed to request pcie subsystem irq\n");
+		return err;
+	}
+
+	irq = platform_get_irq_byname(pdev, "legacy");
+	if (irq < 0) {
+		dev_err(dev, "missing pcie_legacy IRQ resource\n");
+		return -EINVAL;
+	}
+
+	irq_set_chained_handler_and_data(irq,
+					 rockchip_pcie_legacy_int_handler,
+					 port);
+
+	irq = platform_get_irq_byname(pdev, "client");
+	if (irq < 0) {
+		dev_err(dev, "missing pcie-client IRQ resource\n");
+		return -EINVAL;
+	}
+
+	err = devm_request_irq(dev, irq, rockchip_pcie_client_irq_handler,
+			       IRQF_SHARED, "pcie-client", port);
+	if (err) {
+		dev_err(dev, "failed to request pcie client irq\n");
+		return err;
+	}
+
+	port->vpcie3v3 = devm_regulator_get_optional(dev, "vpcie3v3");
+	if (IS_ERR(port->vpcie3v3)) {
+		if (PTR_ERR(port->vpcie3v3) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+		dev_info(dev, "No vpcie3v3 regulator found.\n");
+	}
+
+	port->vpcie1v8 = devm_regulator_get_optional(dev, "vpcie1v8");
+	if (IS_ERR(port->vpcie1v8)) {
+		if (PTR_ERR(port->vpcie1v8) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+		dev_info(dev, "No vpcie1v8 regulator found.\n");
+	}
+
+	port->vpcie0v9 = devm_regulator_get_optional(dev, "vpcie0v9");
+	if (IS_ERR(port->vpcie0v9)) {
+		if (PTR_ERR(port->vpcie0v9) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+		dev_info(dev, "No vpcie0v9 regulator found.\n");
+	}
+
+	return 0;
+}
+
+static int rockchip_pcie_set_vpcie(struct rockchip_pcie_port *port)
+{
+	int err;
+
+	if (!IS_ERR(port->vpcie3v3)) {
+		err = regulator_enable(port->vpcie3v3);
+		if (err) {
+			dev_err(port->dev, "Fail to enable vpcie3v3 regulator.\n");
+			goto err_out;
+		}
+	}
+
+	if (!IS_ERR(port->vpcie1v8)) {
+		err = regulator_enable(port->vpcie1v8);
+		if (err) {
+			dev_err(port->dev, "Fail to enable vpcie1v8 regulator.\n");
+			goto err_disable_3v3;
+		}
+	}
+
+	if (!IS_ERR(port->vpcie0v9)) {
+		err = regulator_enable(port->vpcie0v9);
+		if (err) {
+			dev_err(port->dev, "Fail to enable vpcie0v9 regulator.\n");
+			goto err_disable_1v8;
+		}
+	}
+
+	return 0;
+
+err_disable_1v8:
+	if (!IS_ERR(port->vpcie1v8))
+		regulator_disable(port->vpcie1v8);
+err_disable_3v3:
+	if (!IS_ERR(port->vpcie3v3))
+		regulator_disable(port->vpcie3v3);
+err_out:
+	return err;
+}
+
+static void rockchip_pcie_enable_interrupts(struct rockchip_pcie_port *port)
+{
+	pcie_write(port, (PCIE_CLIENT_INT_CLI << 16) &
+		   (~PCIE_CLIENT_INT_CLI), PCIE_CLIENT_INT_MASK);
+	pcie_write(port, PCIE_CORE_INT, PCIE_CORE_INT_MASK);
+}
+
+static int rockchip_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
+				  irq_hw_number_t hwirq)
+{
+	irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq);
+	irq_set_chip_data(irq, domain->host_data);
+
+	return 0;
+}
+
+static const struct irq_domain_ops intx_domain_ops = {
+	.map = rockchip_pcie_intx_map,
+};
+
+static int rockchip_pcie_init_irq_domain(struct rockchip_pcie_port *pp)
+{
+	struct device *dev = pp->dev;
+	struct device_node *intc = of_get_next_child(dev->of_node, NULL);
+
+	if (!intc) {
+		dev_err(dev, "Missing child interrupt-controller node\n");
+		return -EINVAL;
+	}
+
+	pp->irq_domain = irq_domain_add_linear(intc, 4, &intx_domain_ops, pp);
+	if (!pp->irq_domain) {
+		dev_err(dev, "Failed to get a INTx IRQ domain\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static irqreturn_t rockchip_pcie_subsys_irq_handler(int irq, void *arg)
+{
+	struct rockchip_pcie_port *pp = arg;
+	u32 reg;
+	u32 sub_reg;
+
+	reg = pcie_read(pp, PCIE_CLIENT_INT_STATUS);
+	if (reg & PCIE_CLIENT_INT_LOCAL) {
+		dev_dbg(pp->dev, "local interrupt recived\n");
+		sub_reg = pcie_read(pp, PCIE_CORE_INT_STATUS);
+		if (sub_reg & PCIE_CORE_INT_PRFPE)
+			dev_dbg(pp->dev, "Parity error detected while reading from the PNP Receive FIFO RAM\n");
+
+		if (sub_reg & PCIE_CORE_INT_CRFPE)
+			dev_dbg(pp->dev, "Parity error detected while reading from the Completion Receive FIFO RAM\n");
+
+		if (sub_reg & PCIE_CORE_INT_RRPE)
+			dev_dbg(pp->dev, "Parity error detected while reading from Replay Buffer RAM\n");
+
+		if (sub_reg & PCIE_CORE_INT_PRFO)
+			dev_dbg(pp->dev, "Overflow occurred in the PNP Receive FIFO\n");
+
+		if (sub_reg & PCIE_CORE_INT_CRFO)
+			dev_dbg(pp->dev, "Overflow occurred in the Completion Receive FIFO\n");
+
+		if (sub_reg & PCIE_CORE_INT_RT)
+			dev_dbg(pp->dev, "Replay timer timed out\n");
+
+		if (sub_reg & PCIE_CORE_INT_RTR)
+			dev_dbg(pp->dev, "Replay timer rolled over after 4 transmissions of the same TLP\n");
+
+		if (sub_reg & PCIE_CORE_INT_PE)
+			dev_dbg(pp->dev, "Phy error detected on receive side\n");
+
+		if (sub_reg & PCIE_CORE_INT_MTR)
+			dev_dbg(pp->dev, "Malformed TLP received from the link\n");
+
+		if (sub_reg & PCIE_CORE_INT_UCR)
+			dev_dbg(pp->dev, "Malformed TLP received from the link\n");
+
+		if (sub_reg & PCIE_CORE_INT_FCE)
+			dev_dbg(pp->dev, "An error was observed in the flow control advertisements from the other side\n");
+
+		if (sub_reg & PCIE_CORE_INT_CT)
+			dev_dbg(pp->dev, "A request timed out waiting for completion\n");
+
+		if (sub_reg & PCIE_CORE_INT_UTC)
+			dev_dbg(pp->dev, "Unmapped TC error\n");
+
+		if (sub_reg & PCIE_CORE_INT_MMVC)
+			dev_dbg(pp->dev, "MSI mask register changes\n");
+
+		pcie_write(pp, sub_reg, PCIE_CORE_INT_STATUS);
+	}
+
+	pcie_write(pp, reg & PCIE_CLIENT_INT_LOCAL, PCIE_CLIENT_INT_STATUS);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t rockchip_pcie_client_irq_handler(int irq, void *arg)
+{
+	struct rockchip_pcie_port *pp = arg;
+	u32 reg;
+
+	reg = pcie_read(pp, PCIE_CLIENT_INT_STATUS);
+	if (reg & PCIE_CLIENT_INT_LEGACY_DONE)
+		dev_dbg(pp->dev, "legacy done interrupt recived\n");
+
+	if (reg & PCIE_CLIENT_INT_MSG)
+		dev_dbg(pp->dev, "message done interrupt recived\n");
+
+	if (reg & PCIE_CLIENT_INT_HOT_RST)
+		dev_dbg(pp->dev, "hot reset interrupt recived\n");
+
+	if (reg & PCIE_CLIENT_INT_DPA)
+		dev_dbg(pp->dev, "dpa interrupt recived\n");
+
+	if (reg & PCIE_CLIENT_INT_FATAL_ERR)
+		dev_dbg(pp->dev, "fatal error interrupt recived\n");
+
+	if (reg & PCIE_CLIENT_INT_NFATAL_ERR)
+		dev_dbg(pp->dev, "no fatal error interrupt recived\n");
+
+	if (reg & PCIE_CLIENT_INT_CORR_ERR)
+		dev_dbg(pp->dev, "correctable error interrupt recived\n");
+
+	pcie_write(pp, reg & (PCIE_CLIENT_INT_LEGACY_DONE |
+			      PCIE_CLIENT_INT_MSG | PCIE_CLIENT_INT_HOT_RST |
+			      PCIE_CLIENT_INT_DPA | PCIE_CLIENT_INT_FATAL_ERR |
+			      PCIE_CLIENT_INT_NFATAL_ERR |
+			      PCIE_CLIENT_INT_CORR_ERR),
+			      PCIE_CLIENT_INT_STATUS);
+	return IRQ_HANDLED;
+}
+
+static void rockchip_pcie_legacy_int_handler(struct irq_desc *desc)
+{
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	struct rockchip_pcie_port *port;
+	u32 reg;
+	u32 hwirq;
+	u32 virq;
+
+	chained_irq_enter(chip, desc);
+	port = irq_desc_get_handler_data(desc);
+
+	reg = pcie_read(port, PCIE_CLIENT_INT_STATUS);
+	reg = (reg & ROCKCHIP_PCIE_RPIFR1_INTR_MASK) >>
+	       ROCKCHIP_PCIE_RPIFR1_INTR_SHIFT;
+
+	while (reg) {
+		hwirq = ffs(reg) - 1;
+		reg &= ~BIT(hwirq);
+
+		virq = irq_find_mapping(port->irq_domain, hwirq);
+		if (virq)
+			generic_handle_irq(virq);
+		else
+			dev_err(port->dev, "unexpected IRQ, INT%d\n", hwirq);
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static int rockchip_pcie_prog_ob_atu(struct rockchip_pcie_port *pp,
+				     int region_no,
+				     int type, u8 num_pass_bits,
+				     u32 lower_addr, u32 upper_addr)
+{
+	u32 ob_addr_0 = 0;
+	u32 ob_addr_1 = 0;
+	u32 ob_desc_0 = 0;
+	u32 ob_desc_1 = 0;
+	void __iomem *aw_base;
+
+	if (!pp)
+		return -EINVAL;
+	if (region_no >= MAX_AXI_WRAPPER_REGION_NUM)
+		return -EINVAL;
+	if ((num_pass_bits + 1) < 8)
+		return -EINVAL;
+	if (num_pass_bits > 63)
+		return -EINVAL;
+	if (region_no == 0) {
+		if (AXI_REGION_0_SIZE < (2ULL << num_pass_bits))
+		return -EINVAL;
+	}
+	if (region_no != 0) {
+		if (AXI_REGION_SIZE < (2ULL << num_pass_bits))
+		return -EINVAL;
+	}
+	aw_base = pp->apb_base + PCIE_CORE_AXI_CONF_BASE;
+	aw_base += (region_no << OB_REG_SIZE_SHIFT);
+
+	ob_addr_0 = (ob_addr_0 &
+		     ~0x0000003fU) | (num_pass_bits &
+		     0x0000003fU);
+	ob_addr_0 = (ob_addr_0 &
+		     ~0xffffff00U) | (lower_addr & 0xffffff00U);
+	ob_addr_1 = upper_addr;
+	ob_desc_0 = (1 << 23 | type);
+
+	writel(ob_addr_0, aw_base);
+	writel(ob_addr_1, aw_base + PCIE_CORE_OB_REGION_ADDR1);
+	writel(ob_desc_0, aw_base + PCIE_CORE_OB_REGION_DESC0);
+	writel(ob_desc_1, aw_base + PCIE_CORE_OB_REGION_DESC1);
+
+	return 0;
+}
+
+static int rockchip_pcie_prog_ib_atu(struct rockchip_pcie_port *pp,
+				     int region_no,
+				     u8 num_pass_bits,
+				     u32 lower_addr,
+				     u32 upper_addr)
+{
+	u32 ib_addr_0 = 0;
+	u32 ib_addr_1 = 0;
+	void __iomem *aw_base;
+
+	if (!pp)
+		return -EINVAL;
+	if (region_no > MAX_AXI_IB_ROOTPORT_REGION_NUM)
+		return -EINVAL;
+	if ((num_pass_bits + 1) < MIN_AXI_ADDR_BITS_PASSED)
+		return -EINVAL;
+	if (num_pass_bits > 63)
+		return -EINVAL;
+	aw_base = pp->apb_base + PCIE_CORE_AXI_INBOUND_BASE;
+	aw_base += (region_no << IB_ROOT_PORT_REG_SIZE_SHIFT);
+	ib_addr_0 = (ib_addr_0 &
+		     ~0x0000003fU) | (num_pass_bits &
+		     0x0000003fU);
+
+	ib_addr_0 = (ib_addr_0 & ~0xffffff00U) |
+		     ((lower_addr << 8) & 0xffffff00U);
+	ib_addr_1 = upper_addr;
+
+	writel(ib_addr_0, aw_base);
+	writel(ib_addr_1, aw_base + PCIE_RP_IB_ADDR_TRANS);
+
+	return 0;
+}
+
+static int rockchip_pcie_probe(struct platform_device *pdev)
+{
+	struct rockchip_pcie_port *port;
+	struct device *dev = &pdev->dev;
+	struct pci_bus *bus, *child;
+	struct resource_entry *win;
+	resource_size_t io_base;
+	struct resource	*busn = NULL;
+	struct resource	*mem = NULL;
+	struct resource	*io = NULL;
+	phys_addr_t io_bus_addr = 0;
+	u32 io_size = 0;
+	phys_addr_t mem_bus_addr = 0;
+	u32 mem_size = 0;
+	int reg_no = 0;
+	int err = 0;
+	int offset = 0;
+
+	LIST_HEAD(res);
+
+	if (!dev->of_node)
+		return -ENODEV;
+
+	port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	port->dev = dev;
+
+	err = rockchip_pcie_parse_dt(port);
+	if (err)
+		return err;
+
+	err = clk_prepare_enable(port->aclk_pcie);
+	if (err) {
+		dev_err(dev, "Unable to enable aclk_pcie clock.\n");
+		goto err_aclk_pcie;
+	}
+
+	err = clk_prepare_enable(port->aclk_perf_pcie);
+	if (err) {
+		dev_err(dev, "Unable to enable aclk_perf_pcie clock.\n");
+		goto err_aclk_perf_pcie;
+	}
+
+	err = clk_prepare_enable(port->hclk_pcie);
+	if (err) {
+		dev_err(dev, "Unable to enable hclk_pcie clock.\n");
+		goto err_hclk_pcie;
+	}
+
+	err = clk_prepare_enable(port->clk_pcie_pm);
+	if (err) {
+		dev_err(dev, "Unable to enable hclk_pcie clock.\n");
+		goto err_pcie_pm;
+	}
+
+	err = rockchip_pcie_set_vpcie(port);
+	if (err) {
+		dev_err(port->dev, "Fail to set vpcie regulator.\n");
+		goto err_set_vpcie;
+	}
+
+	err = rockchip_pcie_init_port(port);
+	if (err)
+		goto err_vpcie;
+
+	platform_set_drvdata(pdev, port);
+
+	rockchip_pcie_enable_interrupts(port);
+
+	err = rockchip_pcie_init_irq_domain(port);
+	if (err < 0)
+		goto err_vpcie;
+
+	err = of_pci_get_host_bridge_resources(dev->of_node, 0, 0xff,
+					       &res, &io_base);
+	if (err)
+		goto err_vpcie;
+
+	/* Get the I/O and memory ranges from DT */
+	resource_list_for_each_entry(win, &res) {
+		switch (resource_type(win->res)) {
+		case IORESOURCE_IO:
+			io = win->res;
+			io->name = "I/O";
+			io_size = resource_size(io);
+			io_bus_addr = io->start - win->offset;
+			err = pci_remap_iospace(io, io_base);
+			if (err) {
+				dev_warn(port->dev, "error %d: failed to map resource %pR\n",
+					 err, io);
+				continue;
+			}
+			break;
+		case IORESOURCE_MEM:
+			mem = win->res;
+			mem->name = "MEM";
+			mem_size = resource_size(mem);
+			mem_bus_addr = mem->start - win->offset;
+			break;
+		case IORESOURCE_BUS:
+			busn = win->res;
+			break;
+		default:
+			continue;
+		}
+	}
+
+	if (mem_size)
+		for (reg_no = 0; reg_no < (mem_size >> 20); reg_no++) {
+			err = rockchip_pcie_prog_ob_atu(port, reg_no + 1,
+							AXI_WRAPPER_MEM_WRITE,
+							20 - 1,
+							mem_bus_addr +
+							(reg_no << 20),
+							0);
+			if (err) {
+				dev_err(dev, "Program RC mem outbound atu failed\n");
+				goto err_vpcie;
+			}
+		}
+
+	err = rockchip_pcie_prog_ib_atu(port, 2, 32 - 1, 0x0, 0);
+	if (err) {
+		dev_err(dev, "Program RC mem inbound atu failed\n");
+		goto err_vpcie;
+	}
+
+	offset = mem_size >> 20;
+
+	if (io_size)
+		for (reg_no = 0; reg_no < (io_size >> 20); reg_no++) {
+			err = rockchip_pcie_prog_ob_atu(port,
+							reg_no + 1 + offset,
+							AXI_WRAPPER_IO_WRITE,
+							20 - 1,
+							io_bus_addr +
+							(reg_no << 20),
+							0);
+			if (err) {
+				dev_err(dev, "Program RC io outbound atu failed\n");
+				goto err_vpcie;
+			}
+		}
+
+	port->root_bus_nr = busn->start;
+
+	bus = pci_scan_root_bus(&pdev->dev, 0,
+				&rockchip_pcie_ops, port, &res);
+
+	if (!bus) {
+		err = -ENOMEM;
+		goto err_vpcie;
+	}
+
+	pci_bus_size_bridges(bus);
+	pci_bus_assign_resources(bus);
+	list_for_each_entry(child, &bus->children, node)
+		pcie_bus_configure_settings(child);
+
+	pci_bus_add_devices(bus);
+
+	return err;
+
+err_vpcie:
+	if (!IS_ERR(port->vpcie3v3))
+		regulator_disable(port->vpcie3v3);
+	if (!IS_ERR(port->vpcie1v8))
+		regulator_disable(port->vpcie1v8);
+	if (!IS_ERR(port->vpcie0v9))
+		regulator_disable(port->vpcie0v9);
+err_set_vpcie:
+	clk_disable_unprepare(port->clk_pcie_pm);
+err_pcie_pm:
+	clk_disable_unprepare(port->hclk_pcie);
+err_hclk_pcie:
+	clk_disable_unprepare(port->aclk_perf_pcie);
+err_aclk_perf_pcie:
+	clk_disable_unprepare(port->aclk_pcie);
+err_aclk_pcie:
+	return err;
+}
+
+static const struct of_device_id rockchip_pcie_of_match[] = {
+	{ .compatible = "rockchip,rk3399-pcie", },
+	{}
+};
+
+static struct platform_driver rockchip_pcie_driver = {
+	.driver = {
+		.name = "rockchip-pcie",
+		.of_match_table = rockchip_pcie_of_match,
+	},
+	.probe = rockchip_pcie_probe,
+
+};
+module_platform_driver(rockchip_pcie_driver);
+
+MODULE_AUTHOR("Rockchip Inc");
+MODULE_DESCRIPTION("Rockchip AXI PCIe driver");
+MODULE_LICENSE("GPL v2");
-- 
2.3.7




From shawn.lin at rock-chips.com  Sun Jul 17 18:13:49 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Mon, 18 Jul 2016 09:13:49 +0800
Subject: [PATCH v3] arm64: dts: rockchip: Add basic support for orion-r68
In-Reply-To: <1468577391-5096-1-git-send-email-matthias.bgg@gmail.com>
References: <1468577391-5096-1-git-send-email-matthias.bgg@gmail.com>
Message-ID: <24940135-f3a9-547f-641b-29cd6b17b663@rock-chips.com>

? 2016/7/15 18:09, Matthias Brugger ??:
> This patch adds basic support for the Tronsmart orion r86 set-top-box.
>
> Signed-off-by: Matthias Brugger <mbrugger at suse.com>
> ---
> Changes since v2:
> - gpio-keys cleanpup
> - set sdmmc properties correctly

It seems there are two "cap-mmc-highspeed;" for emmc.

>
>  Documentation/devicetree/bindings/arm/rockchip.txt |   4 +
>  arch/arm64/boot/dts/rockchip/Makefile              |   1 +
>  .../boot/dts/rockchip/rk3368-orion-r68-meta.dts    | 385 +++++++++++++++++++++
>  3 files changed, 390 insertions(+)
>  create mode 100644 arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts
>
> diff --git a/Documentation/devicetree/bindings/arm/rockchip.txt b/Documentation/devicetree/bindings/arm/rockchip.txt
> index 715d960..1d5c65e0 100644
> --- a/Documentation/devicetree/bindings/arm/rockchip.txt
> +++ b/Documentation/devicetree/bindings/arm/rockchip.txt
> @@ -110,3 +110,7 @@ Rockchip platforms device tree bindings
>  - Rockchip RK3399 evb:
>      Required root node properties:
>        - compatible = "rockchip,rk3399-evb", "rockchip,rk3399";
> +
> +- Tronsmart Orion R68 Meta
> +    Required root node properties:
> +      - compatible = "tronsmart,orion-r68-meta", "rockchip,rk3368";
> diff --git a/arch/arm64/boot/dts/rockchip/Makefile b/arch/arm64/boot/dts/rockchip/Makefile
> index 7037a16..87669f6 100644
> --- a/arch/arm64/boot/dts/rockchip/Makefile
> +++ b/arch/arm64/boot/dts/rockchip/Makefile
> @@ -1,5 +1,6 @@
>  dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-evb-act8846.dtb
>  dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-geekbox.dtb
> +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-orion-r68-meta.dtb
>  dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-r88.dtb
>  dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-evb.dtb
>
> diff --git a/arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts b/arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts
> new file mode 100644
> index 0000000..d5e1af5
> --- /dev/null
> +++ b/arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts
> @@ -0,0 +1,385 @@
> +/*
> + * Copyright (c) 2016 Matthias Brugger <mbrugger at suse.com>
> + *
> + * This file is dual-licensed: you can use it either under the terms
> + * of the GPL or the X11 license, at your option. Note that this dual
> + * licensing only applies to this file, and not this project as a
> + * whole.
> + *
> + *  a) This file is free software; you can redistribute it and/or
> + *     modify it under the terms of the GNU General Public License as
> + *     published by the Free Software Foundation; either version 2 of the
> + *     License, or (at your option) any later version.
> + *
> + *     This file 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.
> + *
> + * Or, alternatively,
> + *
> + *  b) Permission is hereby granted, free of charge, to any person
> + *     obtaining a copy of this software and associated documentation
> + *     files (the "Software"), to deal in the Software without
> + *     restriction, including without limitation the rights to use,
> + *     copy, modify, merge, publish, distribute, sublicense, and/or
> + *     sell copies of the Software, and to permit persons to whom the
> + *     Software is furnished to do so, subject to the following
> + *     conditions:
> + *
> + *     The above copyright notice and this permission notice shall be
> + *     included in all copies or substantial portions of the Software.
> + *
> + *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
> + *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
> + *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
> + *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + *     OTHER DEALINGS IN THE SOFTWARE.
> + */
> +
> +/dts-v1/;
> +#include <dt-bindings/input/input.h>
> +#include "rk3368.dtsi"
> +
> +/ {
> +	model = "Rockchip Orion R68";
> +	compatible = "tronsmart,orion-r68-meta", "rockchip,rk3368";
> +
> +	chosen {
> +		stdout-path = "serial2:115200n8";
> +	};
> +
> +	memory {
> +		device_type = "memory";
> +		reg = <0x0 0x0 0x0 0x80000000>;
> +	};
> +
> +	emmc_pwrseq: emmc-pwrseq {
> +		compatible = "mmc-pwrseq-emmc";
> +		pinctrl-0 = <&emmc_reset>;
> +		pinctrl-names = "default";
> +		reset-gpios = <&gpio2 3 GPIO_ACTIVE_HIGH>;
> +	};
> +
> +	ext_gmac: external-gmac-clock {
> +		compatible = "fixed-clock";
> +		#clock-cells = <0>;
> +		clock-frequency = <125000000>;
> +		clock-output-names = "ext_gmac";
> +	};
> +
> +	keys: gpio-keys {
> +		compatible = "gpio-keys";
> +		pinctrl-names = "default";
> +		pinctrl-0 = <&pwr_key>;
> +
> +		power {
> +			wakeup-source;
> +			gpios = <&gpio0 5 GPIO_ACTIVE_HIGH>;
> +			label = "GPIO Power";
> +			linux,code = <KEY_POWER>;
> +		};
> +	};
> +
> +	leds: gpio-leds {
> +		compatible = "gpio-leds";
> +
> +		red {
> +			gpios = <&gpio3 29 GPIO_ACTIVE_HIGH>;
> +			label = "orion:red:led";
> +			pinctrl-names = "default";
> +			pinctrl-0 = <&led_ctl>;
> +			default-state = "on";
> +		};
> +
> +		blue {
> +			gpios = <&gpio0 12 GPIO_ACTIVE_HIGH>;
> +			label = "orion:blue:led";
> +			pinctrl-names = "default";
> +			pinctrl-0 = <&stby_pwren>;
> +			default-state = "off";
> +		};
> +	};
> +
> +	vcc_18: vcc18-regulator {
> +		compatible = "regulator-fixed";
> +		regulator-name = "vcc_18";
> +		regulator-min-microvolt = <1800000>;
> +		regulator-max-microvolt = <1800000>;
> +		regulator-always-on;
> +		regulator-boot-on;
> +		vin-supply = <&vcc_sys>;
> +	};
> +
> +	/* supplies both host and otg */
> +	vcc_host: vcc-host-regulator {
> +		compatible = "regulator-fixed";
> +		gpio = <&gpio0 4 GPIO_ACTIVE_LOW>;
> +		pinctrl-names = "default";
> +		pinctrl-0 = <&host_vbus_drv>;
> +		regulator-name = "vcc_host";
> +		regulator-always-on;
> +		regulator-boot-on;
> +		vin-supply = <&vcc_sys>;
> +	};
> +
> +	vccio_sd: vcc-io-sd-regulator {
> +		compatible = "regulator-fixed";
> +		regulator-name= "vccio_sd";
> +		regulator-min-microvolt = <1800000>;
> +		regulator-max-microvolt = <3300000>;
> +		regulator-always-on;
> +		regulator-boot-on;
> +		vin-supply = <&vcc_io>;
> +	};
> +
> +	vcc_sd: vcc-sd-regulator {
> +		compatible = "regulator-fixed";
> +		regulator-name = "vcc_sd";
> +		gpio = <&gpio3 11 GPIO_ACTIVE_LOW>;
> +		regulator-min-microvolt = <1800000>;
> +		regulator-max-microvolt = <3300000>;
> +		regulator-always-on;
> +		regulator-boot-on;
> +		vin-supply = <&vcc_io>;
> +	};
> +
> +	vcc_io: vcc-io-regulator {
> +		compatible = "regulator-fixed";
> +		regulator-name = "vcc_io";
> +		regulator-min-microvolt = <3300000>;
> +		regulator-max-microvolt = <3300000>;
> +		regulator-always-on;
> +		regulator-boot-on;
> +		vin-supply = <&vcc_sys>;
> +	};
> +
> +	vcc_lan: vcc-lan-regulator {
> +		compatible = "regulator-fixed";
> +		regulator-name = "vcc_lan";
> +		regulator-min-microvolt = <3300000>;
> +		regulator-max-microvolt = <3300000>;
> +		regulator-always-on;
> +		regulator-boot-on;
> +		vin-supply = <&vcc_io>;
> +	};
> +
> +	vcc_sys: vcc-sys-regulator {
> +		compatible = "regulator-fixed";
> +		regulator-name = "vcc_sys";
> +		regulator-min-microvolt = <5000000>;
> +		regulator-max-microvolt = <5000000>;
> +		regulator-always-on;
> +		regulator-boot-on;
> +	};
> +
> +	vccio_wl: vccio-wl-regulator {
> +		compatible = "regulator-fixed";
> +		regulator-name = "vccio_wl";
> +		regulator-min-microvolt = <3300000>;
> +		regulator-max-microvolt = <3300000>;
> +		regulator-always-on;
> +		regulator-boot-on;
> +		vin-supply = <&vcc_io>;
> +	};
> +
> +	vdd_10: vdd-10-regulator {
> +		compatible = "regulator-fixed";
> +		regulator-name = "vdd_10";
> +		regulator-min-microvolt = <1000000>;
> +		regulator-max-microvolt = <1000000>;
> +		regulator-always-on;
> +		regulator-boot-on;
> +		vin-supply = <&vcc_sys>;
> +	};
> +};
> +
> +&emmc {
> +	bus-width = <8>;
> +	cap-mmc-highspeed;
> +	disable-wp;
> +	mmc-pwrseq = <&emmc_pwrseq>;
> +	non-removable;
> +	num-slots = <1>;
> +	pinctrl-names = "default";
> +	pinctrl-0 = <&emmc_clk &emmc_cmd &emmc_bus8>;
> +	keep-power-in-suspend;
> +	cap-mmc-highspeed;
> +	mmc-hs200-1_2v;
> +	mmc-hs200-1_8v;
> +	status = "okay";
> +};
> +
> +&gmac {
> +	assigned-clocks = <&cru SCLK_MAC>;
> +	assigned-clock-parents = <&ext_gmac>;
> +	phy-supply = <&vcc_lan>;
> +	phy-mode = "rgmii";
> +	clock_in_out = "input";
> +	snps,reset-gpio = <&gpio3 12 0>;
> +	snps,reset-active-low;
> +	snps,reset-delays-us = <0 10000 1000000>;
> +	pinctrl-names = "default";
> +	pinctrl-0 = <&rgmii_pins>;
> +	tx_delay = <0x30>;
> +	rx_delay = <0x10>;
> +	status = "ok";
> +};
> +
> +&i2c0 {
> +	status = "okay";
> +
> +	vdd_cpu: syr827 at 40 {
> +		compatible = "silergy,syr827";
> +		reg = <0x40>;
> +		fcs,suspend-voltage-selector = <1>;
> +		regulator-name = "vdd_cpu";
> +		regulator-enable-ramp-delay = <300>;
> +		regulator-min-microvolt = <712500>;
> +		regulator-max-microvolt = <1500000>;
> +		regulator-ramp-delay = <8000>;
> +		regulator-always-on;
> +		regulator-boot-on;
> +		vin-supply = <&vcc_sys>;
> +	};
> +
> +	hym8563: hym8563 at 51 {
> +		compatible = "haoyu,hym8563";
> +		reg = <0x51>;
> +		#clock-cells = <0>;
> +		clock-frequency = <32768>;
> +		clock-output-names = "xin32k";
> +		/* rtc_int is not connected */
> +	};
> +};
> +
> +&pinctrl {
> +	pcfg_pull_none_drv_8ma: pcfg-pull-none-drv-8ma {
> +		bias-disable;
> +		drive-strength = <8>;
> +	};
> +
> +	pcfg_pull_up_drv_8ma: pcfg-pull-up-drv-8ma {
> +		bias-pull-up;
> +		drive-strength = <8>;
> +	};
> +
> +	emmc {
> +		emmc_bus8: emmc-bus8 {
> +			rockchip,pins = <1 18 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
> +					<1 19 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
> +					<1 20 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
> +					<1 21 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
> +					<1 22 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
> +					<1 23 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
> +					<1 24 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
> +					<1 25 RK_FUNC_2 &pcfg_pull_up_drv_8ma>;
> +		};
> +
> +		emmc-clk {
> +			rockchip,pins = <2 4 RK_FUNC_2 &pcfg_pull_none_drv_8ma>;
> +		};
> +
> +		emmc-cmd {
> +			rockchip,pins = <1 26 RK_FUNC_2 &pcfg_pull_up_drv_8ma>;
> +		};
> +
> +		emmc_reset: emmc-reset {
> +			rockchip,pins = <2 3 RK_FUNC_GPIO &pcfg_pull_none>;
> +		};
> +	};
> +
> +	keys {
> +		pwr_key: pwr-key {
> +			rockchip,pins = <0 5 RK_FUNC_GPIO &pcfg_pull_down>;
> +		};
> +	};
> +
> +	leds {
> +		stby_pwren: stby-pwren {
> +			rockchip,pins = <0 12 RK_FUNC_GPIO &pcfg_pull_none>;
> +		};
> +
> +		led_ctl: led-ctl {
> +			rockchip,pins = <3 29 RK_FUNC_GPIO &pcfg_pull_none>;
> +		};
> +	};
> +
> +	sdmmc {
> +		sdmmc_clk: sdmmc-clk {
> +			rockchip,pins = <2 9 RK_FUNC_1 &pcfg_pull_none_drv_8ma>;
> +		};
> +
> +		sdmmc_cmd: sdmmc-cmd {
> +			rockchip,pins = <2 10 RK_FUNC_1 &pcfg_pull_up_drv_8ma>;
> +		};
> +
> +		sdmmc_cd: sdmmc-cd {
> +			rockchip,pins = <2 11 RK_FUNC_1 &pcfg_pull_up_drv_8ma>;
> +		};
> +
> +		sdmmc_bus1: sdmmc-bus1 {
> +			rockchip,pins = <2 5 RK_FUNC_1 &pcfg_pull_up_drv_8ma>;
> +		};
> +
> +		sdmmc_bus4: sdmmc-bus4 {
> +			rockchip,pins = <2 5 RK_FUNC_1 &pcfg_pull_up_drv_8ma>,
> +					<2 6 RK_FUNC_1 &pcfg_pull_up_drv_8ma>,
> +					<2 7 RK_FUNC_1 &pcfg_pull_up_drv_8ma>,
> +					<2 8 RK_FUNC_1 &pcfg_pull_up_drv_8ma>;
> +		};
> +	};
> +
> +	usb {
> +		host_vbus_drv: host-vbus-drv {
> +			rockchip,pins = <0 4 RK_FUNC_GPIO &pcfg_pull_none>;
> +		};
> +	};
> +};
> +
> +&saradc {
> +	vref-supply = <&vcc_18>;
> +	status = "okay";
> +};
> +
> +&sdmmc {
> +	clock-frequency = <50000000>;
> +	clock-freq-min-max = <400000 50000000>;
> +	cap-sd-highspeed;
> +	card-detect-delay = <200>;
> +
> +	pinctrl-names = "default";
> +	pinctrl-0 = <&sdmmc_clk &sdmmc_cmd &sdmmc_cd &sdmmc_bus4>;
> +	bus-width = <4>;
> +	num-slots = <1>;
> +	keep-power-in-suspend;
> +
> +	vmmc-supply = <&vcc_sd>;
> +	vqmmc-supply = <&vccio_sd>;
> +	status = "okay";
> +};
> +
> +&uart2 {
> +	status = "okay";
> +};
> +
> +&uart4 {
> +	status = "okay";
> +	pinctrl-names = "default";
> +	pinctrl-0 = <&uart4_xfer>;
> +};
> +
> +&usb_host0_ehci {
> +	status = "okay";
> +};
> +
> +&usb_otg {
> +	status = "okay";
> +};
> +
> +&wdt {
> +	status = "okay";
> +};
>


-- 
Best Regards
Shawn Lin



From frank.wang at rock-chips.com  Mon Jul 18 03:02:28 2016
From: frank.wang at rock-chips.com (Frank Wang)
Date: Mon, 18 Jul 2016 18:02:28 +0800
Subject: [PATCH v7 2/2] phy: rockchip-inno-usb2: add a new driver for
 Rockchip usb2phy
In-Reply-To: <3635389.HlLpm27c0G@phil>
References: <1466505005-86122-1-git-send-email-frank.wang@rock-chips.com>
 <1466505005-86122-3-git-send-email-frank.wang@rock-chips.com>
 <3635389.HlLpm27c0G@phil>
Message-ID: <901bc2d0-34ac-4c18-8fd2-491b10a43579@rock-chips.com>

Hi Heiko,

On 2016/6/25 3:58, Heiko Stuebner wrote:
> Am Dienstag, 21. Juni 2016, 18:30:05 schrieb Frank Wang:
>> The newer SoCs (rk3366, rk3399) take a different usb-phy IP block
>> than rk3288 and before, and most of phy-related registers are also
>> different from the past, so a new phy driver is required necessarily.
>>
>> Signed-off-by: Frank Wang <frank.wang at rock-chips.com>
>> Suggested-by: Heiko Stuebner <heiko at sntech.de>
>> Suggested-by: Guenter Roeck <linux at roeck-us.net>
>> Suggested-by: Doug Anderson <dianders at chromium.org>
> still looks nice, so still
> Reviewed-by: Heiko Stuebner <heiko at sntech.de>
>
>

Currently, we have already prepared compatible for rk3399, however, I 
found these series of patch had not merged in, so shall I reopen it or 
commit a new one?

BR.
Frank





From heiko at sntech.de  Mon Jul 18 04:24:20 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Mon, 18 Jul 2016 13:24:20 +0200
Subject: [PATCH] ARM: dts: add rk3288-firefly-reload
In-Reply-To: <1468769937-23387-2-git-send-email-ayaka@soulik.info>
References: <5614243.tqTyZoc945@phil>
 <1468769937-23387-1-git-send-email-ayaka@soulik.info>
 <1468769937-23387-2-git-send-email-ayaka@soulik.info>
Message-ID: <1549971.ctkvOGm2xW@diego>

Hi Randy,

Am Sonntag, 17. Juli 2016, 23:38:57 schrieb Randy Li:
> The Firefly RK3288 Reload is a combination Firefly rk3288 core board
> with the Reload baseboard. Add a dtsi for the Firefly rk3288 core
> which can be included into the dts for the various baseboards
> in the future and dts for Reload base board.
> 
> Currently supported are serial console, wired networking, eMMC and
> SD storage, SPFIF, IR receiver, LEDs, SDIO wifi and USB. But only
> the OTG could work on the host mode now, the other USB host can't
> work now, additional patches are required.
> 
> Signed-off-by: Randy Li <ayaka at soulik.info>

looks mostly good, two small issues below. While I could replace the
io-domain node with the newer variant myself, the licensing issue will
require a resend anyway.

Please also carry over Rob's Ack in your resend as well.


> ---
>  Documentation/devicetree/bindings/arm/rockchip.txt |   4 +
>  arch/arm/boot/dts/Makefile                         |   1 +
>  arch/arm/boot/dts/rk3288-firefly-reload-core.dtsi  | 304
> ++++++++++++++++++++ arch/arm/boot/dts/rk3288-firefly-reload.dts        |
> 320 +++++++++++++++++++++ 4 files changed, 629 insertions(+)
>  create mode 100644 arch/arm/boot/dts/rk3288-firefly-reload-core.dtsi
>  create mode 100644 arch/arm/boot/dts/rk3288-firefly-reload.dts
> 
> diff --git a/Documentation/devicetree/bindings/arm/rockchip.txt
> b/Documentation/devicetree/bindings/arm/rockchip.txt index
> 715d960..3380212c 100644
> --- a/Documentation/devicetree/bindings/arm/rockchip.txt
> +++ b/Documentation/devicetree/bindings/arm/rockchip.txt
> @@ -31,6 +31,10 @@ Rockchip platforms device tree bindings
>      or
>        - compatible = "firefly,firefly-rk3288-beta", "rockchip,rk3288";
> 
> +- Firefly Firefly-RK3288 Reload board:
> +    Required root node properties:
> +      - compatible = "firefly,firefly-rk3288-reload", "rockchip,rk3288";
> +
>  - ChipSPARK PopMetal-RK3288 board:
>      Required root node properties:
>        - compatible = "chipspark,popmetal-rk3288", "rockchip,rk3288";
> diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
> index 414b427..8173e77 100644
> --- a/arch/arm/boot/dts/Makefile
> +++ b/arch/arm/boot/dts/Makefile
> @@ -606,6 +606,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += \
>  	rk3288-evb-rk808.dtb \
>  	rk3288-firefly-beta.dtb \
>  	rk3288-firefly.dtb \
> +	rk3288-firefly-reload.dtb \
>  	rk3288-miqi.dtb \
>  	rk3288-popmetal.dtb \
>  	rk3288-r89.dtb \
> diff --git a/arch/arm/boot/dts/rk3288-firefly-reload-core.dtsi
> b/arch/arm/boot/dts/rk3288-firefly-reload-core.dtsi new file mode 100644
> index 0000000..039ae16
> --- /dev/null
> +++ b/arch/arm/boot/dts/rk3288-firefly-reload-core.dtsi
> @@ -0,0 +1,304 @@
> +/*
> + * Device tree file for Firefly Rockchip RK3288 Core board
> + * Copyright (c) 2016 Randy Li <ayaka at soulik.info>
> + *
> + * Licensed under GPLv2 or later.
> + */

please allow licensing under GPL and MIT licenses ... see license headers
of the other Rockchip boards. Same for the dts file below.


> +#include <dt-bindings/input/input.h>
> +#include "rk3288.dtsi"
> +
> +/ {
> +	memory {
> +		device_type = "memory";
> +		reg = <0 0x80000000>;
> +	};
> +
> +	dovdd_1v8: dovdd-1v8-regulator {
> +		compatible = "regulator-fixed";
> +		regulator-name = "dovdd_1v8";
> +		regulator-min-microvolt = <1800000>;
> +		regulator-max-microvolt = <1800000>;
> +		vin-supply = <&vcc28_dvp>;
> +	};
> +
> +	ext_gmac: external-gmac-clock {
> +		compatible = "fixed-clock";
> +		#clock-cells = <0>;
> +		clock-frequency = <125000000>;
> +		clock-output-names = "ext_gmac";
> +	};
> +
> +	io_domains: io-domains {
> +		compatible = "rockchip,rk3288-io-voltage-domain";
> +		rockchip,grf = <&grf>;
> +
> +		audio-supply = <&vcca_33>;
> +		bb-supply = <&vcc_io>;
> +		dvp-supply = <&dovdd_1v8>;
> +		flash0-supply = <&vcc_flash>;
> +		flash1-supply = <&vcc_lan>;
> +		gpio30-supply = <&vcc_io>;
> +		gpio1830-supply = <&vcc_io>;
> +		lcdc-supply = <&vcc_io>;
> +		sdcard-supply = <&vccio_sd>;
> +		wifi-supply = <&vccio_wl>;
> +	};

io-domains are now a sub-device of the GRF, see
https://git.kernel.org/cgit/linux/kernel/git/mmind/linux-rockchip.git/commit/?h=v4.8-armsoc/dts32&id=3445b2fae55f1a232e6e4843f4f74de074d8c07f

Heiko


From heiko at sntech.de  Mon Jul 18 04:28:00 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Mon, 18 Jul 2016 13:28 +0200
Subject: [PATCH v7 2/2] phy: rockchip-inno-usb2: add a new driver for
 Rockchip usb2phy
In-Reply-To: <901bc2d0-34ac-4c18-8fd2-491b10a43579@rock-chips.com>
References: <1466505005-86122-1-git-send-email-frank.wang@rock-chips.com>
 <3635389.HlLpm27c0G@phil>
 <901bc2d0-34ac-4c18-8fd2-491b10a43579@rock-chips.com>
Message-ID: <1995194.5TyLgBic8c@diego>

Hi Frank,

Am Montag, 18. Juli 2016, 18:02:28 schrieb Frank Wang:
> On 2016/6/25 3:58, Heiko Stuebner wrote:
> > Am Dienstag, 21. Juni 2016, 18:30:05 schrieb Frank Wang:
> >> The newer SoCs (rk3366, rk3399) take a different usb-phy IP block
> >> than rk3288 and before, and most of phy-related registers are also
> >> different from the past, so a new phy driver is required necessarily.
> >> 
> >> Signed-off-by: Frank Wang <frank.wang at rock-chips.com>
> >> Suggested-by: Heiko Stuebner <heiko at sntech.de>
> >> Suggested-by: Guenter Roeck <linux at roeck-us.net>
> >> Suggested-by: Doug Anderson <dianders at chromium.org>
> > 
> > still looks nice, so still
> > Reviewed-by: Heiko Stuebner <heiko at sntech.de>
> 
> Currently, we have already prepared compatible for rk3399, however, I
> found these series of patch had not merged in, so shall I reopen it or
> commit a new one?

the phy tree from Kishon feeds into the usb tree and the merge for 4.8 was 
done on friday it seems. So it looks like the usb2 phy will need to wait until 
after the merge window anyway, so you could include the rk3399 support already 
and resend the series, so everything is ready then.


Heiko


From zyw at rock-chips.com  Mon Jul 18 07:34:34 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Mon, 18 Jul 2016 22:34:34 +0800
Subject: [PATCH] ASoC: rockchip: correct the spdif clk
Message-ID: <1468852474-15735-1-git-send-email-zyw@rock-chips.com>

The spdif mclk should be 128 times of sample rate, and there is a
internal divider, the real rate of spdif mclk is mclk / (div + 1).
Hence, the original driver always get the good frequency for
48000/96000/44100/192000. But for 32000, the mclk is incorrect,
it should be 32000*128, but get 48000*128. Do not use the internal
divider here, just set all mclk to 128 * sample rate directly.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>

---

 sound/soc/rockchip/rockchip_spdif.c | 17 +----------------
 1 file changed, 1 insertion(+), 16 deletions(-)

diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c
index 100781e..4ca2657 100644
--- a/sound/soc/rockchip/rockchip_spdif.c
+++ b/sound/soc/rockchip/rockchip_spdif.c
@@ -101,21 +101,7 @@ static int rk_spdif_hw_params(struct snd_pcm_substream *substream,
 	int ret;
 
 	srate = params_rate(params);
-	switch (srate) {
-	case 32000:
-	case 48000:
-	case 96000:
-		mclk = 96000 * 128; /* 12288000 hz */
-		break;
-	case 44100:
-		mclk = 44100 * 256; /* 11289600 hz */
-		break;
-	case 192000:
-		mclk = 192000 * 128; /* 24576000 hz */
-		break;
-	default:
-		return -EINVAL;
-	}
+	mclk = srate * 128;
 
 	switch (params_format(params)) {
 	case SNDRV_PCM_FORMAT_S16_LE:
@@ -139,7 +125,6 @@ static int rk_spdif_hw_params(struct snd_pcm_substream *substream,
 		return ret;
 	}
 
-	val |= SPDIF_CFGR_CLK_DIV(mclk/(srate * 256));
 	ret = regmap_update_bits(spdif->regmap, SPDIF_CFGR,
 		SPDIF_CFGR_CLK_DIV_MASK | SPDIF_CFGR_HALFWORD_ENABLE |
 		SDPIF_CFGR_VDW_MASK,
-- 
2.6.3



From ayaka at soulik.info  Mon Jul 18 08:32:31 2016
From: ayaka at soulik.info (Randy Li)
Date: Mon, 18 Jul 2016 23:32:31 +0800
Subject: [PATCH v2] ARM: dts: add rk3288-firefly-reload
In-Reply-To: <1549971.ctkvOGm2xW@diego>
References: <1549971.ctkvOGm2xW@diego>
Message-ID: <1468855952-2006-1-git-send-email-ayaka@soulik.info>

Hi Heiko:

I use GPLv2 because of it looks more shorter, anyway I am back to allow
X11 now. The modification request of io-domain have been included.

Thank you for reviewing.

Randy Li (1):
  ARM: dts: add rk3288-firefly-reload

 Documentation/devicetree/bindings/arm/rockchip.txt |   4 +
 arch/arm/boot/dts/Makefile                         |   1 +
 arch/arm/boot/dts/rk3288-firefly-reload-core.dtsi  | 339 ++++++++++++++++++++
 arch/arm/boot/dts/rk3288-firefly-reload.dts        | 356 +++++++++++++++++++++
 4 files changed, 700 insertions(+)
 create mode 100644 arch/arm/boot/dts/rk3288-firefly-reload-core.dtsi
 create mode 100644 arch/arm/boot/dts/rk3288-firefly-reload.dts

-- 
2.7.4



From ayaka at soulik.info  Mon Jul 18 08:32:32 2016
From: ayaka at soulik.info (Randy Li)
Date: Mon, 18 Jul 2016 23:32:32 +0800
Subject: [PATCH] ARM: dts: add rk3288-firefly-reload
In-Reply-To: <1468855952-2006-1-git-send-email-ayaka@soulik.info>
References: <1549971.ctkvOGm2xW@diego>
 <1468855952-2006-1-git-send-email-ayaka@soulik.info>
Message-ID: <1468855952-2006-2-git-send-email-ayaka@soulik.info>

The Firefly RK3288 Reload is a combination Firefly rk3288 core board
with the Reload baseboard. Add a dtsi for the Firefly rk3288 core
which can be included into the dts for the various baseboards
in the future and dts for Reload base board.

Currently supported are serial console, wired networking, eMMC and
SD storage, SPFIF, IR receiver, LEDs, SDIO wifi and USB. But only
the OTG could work on the host mode now, the other USB host can't
work now, additional patches are required.

Signed-off-by: Randy Li <ayaka at soulik.info>
Acked-by: Rob Herring <robh at kernel.org>
---
 Documentation/devicetree/bindings/arm/rockchip.txt |   4 +
 arch/arm/boot/dts/Makefile                         |   1 +
 arch/arm/boot/dts/rk3288-firefly-reload-core.dtsi  | 339 ++++++++++++++++++++
 arch/arm/boot/dts/rk3288-firefly-reload.dts        | 356 +++++++++++++++++++++
 4 files changed, 700 insertions(+)
 create mode 100644 arch/arm/boot/dts/rk3288-firefly-reload-core.dtsi
 create mode 100644 arch/arm/boot/dts/rk3288-firefly-reload.dts

diff --git a/Documentation/devicetree/bindings/arm/rockchip.txt b/Documentation/devicetree/bindings/arm/rockchip.txt
index 715d960..3380212c 100644
--- a/Documentation/devicetree/bindings/arm/rockchip.txt
+++ b/Documentation/devicetree/bindings/arm/rockchip.txt
@@ -31,6 +31,10 @@ Rockchip platforms device tree bindings
     or
       - compatible = "firefly,firefly-rk3288-beta", "rockchip,rk3288";
 
+- Firefly Firefly-RK3288 Reload board:
+    Required root node properties:
+      - compatible = "firefly,firefly-rk3288-reload", "rockchip,rk3288";
+
 - ChipSPARK PopMetal-RK3288 board:
     Required root node properties:
       - compatible = "chipspark,popmetal-rk3288", "rockchip,rk3288";
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index 414b427..8173e77 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -606,6 +606,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += \
 	rk3288-evb-rk808.dtb \
 	rk3288-firefly-beta.dtb \
 	rk3288-firefly.dtb \
+	rk3288-firefly-reload.dtb \
 	rk3288-miqi.dtb \
 	rk3288-popmetal.dtb \
 	rk3288-r89.dtb \
diff --git a/arch/arm/boot/dts/rk3288-firefly-reload-core.dtsi b/arch/arm/boot/dts/rk3288-firefly-reload-core.dtsi
new file mode 100644
index 0000000..5e5a348
--- /dev/null
+++ b/arch/arm/boot/dts/rk3288-firefly-reload-core.dtsi
@@ -0,0 +1,339 @@
+/*
+ * Device tree file for Firefly Rockchip RK3288 Core board
+ * Copyright (c) 2016 Randy Li <ayaka at soulik.info>
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of the
+ *     License, or (at your option) any later version.
+ *
+ *     This file 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.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <dt-bindings/input/input.h>
+#include "rk3288.dtsi"
+
+/ {
+	memory {
+		device_type = "memory";
+		reg = <0 0x80000000>;
+	};
+
+	dovdd_1v8: dovdd-1v8-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "dovdd_1v8";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+		vin-supply = <&vcc28_dvp>;
+	};
+
+	ext_gmac: external-gmac-clock {
+		compatible = "fixed-clock";
+		#clock-cells = <0>;
+		clock-frequency = <125000000>;
+		clock-output-names = "ext_gmac";
+	};
+
+	vbat_wl: vcc_sys: vsys-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_sys";
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+		regulator-always-on;
+		regulator-boot-on;
+	};
+
+	vcc_flash: flash-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_flash";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+		vin-supply = <&vcc_io>;
+	};
+
+	vcc_5v: usb-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_5v";
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+		regulator-always-on;
+		regulator-boot-on;
+		vin-supply = <&vcc_sys>;
+	};
+
+};
+
+&cpu0 {
+	cpu0-supply = <&vdd_cpu>;
+};
+
+&emmc {
+	bus-width = <8>;
+	cap-mmc-highspeed;
+	mmc-ddr-1_8v;
+	mmc-hs200-1_8v;
+	disable-wp;
+	non-removable;
+	num-slots = <1>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&emmc_clk>, <&emmc_cmd>, <&emmc_pwr>, <&emmc_bus8>;
+	vmmc-supply = <&vcc_io>;
+	vqmmc-supply = <&vcc_flash>;
+	status = "okay";
+};
+
+&gmac {
+	assigned-clocks = <&cru SCLK_MAC>;
+	assigned-clock-parents = <&ext_gmac>;
+	clock_in_out = "input";
+	pinctrl-names = "default";
+	pinctrl-0 = <&rgmii_pins>, <&phy_rst>, <&phy_pmeb>, <&phy_int>;
+	phy-supply = <&vcc_lan>;
+	phy-mode = "rgmii";
+	snps,reset-active-low;
+	snps,reset-delays-us = <0 10000 1000000>;
+	snps,reset-gpio = <&gpio4 8 GPIO_ACTIVE_LOW>;
+	tx_delay = <0x30>;
+	rx_delay = <0x10>;
+	status = "ok";
+};
+
+&i2c0 {
+	clock-frequency = <400000>;
+	status = "okay";
+
+	vdd_cpu: syr827 at 40 {
+		compatible = "silergy,syr827";
+		fcs,suspend-voltage-selector = <1>;
+		reg = <0x40>;
+		regulator-name = "vdd_cpu";
+		regulator-min-microvolt = <850000>;
+		regulator-max-microvolt = <1350000>;
+		regulator-always-on;
+		regulator-boot-on;
+		regulator-enable-ramp-delay = <300>;
+		regulator-ramp-delay = <8000>;
+		vin-supply = <&vcc_sys>;
+	};
+
+	vdd_gpu: syr828 at 41 {
+		compatible = "silergy,syr828";
+		fcs,suspend-voltage-selector = <1>;
+		reg = <0x41>;
+		regulator-name = "vdd_gpu";
+		regulator-min-microvolt = <850000>;
+		regulator-max-microvolt = <1350000>;
+		regulator-always-on;
+		vin-supply = <&vcc_sys>;
+	};
+
+	act8846: act8846 at 5a {
+		compatible = "active-semi,act8846";
+		reg = <0x5a>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pmic_vsel>, <&pwr_hold>;
+		system-power-controller;
+
+		vp1-supply = <&vcc_sys>;
+		vp2-supply = <&vcc_sys>;
+		vp3-supply = <&vcc_sys>;
+		vp4-supply = <&vcc_sys>;
+		inl1-supply = <&vcc_sys>;
+		inl2-supply = <&vcc_sys>;
+		inl3-supply = <&vcc_20>;
+
+		regulators {
+			vcc_ddr: REG1 {
+				regulator-name = "vcc_ddr";
+				regulator-min-microvolt = <1200000>;
+				regulator-max-microvolt = <1200000>;
+				regulator-always-on;
+			};
+
+			vcc_io: REG2 {
+				regulator-name = "vcc_io";
+				regulator-min-microvolt = <3300000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-always-on;
+			};
+
+			vdd_log: REG3 {
+				regulator-name = "vdd_log";
+				regulator-min-microvolt = <1100000>;
+				regulator-max-microvolt = <1100000>;
+				regulator-always-on;
+			};
+
+			vcc_20: REG4 {
+				regulator-name = "vcc_20";
+				regulator-min-microvolt = <2000000>;
+				regulator-max-microvolt = <2000000>;
+				regulator-always-on;
+			};
+
+			vccio_sd: REG5 {
+				regulator-name = "vccio_sd";
+				regulator-min-microvolt = <3300000>;
+				regulator-max-microvolt = <3300000>;
+				regulator-always-on;
+			};
+
+			vdd10_lcd: REG6 {
+				regulator-name = "vdd10_lcd";
+				regulator-min-microvolt = <1000000>;
+				regulator-max-microvolt = <1000000>;
+				regulator-always-on;
+			};
+
+			vcca_33: REG7  {
+				regulator-name = "vcca_33";
+				regulator-min-microvolt = <3300000>;
+				regulator-max-microvolt = <3300000>;
+			};
+
+			vcc_lan: REG8 {
+				regulator-name = "vcca_lan";
+				regulator-min-microvolt = <3300000>;
+				regulator-max-microvolt = <3300000>;
+			};
+
+			vccio_pmu: REG9 {
+				regulator-name = "vccio_pmu";
+				regulator-min-microvolt = <3300000>;
+				regulator-max-microvolt = <3300000>;
+			};
+
+			vdd_10: REG10 {
+				regulator-name = "vdd_10";
+				regulator-min-microvolt = <1000000>;
+				regulator-max-microvolt = <1000000>;
+				regulator-always-on;
+			};
+
+			vccio_wl: vcc_18: REG11 {
+				regulator-name = "vcc_18";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-always-on;
+			};
+
+			vcc18_lcd: REG12 {
+				regulator-name = "vcc18_lcd";
+				regulator-min-microvolt = <1800000>;
+				regulator-max-microvolt = <1800000>;
+				regulator-always-on;
+			};
+		};
+	};
+};
+
+&pinctrl {
+	pcfg_output_high: pcfg-output-high {
+		output-high;
+	};
+
+	pcfg_output_low: pcfg-output-low {
+		output-low;
+	};
+
+	pcfg_pull_up_drv_12ma: pcfg-pull-up-drv-12ma {
+		bias-pull-up;
+		drive-strength = <12>;
+	};
+
+	act8846 {
+		pwr_hold: pwr-hold {
+			rockchip,pins = <0 1 RK_FUNC_GPIO &pcfg_output_high>;
+		};
+
+		pmic_vsel: pmic-vsel {
+			rockchip,pins = <7 14 RK_FUNC_GPIO &pcfg_output_low>;
+		};
+	};
+
+	gmac {
+		phy_int: phy-int {
+			rockchip,pins = <0 9 RK_FUNC_GPIO &pcfg_pull_up>;
+		};
+
+		phy_pmeb: phy-pmeb {
+			rockchip,pins = <0 8 RK_FUNC_GPIO &pcfg_pull_up>;
+		};
+
+		phy_rst: phy-rst {
+			rockchip,pins = <4 8 RK_FUNC_GPIO &pcfg_output_high>;
+		};
+	};
+};
+
+&tsadc {
+	rockchip,hw-tshut-mode = <0>;
+	rockchip,hw-tshut-polarity = <0>;
+	status = "okay";
+};
+
+&vopb {
+	status = "okay";
+};
+
+&vopb_mmu {
+	status = "okay";
+};
+
+&vopl {
+	status = "okay";
+};
+
+&vopl_mmu {
+	status = "okay";
+};
+
+&wdt {
+	status = "okay";
+};
+
+&io_domains {
+	status = "okay";
+
+	audio-supply = <&vcca_33>;
+	bb-supply = <&vcc_io>;
+	dvp-supply = <&dovdd_1v8>;
+	flash0-supply = <&vcc_flash>;
+	flash1-supply = <&vcc_lan>;
+	gpio30-supply = <&vcc_io>;
+	gpio1830-supply = <&vcc_io>;
+	lcdc-supply = <&vcc_io>;
+	sdcard-supply = <&vccio_sd>;
+	wifi-supply = <&vccio_wl>;
+};
diff --git a/arch/arm/boot/dts/rk3288-firefly-reload.dts b/arch/arm/boot/dts/rk3288-firefly-reload.dts
new file mode 100644
index 0000000..43145d1
--- /dev/null
+++ b/arch/arm/boot/dts/rk3288-firefly-reload.dts
@@ -0,0 +1,356 @@
+/*
+ * Device tree file for Firefly Rockchip RK3288 Core board
+ * Copyright (c) 2016 Randy Li <ayaka at soulik.info>
+ *
+ * This file is dual-licensed: you can use it either under the terms
+ * of the GPL or the X11 license, at your option. Note that this dual
+ * licensing only applies to this file, and not this project as a
+ * whole.
+ *
+ *  a) This file is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of the
+ *     License, or (at your option) any later version.
+ *
+ *     This file 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.
+ *
+ * Or, alternatively,
+ *
+ *  b) Permission is hereby granted, free of charge, to any person
+ *     obtaining a copy of this software and associated documentation
+ *     files (the "Software"), to deal in the Software without
+ *     restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or
+ *     sell copies of the Software, and to permit persons to whom the
+ *     Software is furnished to do so, subject to the following
+ *     conditions:
+ *
+ *     The above copyright notice and this permission notice shall be
+ *     included in all copies or substantial portions of the Software.
+ *
+ *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ *     OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/dts-v1/;
+#include "rk3288-firefly-reload-core.dtsi"
+
+/ {
+	model = "Firefly-RK3288-reload";
+	compatible = "firefly,firefly-rk3288-reload", "rockchip,rk3288";
+
+	leds {
+		compatible = "gpio-leds";
+
+		power {
+			gpios = <&gpio8 2 GPIO_ACTIVE_LOW>;
+			label = "firefly:blue:power";
+			pinctrl-names = "default";
+			pinctrl-0 = <&power_led>;
+			panic-indicator;
+		};
+
+		work {
+			gpios = <&gpio8 1 GPIO_ACTIVE_LOW>;
+			label = "firefly:blue:user";
+			linux,default-trigger = "rc-feedback";
+			pinctrl-names = "default";
+			pinctrl-0 = <&work_led>;
+		};
+	};
+	
+	vcc_sd: sdmmc-regulator {
+		compatible = "regulator-fixed";
+		gpio = <&gpio7 11 GPIO_ACTIVE_LOW>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&sdmmc_pwr>;
+		regulator-name = "vcc_sd";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		startup-delay-us = <100000>;
+		vin-supply = <&vcc_io>;
+	};
+
+	sdio_pwrseq: sdio-pwrseq {
+		compatible = "mmc-pwrseq-simple";
+		clocks = <&hym8563>;
+		clock-names = "ext_clock";
+		pinctrl-names = "default";
+		pinctrl-0 = <&wifi_enable>;
+		reset-gpios = <&gpio4 28 GPIO_ACTIVE_LOW>;
+	};
+
+	vcc_host_5v: usb-host-regulator {
+		compatible = "regulator-fixed";
+		enable-active-high;
+		gpio = <&gpio0 14 GPIO_ACTIVE_HIGH>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&host_vbus_drv>;
+		regulator-name = "vcc_host_5v";
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+		regulator-always-on;
+		vin-supply = <&vcc_5v>;
+	};
+
+	vcc_otg_5v: usb-otg-regulator {
+		compatible = "regulator-fixed";
+		enable-active-high;
+		gpio = <&gpio0 12 GPIO_ACTIVE_HIGH>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&otg_vbus_drv>;
+		regulator-name = "vcc_otg_5v";
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+		regulator-always-on;
+		vin-supply = <&vcc_5v>;
+	};
+
+	/*
+	 * A TT8142 creates both dovdd_1v8 and vcc28_dvp, controlled
+	 * by the dvp_pwr pin.
+	 */
+	vcc28_dvp: vcc28-dvp-regulator {
+		compatible = "regulator-fixed";
+		enable-active-high;
+		gpio = <&gpio0 11 GPIO_ACTIVE_HIGH>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&dvp_pwr>;
+		regulator-name = "vcc28_dvp";
+		regulator-min-microvolt = <2800000>;
+		regulator-max-microvolt = <2800000>;
+		regulator-always-on;
+		vin-supply = <&vcc_io>;
+	};
+
+	ir-receiver {
+		compatible = "gpio-ir-receiver";
+		gpios = <&gpio7 0 GPIO_ACTIVE_LOW>;
+	};
+
+	gpio-keys {
+		compatible = "gpio-keys";
+
+		power {
+			wakeup-source;
+			gpios = <&gpio0 5 GPIO_ACTIVE_LOW>;
+			label = "GPIO Power";
+			linux,code = <KEY_POWER>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&pwr_key>;
+		};
+	};
+
+	sound {
+		compatible = "simple-audio-card";
+		simple-audio-card,name = "SPDIF";
+		simple-audio-card,dai-link at 1 {  /* S/PDIF - S/PDIF */
+			cpu { sound-dai = <&spdif>; };
+			codec { sound-dai = <&spdif_out>; };
+		};
+	};
+
+	spdif_out: spdif-out {
+		compatible = "linux,spdif-dit";
+		#sound-dai-cells = <0>;
+	};
+};
+
+&i2c0 {
+	hym8563: hym8563 at 51 {
+		compatible = "haoyu,hym8563";
+		reg = <0x51>;
+		#clock-cells = <0>;
+		clock-frequency = <32768>;
+		clock-output-names = "xin32k";
+		interrupt-parent = <&gpio7>;
+		interrupts = <4 IRQ_TYPE_EDGE_FALLING>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&rtc_int>;
+	};
+};
+
+&i2c2 {
+	status = "okay";
+
+	codec: es8328 at 10 {
+		compatible = "everest,es8328";
+		DVDD-supply = <&vcca_33>;
+		AVDD-supply = <&vcca_33>;
+		PVDD-supply = <&vcca_33>;
+		HPVDD-supply = <&vcca_33>;
+		clocks = <&cru HCLK_I2S0>, <&cru SCLK_I2S0>;
+		clock-names = "i2s_hclk", "i2s_clk";
+		reg = <0x10>;
+	};
+};
+
+&i2s {
+	status = "okay";
+};
+
+&sdmmc {
+	bus-width = <4>;
+	cap-mmc-highspeed;
+	cap-sd-highspeed;
+	sd-uhs-sdr12;
+	sd-uhs-sdr25;
+	sd-uhs-sdr50;
+	sd-uhs-sdr104;
+	sd-uhs-ddr50;
+	card-detect-delay = <200>;
+	disable-wp;
+	num-slots = <1>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&sdmmc_clk>, <&sdmmc_cmd>, <&sdmmc_cd>, <&sdmmc_bus4>;
+	vmmc-supply = <&vcc_sd>;
+	vqmmc-supply = <&vccio_sd>;
+	status = "okay";
+};
+
+&sdio0 {
+	bus-width = <4>;
+	cap-sd-highspeed;
+	sd-uhs-sdr12;
+	sd-uhs-sdr25;
+	sd-uhs-sdr50;
+	sd-uhs-ddr50;
+	cap-sdio-irq;
+	disable-wp;
+	mmc-pwrseq = <&sdio_pwrseq>;
+	non-removable;
+	num-slots = <1>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&sdio0_bus4>, <&sdio0_cmd>, <&sdio0_clk>, <&sdio0_int>;
+	vmmc-supply = <&vbat_wl>;
+	vqmmc-supply = <&vccio_wl>;
+	status = "okay";
+};
+
+&uart0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart0_xfer>, <&uart0_cts>, <&uart0_rts>;
+	status = "okay";
+};
+
+&uart1 {
+	status = "okay";
+};
+
+&uart2 {
+	status = "okay";
+};
+
+&uart3 {
+	status = "okay";
+};
+
+&usbphy {
+	status = "okay";
+};
+
+&usb_host1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&usbhub_rst>;
+	status = "okay";
+};
+
+&usb_otg {
+	status = "okay";
+};
+
+&spdif {
+	status = "okay";
+};
+
+&pinctrl {
+	ir {
+		ir_int: ir-int {
+			rockchip,pins = <7 0 RK_FUNC_GPIO &pcfg_pull_up>;
+		};
+	};
+
+	dvp {
+		dvp_pwr: dvp-pwr {
+			rockchip,pins = <0 11 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+
+	hym8563 {
+		rtc_int: rtc-int {
+			rockchip,pins = <7 4 RK_FUNC_GPIO &pcfg_pull_up>;
+		};
+	};
+
+	keys {
+		pwr_key: pwr-key {
+			rockchip,pins = <0 5 RK_FUNC_GPIO &pcfg_pull_up>;
+		};
+	};
+
+	leds {
+		power_led: power-led {
+			rockchip,pins = <8 2 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+
+		work_led: work-led {
+			rockchip,pins = <8 1 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+
+	sdmmc {
+		/*
+		 * Default drive strength isn't enough to achieve even
+		 * high-speed mode on firefly board so bump up to 12ma.
+		 */
+		sdmmc_bus4: sdmmc-bus4 {
+			rockchip,pins = <6 16 RK_FUNC_1 &pcfg_pull_up_drv_12ma>,
+					<6 17 RK_FUNC_1 &pcfg_pull_up_drv_12ma>,
+					<6 18 RK_FUNC_1 &pcfg_pull_up_drv_12ma>,
+					<6 19 RK_FUNC_1 &pcfg_pull_up_drv_12ma>;
+		};
+
+		sdmmc_clk: sdmmc-clk {
+			rockchip,pins = <6 20 RK_FUNC_1 &pcfg_pull_none_12ma>;
+		};
+
+		sdmmc_cmd: sdmmc-cmd {
+			rockchip,pins = <6 21 RK_FUNC_1 &pcfg_pull_up_drv_12ma>;
+		};
+
+		sdmmc_pwr: sdmmc-pwr {
+			rockchip,pins = <7 11 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+
+	sdio {
+		wifi_enable: wifi-enable {
+			rockchip,pins = <4 28 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+
+	usb_host {
+		host_vbus_drv: host-vbus-drv {
+			rockchip,pins = <0 14 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+
+		usbhub_rst: usbhub-rst {
+			rockchip,pins = <8 3 RK_FUNC_GPIO &pcfg_output_high>;
+		};
+	};
+
+	usb_otg {
+		otg_vbus_drv: otg-vbus-drv {
+			rockchip,pins = <0 12 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+};
-- 
2.7.4



From matthias.bgg at gmail.com  Mon Jul 18 09:24:11 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Mon, 18 Jul 2016 18:24:11 +0200
Subject: [PATCH v3] arm64: dts: rockchip: Add basic support for orion-r68
In-Reply-To: <24940135-f3a9-547f-641b-29cd6b17b663@rock-chips.com>
References: <1468577391-5096-1-git-send-email-matthias.bgg@gmail.com>
 <24940135-f3a9-547f-641b-29cd6b17b663@rock-chips.com>
Message-ID: <56da15b4-cb95-2bcf-ab62-bb4b74a48889@gmail.com>



On 07/18/2016 03:13 AM, Shawn Lin wrote:
> ? 2016/7/15 18:09, Matthias Brugger ??:
>> This patch adds basic support for the Tronsmart orion r86 set-top-box.
>>
>> Signed-off-by: Matthias Brugger <mbrugger at suse.com>
>> ---
>> Changes since v2:
>> - gpio-keys cleanpup
>> - set sdmmc properties correctly
>
> It seems there are two "cap-mmc-highspeed;" for emmc.
>

True, I will fix it in the next version.

Thanks,
Matthias

>>
>>  Documentation/devicetree/bindings/arm/rockchip.txt |   4 +
>>  arch/arm64/boot/dts/rockchip/Makefile              |   1 +
>>  .../boot/dts/rockchip/rk3368-orion-r68-meta.dts    | 385
>> +++++++++++++++++++++
>>  3 files changed, 390 insertions(+)
>>  create mode 100644
>> arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts
>>
>> diff --git a/Documentation/devicetree/bindings/arm/rockchip.txt
>> b/Documentation/devicetree/bindings/arm/rockchip.txt
>> index 715d960..1d5c65e0 100644
>> --- a/Documentation/devicetree/bindings/arm/rockchip.txt
>> +++ b/Documentation/devicetree/bindings/arm/rockchip.txt
>> @@ -110,3 +110,7 @@ Rockchip platforms device tree bindings
>>  - Rockchip RK3399 evb:
>>      Required root node properties:
>>        - compatible = "rockchip,rk3399-evb", "rockchip,rk3399";
>> +
>> +- Tronsmart Orion R68 Meta
>> +    Required root node properties:
>> +      - compatible = "tronsmart,orion-r68-meta", "rockchip,rk3368";
>> diff --git a/arch/arm64/boot/dts/rockchip/Makefile
>> b/arch/arm64/boot/dts/rockchip/Makefile
>> index 7037a16..87669f6 100644
>> --- a/arch/arm64/boot/dts/rockchip/Makefile
>> +++ b/arch/arm64/boot/dts/rockchip/Makefile
>> @@ -1,5 +1,6 @@
>>  dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-evb-act8846.dtb
>>  dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-geekbox.dtb
>> +dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-orion-r68-meta.dtb
>>  dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3368-r88.dtb
>>  dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-evb.dtb
>>
>> diff --git a/arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts
>> b/arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts
>> new file mode 100644
>> index 0000000..d5e1af5
>> --- /dev/null
>> +++ b/arch/arm64/boot/dts/rockchip/rk3368-orion-r68-meta.dts
>> @@ -0,0 +1,385 @@
>> +/*
>> + * Copyright (c) 2016 Matthias Brugger <mbrugger at suse.com>
>> + *
>> + * This file is dual-licensed: you can use it either under the terms
>> + * of the GPL or the X11 license, at your option. Note that this dual
>> + * licensing only applies to this file, and not this project as a
>> + * whole.
>> + *
>> + *  a) This file is free software; you can redistribute it and/or
>> + *     modify it under the terms of the GNU General Public License as
>> + *     published by the Free Software Foundation; either version 2 of
>> the
>> + *     License, or (at your option) any later version.
>> + *
>> + *     This file 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.
>> + *
>> + * Or, alternatively,
>> + *
>> + *  b) Permission is hereby granted, free of charge, to any person
>> + *     obtaining a copy of this software and associated documentation
>> + *     files (the "Software"), to deal in the Software without
>> + *     restriction, including without limitation the rights to use,
>> + *     copy, modify, merge, publish, distribute, sublicense, and/or
>> + *     sell copies of the Software, and to permit persons to whom the
>> + *     Software is furnished to do so, subject to the following
>> + *     conditions:
>> + *
>> + *     The above copyright notice and this permission notice shall be
>> + *     included in all copies or substantial portions of the Software.
>> + *
>> + *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
>> + *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
>> + *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
>> + *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
>> + *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
>> + *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
>> + *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
>> + *     OTHER DEALINGS IN THE SOFTWARE.
>> + */
>> +
>> +/dts-v1/;
>> +#include <dt-bindings/input/input.h>
>> +#include "rk3368.dtsi"
>> +
>> +/ {
>> +    model = "Rockchip Orion R68";
>> +    compatible = "tronsmart,orion-r68-meta", "rockchip,rk3368";
>> +
>> +    chosen {
>> +        stdout-path = "serial2:115200n8";
>> +    };
>> +
>> +    memory {
>> +        device_type = "memory";
>> +        reg = <0x0 0x0 0x0 0x80000000>;
>> +    };
>> +
>> +    emmc_pwrseq: emmc-pwrseq {
>> +        compatible = "mmc-pwrseq-emmc";
>> +        pinctrl-0 = <&emmc_reset>;
>> +        pinctrl-names = "default";
>> +        reset-gpios = <&gpio2 3 GPIO_ACTIVE_HIGH>;
>> +    };
>> +
>> +    ext_gmac: external-gmac-clock {
>> +        compatible = "fixed-clock";
>> +        #clock-cells = <0>;
>> +        clock-frequency = <125000000>;
>> +        clock-output-names = "ext_gmac";
>> +    };
>> +
>> +    keys: gpio-keys {
>> +        compatible = "gpio-keys";
>> +        pinctrl-names = "default";
>> +        pinctrl-0 = <&pwr_key>;
>> +
>> +        power {
>> +            wakeup-source;
>> +            gpios = <&gpio0 5 GPIO_ACTIVE_HIGH>;
>> +            label = "GPIO Power";
>> +            linux,code = <KEY_POWER>;
>> +        };
>> +    };
>> +
>> +    leds: gpio-leds {
>> +        compatible = "gpio-leds";
>> +
>> +        red {
>> +            gpios = <&gpio3 29 GPIO_ACTIVE_HIGH>;
>> +            label = "orion:red:led";
>> +            pinctrl-names = "default";
>> +            pinctrl-0 = <&led_ctl>;
>> +            default-state = "on";
>> +        };
>> +
>> +        blue {
>> +            gpios = <&gpio0 12 GPIO_ACTIVE_HIGH>;
>> +            label = "orion:blue:led";
>> +            pinctrl-names = "default";
>> +            pinctrl-0 = <&stby_pwren>;
>> +            default-state = "off";
>> +        };
>> +    };
>> +
>> +    vcc_18: vcc18-regulator {
>> +        compatible = "regulator-fixed";
>> +        regulator-name = "vcc_18";
>> +        regulator-min-microvolt = <1800000>;
>> +        regulator-max-microvolt = <1800000>;
>> +        regulator-always-on;
>> +        regulator-boot-on;
>> +        vin-supply = <&vcc_sys>;
>> +    };
>> +
>> +    /* supplies both host and otg */
>> +    vcc_host: vcc-host-regulator {
>> +        compatible = "regulator-fixed";
>> +        gpio = <&gpio0 4 GPIO_ACTIVE_LOW>;
>> +        pinctrl-names = "default";
>> +        pinctrl-0 = <&host_vbus_drv>;
>> +        regulator-name = "vcc_host";
>> +        regulator-always-on;
>> +        regulator-boot-on;
>> +        vin-supply = <&vcc_sys>;
>> +    };
>> +
>> +    vccio_sd: vcc-io-sd-regulator {
>> +        compatible = "regulator-fixed";
>> +        regulator-name= "vccio_sd";
>> +        regulator-min-microvolt = <1800000>;
>> +        regulator-max-microvolt = <3300000>;
>> +        regulator-always-on;
>> +        regulator-boot-on;
>> +        vin-supply = <&vcc_io>;
>> +    };
>> +
>> +    vcc_sd: vcc-sd-regulator {
>> +        compatible = "regulator-fixed";
>> +        regulator-name = "vcc_sd";
>> +        gpio = <&gpio3 11 GPIO_ACTIVE_LOW>;
>> +        regulator-min-microvolt = <1800000>;
>> +        regulator-max-microvolt = <3300000>;
>> +        regulator-always-on;
>> +        regulator-boot-on;
>> +        vin-supply = <&vcc_io>;
>> +    };
>> +
>> +    vcc_io: vcc-io-regulator {
>> +        compatible = "regulator-fixed";
>> +        regulator-name = "vcc_io";
>> +        regulator-min-microvolt = <3300000>;
>> +        regulator-max-microvolt = <3300000>;
>> +        regulator-always-on;
>> +        regulator-boot-on;
>> +        vin-supply = <&vcc_sys>;
>> +    };
>> +
>> +    vcc_lan: vcc-lan-regulator {
>> +        compatible = "regulator-fixed";
>> +        regulator-name = "vcc_lan";
>> +        regulator-min-microvolt = <3300000>;
>> +        regulator-max-microvolt = <3300000>;
>> +        regulator-always-on;
>> +        regulator-boot-on;
>> +        vin-supply = <&vcc_io>;
>> +    };
>> +
>> +    vcc_sys: vcc-sys-regulator {
>> +        compatible = "regulator-fixed";
>> +        regulator-name = "vcc_sys";
>> +        regulator-min-microvolt = <5000000>;
>> +        regulator-max-microvolt = <5000000>;
>> +        regulator-always-on;
>> +        regulator-boot-on;
>> +    };
>> +
>> +    vccio_wl: vccio-wl-regulator {
>> +        compatible = "regulator-fixed";
>> +        regulator-name = "vccio_wl";
>> +        regulator-min-microvolt = <3300000>;
>> +        regulator-max-microvolt = <3300000>;
>> +        regulator-always-on;
>> +        regulator-boot-on;
>> +        vin-supply = <&vcc_io>;
>> +    };
>> +
>> +    vdd_10: vdd-10-regulator {
>> +        compatible = "regulator-fixed";
>> +        regulator-name = "vdd_10";
>> +        regulator-min-microvolt = <1000000>;
>> +        regulator-max-microvolt = <1000000>;
>> +        regulator-always-on;
>> +        regulator-boot-on;
>> +        vin-supply = <&vcc_sys>;
>> +    };
>> +};
>> +
>> +&emmc {
>> +    bus-width = <8>;
>> +    cap-mmc-highspeed;
>> +    disable-wp;
>> +    mmc-pwrseq = <&emmc_pwrseq>;
>> +    non-removable;
>> +    num-slots = <1>;
>> +    pinctrl-names = "default";
>> +    pinctrl-0 = <&emmc_clk &emmc_cmd &emmc_bus8>;
>> +    keep-power-in-suspend;
>> +    cap-mmc-highspeed;
>> +    mmc-hs200-1_2v;
>> +    mmc-hs200-1_8v;
>> +    status = "okay";
>> +};
>> +
>> +&gmac {
>> +    assigned-clocks = <&cru SCLK_MAC>;
>> +    assigned-clock-parents = <&ext_gmac>;
>> +    phy-supply = <&vcc_lan>;
>> +    phy-mode = "rgmii";
>> +    clock_in_out = "input";
>> +    snps,reset-gpio = <&gpio3 12 0>;
>> +    snps,reset-active-low;
>> +    snps,reset-delays-us = <0 10000 1000000>;
>> +    pinctrl-names = "default";
>> +    pinctrl-0 = <&rgmii_pins>;
>> +    tx_delay = <0x30>;
>> +    rx_delay = <0x10>;
>> +    status = "ok";
>> +};
>> +
>> +&i2c0 {
>> +    status = "okay";
>> +
>> +    vdd_cpu: syr827 at 40 {
>> +        compatible = "silergy,syr827";
>> +        reg = <0x40>;
>> +        fcs,suspend-voltage-selector = <1>;
>> +        regulator-name = "vdd_cpu";
>> +        regulator-enable-ramp-delay = <300>;
>> +        regulator-min-microvolt = <712500>;
>> +        regulator-max-microvolt = <1500000>;
>> +        regulator-ramp-delay = <8000>;
>> +        regulator-always-on;
>> +        regulator-boot-on;
>> +        vin-supply = <&vcc_sys>;
>> +    };
>> +
>> +    hym8563: hym8563 at 51 {
>> +        compatible = "haoyu,hym8563";
>> +        reg = <0x51>;
>> +        #clock-cells = <0>;
>> +        clock-frequency = <32768>;
>> +        clock-output-names = "xin32k";
>> +        /* rtc_int is not connected */
>> +    };
>> +};
>> +
>> +&pinctrl {
>> +    pcfg_pull_none_drv_8ma: pcfg-pull-none-drv-8ma {
>> +        bias-disable;
>> +        drive-strength = <8>;
>> +    };
>> +
>> +    pcfg_pull_up_drv_8ma: pcfg-pull-up-drv-8ma {
>> +        bias-pull-up;
>> +        drive-strength = <8>;
>> +    };
>> +
>> +    emmc {
>> +        emmc_bus8: emmc-bus8 {
>> +            rockchip,pins = <1 18 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
>> +                    <1 19 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
>> +                    <1 20 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
>> +                    <1 21 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
>> +                    <1 22 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
>> +                    <1 23 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
>> +                    <1 24 RK_FUNC_2 &pcfg_pull_up_drv_8ma>,
>> +                    <1 25 RK_FUNC_2 &pcfg_pull_up_drv_8ma>;
>> +        };
>> +
>> +        emmc-clk {
>> +            rockchip,pins = <2 4 RK_FUNC_2 &pcfg_pull_none_drv_8ma>;
>> +        };
>> +
>> +        emmc-cmd {
>> +            rockchip,pins = <1 26 RK_FUNC_2 &pcfg_pull_up_drv_8ma>;
>> +        };
>> +
>> +        emmc_reset: emmc-reset {
>> +            rockchip,pins = <2 3 RK_FUNC_GPIO &pcfg_pull_none>;
>> +        };
>> +    };
>> +
>> +    keys {
>> +        pwr_key: pwr-key {
>> +            rockchip,pins = <0 5 RK_FUNC_GPIO &pcfg_pull_down>;
>> +        };
>> +    };
>> +
>> +    leds {
>> +        stby_pwren: stby-pwren {
>> +            rockchip,pins = <0 12 RK_FUNC_GPIO &pcfg_pull_none>;
>> +        };
>> +
>> +        led_ctl: led-ctl {
>> +            rockchip,pins = <3 29 RK_FUNC_GPIO &pcfg_pull_none>;
>> +        };
>> +    };
>> +
>> +    sdmmc {
>> +        sdmmc_clk: sdmmc-clk {
>> +            rockchip,pins = <2 9 RK_FUNC_1 &pcfg_pull_none_drv_8ma>;
>> +        };
>> +
>> +        sdmmc_cmd: sdmmc-cmd {
>> +            rockchip,pins = <2 10 RK_FUNC_1 &pcfg_pull_up_drv_8ma>;
>> +        };
>> +
>> +        sdmmc_cd: sdmmc-cd {
>> +            rockchip,pins = <2 11 RK_FUNC_1 &pcfg_pull_up_drv_8ma>;
>> +        };
>> +
>> +        sdmmc_bus1: sdmmc-bus1 {
>> +            rockchip,pins = <2 5 RK_FUNC_1 &pcfg_pull_up_drv_8ma>;
>> +        };
>> +
>> +        sdmmc_bus4: sdmmc-bus4 {
>> +            rockchip,pins = <2 5 RK_FUNC_1 &pcfg_pull_up_drv_8ma>,
>> +                    <2 6 RK_FUNC_1 &pcfg_pull_up_drv_8ma>,
>> +                    <2 7 RK_FUNC_1 &pcfg_pull_up_drv_8ma>,
>> +                    <2 8 RK_FUNC_1 &pcfg_pull_up_drv_8ma>;
>> +        };
>> +    };
>> +
>> +    usb {
>> +        host_vbus_drv: host-vbus-drv {
>> +            rockchip,pins = <0 4 RK_FUNC_GPIO &pcfg_pull_none>;
>> +        };
>> +    };
>> +};
>> +
>> +&saradc {
>> +    vref-supply = <&vcc_18>;
>> +    status = "okay";
>> +};
>> +
>> +&sdmmc {
>> +    clock-frequency = <50000000>;
>> +    clock-freq-min-max = <400000 50000000>;
>> +    cap-sd-highspeed;
>> +    card-detect-delay = <200>;
>> +
>> +    pinctrl-names = "default";
>> +    pinctrl-0 = <&sdmmc_clk &sdmmc_cmd &sdmmc_cd &sdmmc_bus4>;
>> +    bus-width = <4>;
>> +    num-slots = <1>;
>> +    keep-power-in-suspend;
>> +
>> +    vmmc-supply = <&vcc_sd>;
>> +    vqmmc-supply = <&vccio_sd>;
>> +    status = "okay";
>> +};
>> +
>> +&uart2 {
>> +    status = "okay";
>> +};
>> +
>> +&uart4 {
>> +    status = "okay";
>> +    pinctrl-names = "default";
>> +    pinctrl-0 = <&uart4_xfer>;
>> +};
>> +
>> +&usb_host0_ehci {
>> +    status = "okay";
>> +};
>> +
>> +&usb_otg {
>> +    status = "okay";
>> +};
>> +
>> +&wdt {
>> +    status = "okay";
>> +};
>>
>
>


From heiko at sntech.de  Mon Jul 18 11:12:32 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Mon, 18 Jul 2016 20:12:32 +0200
Subject: [PATCH v3] arm64: dts: rockchip: Add basic support for orion-r68
In-Reply-To: <1468577391-5096-1-git-send-email-matthias.bgg@gmail.com>
References: <1468577391-5096-1-git-send-email-matthias.bgg@gmail.com>
Message-ID: <5040831.O6sNvccABs@diego>

Am Freitag, 15. Juli 2016, 12:09:51 schrieb Matthias Brugger:
> This patch adds basic support for the Tronsmart orion r86 set-top-box.
> 
> Signed-off-by: Matthias Brugger <mbrugger at suse.com>

applied [0] with Rob's Ack.

I've removed the duplicate mmc cap and also reordered properties to be 
alphabetically sorted, please double check.

While it is in my 4.8 dts branch, this will only make it into 4.9 though.


Heiko


From heiko at sntech.de  Mon Jul 18 12:46:19 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Mon, 18 Jul 2016 21:46:19 +0200
Subject: [PATCH] ARM: dts: add rk3288-firefly-reload
In-Reply-To: <1468855952-2006-2-git-send-email-ayaka@soulik.info>
References: <1549971.ctkvOGm2xW@diego>
 <1468855952-2006-1-git-send-email-ayaka@soulik.info>
 <1468855952-2006-2-git-send-email-ayaka@soulik.info>
Message-ID: <2868781.4x2gJCRbLH@diego>

Am Montag, 18. Juli 2016, 23:32:32 schrieb Randy Li:
> The Firefly RK3288 Reload is a combination Firefly rk3288 core board
> with the Reload baseboard. Add a dtsi for the Firefly rk3288 core
> which can be included into the dts for the various baseboards
> in the future and dts for Reload base board.
> 
> Currently supported are serial console, wired networking, eMMC and
> SD storage, SPFIF, IR receiver, LEDs, SDIO wifi and USB. But only
> the OTG could work on the host mode now, the other USB host can't
> work now, additional patches are required.
> 
> Signed-off-by: Randy Li <ayaka at soulik.info>
> Acked-by: Rob Herring <robh at kernel.org>

applied [0], after some minor reordering - please double check.

Also, while the branch is named 4.8 this will only go into 4.9, as the merge-
window supposedly opens on the weekend.


Heiko

[0] https://git.kernel.org/cgit/linux/kernel/git/mmind/linux-rockchip.git/commit/?id=e00fdd4931ea4c47f53cba59b7a3800677546bea



From heiko at sntech.de  Mon Jul 18 13:50:37 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Mon, 18 Jul 2016 22:50:37 +0200
Subject: [PATCH] arm64: dts: rockchip: add spiX aliases for rk3399
In-Reply-To: <1468545873-134339-1-git-send-email-briannorris@chromium.org>
References: <1468545873-134339-1-git-send-email-briannorris@chromium.org>
Message-ID: <2463583.1KcLgghiy0@diego>

Am Donnerstag, 14. Juli 2016, 18:24:33 schrieb Brian Norris:
> It's nicer to see this:
> 
>     # ls -l /sys/bus/spi/devices/
>     total 0
>     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi0.0 ->
> ../../../devices/platform/ff1c0000.spi/spi_master/spi0/spi0.0 lrwxrwxrwx 1
> root root 0 Dec 31  1969 spi1.0 ->
> ../../../devices/platform/ff1d0000.spi/spi_master/spi1/spi1.0 lrwxrwxrwx 1
> root root 0 Dec 31  1969 spi2.0 ->
> ../../../devices/platform/ff1e0000.spi/spi_master/spi2/spi2.0 lrwxrwxrwx 1
> root root 0 Dec 31  1969 spi5.0 ->
> ../../../devices/platform/ff200000.spi/spi_master/spi5/spi5.0
> 
> than this:
> 
>     # ls -l /sys/bus/spi/devices/
>     total 0
>     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi32763.0 ->
> ../../../devices/platform/ff200000.spi/spi_master/spi32763/spi32763.0
> lrwxrwxrwx 1 root root 0 Dec 31  1969 spi32764.0 ->
> ../../../devices/platform/ff1e0000.spi/spi_master/spi32764/spi32764.0
> lrwxrwxrwx 1 root root 0 Dec 31  1969 spi32765.0 ->
> ../../../devices/platform/ff1d0000.spi/spi_master/spi32765/spi32765.0
> lrwxrwxrwx 1 root root 0 Dec 31  1969 spi32766.0 ->
> ../../../devices/platform/ff1c0000.spi/spi_master/spi32766/spi32766.0
> 
> With the latter, it's much clearer which bus devices are on.
> 
> Signed-off-by: Brian Norris <briannorris at chromium.org>

applied for 4.9

Thanks
Heiko


From heiko at sntech.de  Mon Jul 18 13:51:25 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Mon, 18 Jul 2016 22:51:25 +0200
Subject: [PATCH] arm64: dts: rockchip: remove broken-cd from sdio0
In-Reply-To: <1468545005-21382-1-git-send-email-shawn.lin@rock-chips.com>
References: <1468545005-21382-1-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <15016159.DdB4GTb1td@diego>

Am Freitag, 15. Juli 2016, 09:10:05 schrieb Shawn Lin:
> commit 1ade61c141e2 ("arm64: dts: rockchip: remove broken-cd
> from emmc and sdio") was intended to remove the abuse of
> broken-cd property from mmc. But somehow it forgot to remove
> this property from sdio0 node. Let's remove it now.
> 
> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>

applied for 4.9

Thanks
Heiko


From mark.yao at rock-chips.com  Mon Jul 18 18:59:29 2016
From: mark.yao at rock-chips.com (Mark yao)
Date: Tue, 19 Jul 2016 09:59:29 +0800
Subject: [PATCH] drm/rockchip: Delete an unnecessary check before
 drm_gem_object_unreference_unlocked()
In-Reply-To: <b66399ed-278b-b85d-4a21-b34164936ef6@users.sourceforge.net>
References: <5307CAA2.8060406@users.sourceforge.net>
 <alpine.DEB.2.02.1402212321410.2043@localhost6.localdomain6>
 <530A086E.8010901@users.sourceforge.net>
 <alpine.DEB.2.02.1402231635510.1985@localhost6.localdomain6>
 <530A72AA.3000601@users.sourceforge.net>
 <alpine.DEB.2.02.1402240658210.2090@localhost6.localdomain6>
 <530B5FB6.6010207@users.sourceforge.net>
 <alpine.DEB.2.10.1402241710370.2074@hadrien>
 <530C5E18.1020800@users.sourceforge.net>
 <alpine.DEB.2.10.1402251014170.2080@hadrien>
 <530CD2C4.4050903@users.sourceforge.net>
 <alpine.DEB.2.10.1402251840450.7035@hadrien>
 <530CF8FF.8080600@users.sourceforge.net>
 <alpine.DEB.2.02.1402252117150.2047@localhost6.localdomain6>
 <530DD06F.4090703@users.sourceforge.net>
 <alpine.DEB.2.02.1402262129250.2221@localhost6.localdomain6>
 <5317A59D.4@users.sourceforge.net>
 <b66399ed-278b-b85d-4a21-b34164936ef6@users.sourceforge.net>
Message-ID: <578D8981.2070102@rock-chips.com>

On 2016?07?15? 21:35, SF Markus Elfring wrote:
> From: Markus Elfring <elfring at users.sourceforge.net>
> Date: Fri, 15 Jul 2016 15:23:22 +0200
>
> The drm_gem_object_unreference_unlocked() function tests whether
> its argument is NULL and then returns immediately.
> Thus the test around the call is not needed.
>
> This issue was detected by using the Coccinelle software.
>
> Signed-off-by: Markus Elfring <elfring at users.sourceforge.net>
> ---
>   drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 8 ++------
>   1 file changed, 2 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> index 20f12bc..6f64e79 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
> @@ -43,14 +43,10 @@ struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb,
>   static void rockchip_drm_fb_destroy(struct drm_framebuffer *fb)
>   {
>   	struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb);
> -	struct drm_gem_object *obj;
>   	int i;
>   
> -	for (i = 0; i < ROCKCHIP_MAX_FB_BUFFER; i++) {
> -		obj = rockchip_fb->obj[i];
> -		if (obj)
> -			drm_gem_object_unreference_unlocked(obj);
> -	}
> +	for (i = 0; i < ROCKCHIP_MAX_FB_BUFFER; i++)
> +		drm_gem_object_unreference_unlocked(rockchip_fb->obj[i]);
>   
Good, Thanks for the fixes, Applied to my drm-fixes.

>   	drm_framebuffer_cleanup(fb);
>   	kfree(rockchip_fb);


-- 
?ark Yao




From mark.yao at rock-chips.com  Mon Jul 18 19:32:21 2016
From: mark.yao at rock-chips.com (Mark yao)
Date: Tue, 19 Jul 2016 10:32:21 +0800
Subject: [GIT PULL] drm/rockchip: some fixes
Message-ID: <578D9135.3060401@rock-chips.com>

Hi Dave
     Here are some little fixes for rockchip drm, looks good for me, and 
there is no doubt on them, So I'd like you can land them.

Thanks.

The following changes since commit 2d635fded22b71381b01e194a59544f2e8d71ef0:

   Merge tag 'drm-vc4-next-2016-07-15' of 
https://github.com/anholt/linux into drm-next (2016-07-16 11:25:11 +1000)

are available in the git repository at:

   https://github.com/markyzq/kernel-drm-rockchip.git 
drm-rockchip-next-fixes-2016-07-19

for you to fetch changes up to 0086772b2f89783d7b492fe0b21a6e0d9a03c0a3:

   drm/rockchip: allocate correct crtc state structure on reset 
(2016-07-19 10:03:38 +0800)

----------------------------------------------------------------
Dan Carpenter (1):
       drm/rockchip: fix a couple off by one bugs

John Keeping (4):
       drm/rockchip: dw_hdmi: remove unused #include
       drm/rockchip: fb: add missing header
       drm/rockchip: fix "should it be static?" warnings
       drm/rockchip: allocate correct crtc state structure on reset

Mark Yao (2):
       drm/rockchip: vop: correct the source size of uv scale factor setting
       drm/rockchip: vop: correct rk3036 register define

Markus Elfring (1):
       drm/rockchip: Delete an unnecessary check before 
drm_gem_object_unreference_unlocked()

Yakir Yang (1):
       drm/rockchip: vop: add uv_vir register field for RK3036 VOP

  drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c |  1 -
  drivers/gpu/drm/rockchip/rockchip_drm_drv.c |  6 +++---
  drivers/gpu/drm/rockchip/rockchip_drm_fb.c  | 11 ++++-------
  drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 21 ++++++++++++++++-----
  drivers/gpu/drm/rockchip/rockchip_vop_reg.c |  7 ++++---
  5 files changed, 27 insertions(+), 19 deletions(-)

-- 
?ark Yao




From u.kleine-koenig at pengutronix.de  Mon Jul 18 23:39:55 2016
From: u.kleine-koenig at pengutronix.de (Uwe =?iso-8859-1?Q?Kleine-K=F6nig?=)
Date: Tue, 19 Jul 2016 08:39:55 +0200
Subject: [PATCH] arm64: dts: rockchip: add spiX aliases for rk3399
In-Reply-To: <1468545873-134339-1-git-send-email-briannorris@chromium.org>
References: <1468545873-134339-1-git-send-email-briannorris@chromium.org>
Message-ID: <20160719063955.GE5368@pengutronix.de>

Hello,

On Thu, Jul 14, 2016 at 06:24:33PM -0700, Brian Norris wrote:
> It's nicer to see this:
> 
>     # ls -l /sys/bus/spi/devices/
>     total 0
>     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi0.0 -> ../../../devices/platform/ff1c0000.spi/spi_master/spi0/spi0.0
>     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi1.0 -> ../../../devices/platform/ff1d0000.spi/spi_master/spi1/spi1.0
>     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi2.0 -> ../../../devices/platform/ff1e0000.spi/spi_master/spi2/spi2.0
>     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi5.0 -> ../../../devices/platform/ff200000.spi/spi_master/spi5/spi5.0
> 
> than this:
> 
>     # ls -l /sys/bus/spi/devices/
>     total 0
>     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi32763.0 -> ../../../devices/platform/ff200000.spi/spi_master/spi32763/spi32763.0
>     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi32764.0 -> ../../../devices/platform/ff1e0000.spi/spi_master/spi32764/spi32764.0
>     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi32765.0 -> ../../../devices/platform/ff1d0000.spi/spi_master/spi32765/spi32765.0
>     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi32766.0 -> ../../../devices/platform/ff1c0000.spi/spi_master/spi32766/spi32766.0
> 
> With the latter, it's much clearer which bus devices are on.
> 
> Signed-off-by: Brian Norris <briannorris at chromium.org>
> ---
>  arch/arm64/boot/dts/rockchip/rk3399.dtsi | 6 ++++++
>  1 file changed, 6 insertions(+)
> 
> diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> index 4c84229789ef..b04abbfd4898 100644
> --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> @@ -70,6 +70,12 @@
>  		serial2 = &uart2;
>  		serial3 = &uart3;
>  		serial4 = &uart4;
> +		spi0 = &spi0;
> +		spi1 = &spi1;
> +		spi2 = &spi2;
> +		spi3 = &spi3;
> +		spi4 = &spi4;
> +		spi5 = &spi5;

Note that Rob Herring (with his dt-maintainer hat on) doesn't like these
aliases.
See for example:
	http://mid.gmane.org/20160705140546.GA10601 at rob-hp-laptop

Best regards
Uwe

-- 
Pengutronix e.K.                           | Uwe Kleine-K?nig            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |


From frank.wang at rock-chips.com  Tue Jul 19 00:23:41 2016
From: frank.wang at rock-chips.com (Frank Wang)
Date: Tue, 19 Jul 2016 15:23:41 +0800
Subject: [PATCH v8 0/3] Add a new Rockchip usb2 phy driver
Message-ID: <1468913024-23813-1-git-send-email-frank.wang@rock-chips.com>

The newer SoCs (rk3366, rk3399) of Rock-chip take a different usb-phy
IP block than rk3288 and before, and most of phy-related registers are
also different from the past, so a new phy driver is required necessarily.

These series patches add phy-rockchip-inno-usb2.c and the corresponding
dt-bindings.

Changes in v8:
 - Added support for rk3399 SoC.
 - Prepare input clock of phy at probe time.
 - Improved sm_work function to handle phy-port state more clearly.

Changes in v7:
 - Renamed functions *usb2phy_resume/*usb2phy_suspend to *usb2phy_power_on/usb2phy_power_off.

Changes in v6:
 - Changed '_' to '-' for otg-id and otg-bvalid property in devicetree bindings.
 - Fixed the output clock would be disabled more than once while phy-port was going to suspend.
 - Improved the driver to avoid the currently empty otg-port would cause null-pointer dereferences.

Changes in v5:
 - Added 'reg' property both in devicetree bindings and driver to match the different phy-blocks.

Changes in v4:
 - Used 'phy-supply' instead of 'vbus_*-supply'.

Changes in v3:
 - Supplemented some hardware-description into the devicetree bindings.
 - Resolved the mapping defect between fixed value in driver and the property
   in devicetree.
 - Code cleanup.

Changes in v2:
 - Specified more hardware-description into the devicetree bindings.
 - Optimized some process of driver.

Frank Wang (3):
  Documentation: bindings: add DT documentation for Rockchip USB2PHY
  phy: rockchip-inno-usb2: add a new driver for Rockchip usb2phy
  arm64: dts: rockchip: add usb2-phy support for rk3399

 .../bindings/phy/phy-rockchip-inno-usb2.txt        |   64 ++
 arch/arm64/boot/dts/rockchip/rk3399-evb.dts        |   19 +
 arch/arm64/boot/dts/rockchip/rk3399.dtsi           |   47 +-
 drivers/phy/Kconfig                                |    7 +
 drivers/phy/Makefile                               |    1 +
 drivers/phy/phy-rockchip-inno-usb2.c               |  707 ++++++++++++++++++++
 6 files changed, 844 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt
 create mode 100644 drivers/phy/phy-rockchip-inno-usb2.c

-- 
1.7.9.5




From frank.wang at rock-chips.com  Tue Jul 19 00:23:42 2016
From: frank.wang at rock-chips.com (Frank Wang)
Date: Tue, 19 Jul 2016 15:23:42 +0800
Subject: [PATCH v8 1/3] Documentation: bindings: add DT documentation for
 Rockchip USB2PHY
In-Reply-To: <1468913024-23813-1-git-send-email-frank.wang@rock-chips.com>
References: <1468913024-23813-1-git-send-email-frank.wang@rock-chips.com>
Message-ID: <1468913024-23813-2-git-send-email-frank.wang@rock-chips.com>

Signed-off-by: Frank Wang <frank.wang at rock-chips.com>
Acked-by: Rob Herring <robh at kernel.org>
Reviewed-by: Heiko Stuebner <heiko at sntech.de>
---

Changes in v8: None

Changes in v7: None

Changes in v6:
 - Changed '_' to '-' for otg-id and otg-bvalid property.

Changes in v5:
 - Added 'reg' property to identify the different phy-blocks.

Changes in v4:
 - Used 'phy-supply' instead of 'vbus_*-supply'.

Changes in v3:
 - Added 'clocks' and 'clock-names' optional properties.
 - Specified 'otg-port' and 'host-port' as the sub-node name.

Changes in v2:
 - Changed vbus_host optional property from gpio to regulator.
 - Specified vbus_otg-supply optional property.
 - Specified otg_id and otg_bvalid property.

 .../bindings/phy/phy-rockchip-inno-usb2.txt        |   64 ++++++++++++++++++++
 1 file changed, 64 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt

diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt b/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt
new file mode 100644
index 0000000..3c29c77
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt
@@ -0,0 +1,64 @@
+ROCKCHIP USB2.0 PHY WITH INNO IP BLOCK
+
+Required properties (phy (parent) node):
+ - compatible : should be one of the listed compatibles:
+	* "rockchip,rk3366-usb2phy"
+	* "rockchip,rk3399-usb2phy"
+ - reg : the address offset of grf for usb-phy configuration.
+ - #clock-cells : should be 0.
+ - clock-output-names : specify the 480m output clock name.
+
+Optional properties:
+ - clocks : phandle + phy specifier pair, for the input clock of phy.
+ - clock-names : input clock name of phy, must be "phyclk".
+
+Required nodes : a sub-node is required for each port the phy provides.
+		 The sub-node name is used to identify host or otg port,
+		 and shall be the following entries:
+	* "otg-port" : the name of otg port.
+	* "host-port" : the name of host port.
+
+Required properties (port (child) node):
+ - #phy-cells : must be 0. See ./phy-bindings.txt for details.
+ - interrupts : specify an interrupt for each entry in interrupt-names.
+ - interrupt-names : a list which shall be the following entries:
+	* "otg-id" : for the otg id interrupt.
+	* "otg-bvalid" : for the otg vbus interrupt.
+	* "linestate" : for the host/otg linestate interrupt.
+
+Optional properties:
+ - phy-supply : phandle to a regulator that provides power to VBUS.
+		See ./phy-bindings.txt for details.
+
+Example:
+
+grf: syscon at ff770000 {
+	compatible = "rockchip,rk3366-grf", "syscon", "simple-mfd";
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+...
+
+	u2phy: usb2-phy at 700 {
+		compatible = "rockchip,rk3366-usb2phy";
+		reg = <0x700 0x2c>;
+		#clock-cells = <0>;
+		clock-output-names = "sclk_otgphy0_480m";
+
+		u2phy_otg: otg-port {
+			#phy-cells = <0>;
+			interrupts = <GIC_SPI 93 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 95 IRQ_TYPE_LEVEL_HIGH>;
+			interrupt-names = "otg-id", "otg-bvalid", "linestate";
+			status = "okay";
+		};
+
+		u2phy_host: host-port {
+			#phy-cells = <0>;
+			interrupts = <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>;
+			interrupt-names = "linestate";
+			status = "okay";
+		};
+	};
+};
-- 
1.7.9.5




From frank.wang at rock-chips.com  Tue Jul 19 00:27:40 2016
From: frank.wang at rock-chips.com (Frank Wang)
Date: Tue, 19 Jul 2016 15:27:40 +0800
Subject: [PATCH v8 2/3] phy: rockchip-inno-usb2: add a new driver for Rockchip
 usb2phy
Message-ID: <1468913260-23938-1-git-send-email-frank.wang@rock-chips.com>

The newer SoCs (rk3366, rk3399) take a different usb-phy IP block
than rk3288 and before, and most of phy-related registers are also
different from the past, so a new phy driver is required necessarily.

Signed-off-by: Frank Wang <frank.wang at rock-chips.com>
Suggested-by: Heiko Stuebner <heiko at sntech.de>
Suggested-by: Guenter Roeck <linux at roeck-us.net>
Suggested-by: Doug Anderson <dianders at chromium.org>
---

Changes in v8:
 - Added support for rk3399 SoC.
 - Prepare input clock of phy at probe time.
 - Improved sm_work function to handle phy-port state more clearly.

Changes in v7:
 - renamed functions *usb2phy_resume/*usb2phy_suspend to *usb2phy_power_on/usb2phy_power_off.

Changes in v6:
 - Fixed the output clock would be disabled more than once while phy-port was going to suspend.
 - Improved the driver to avoid the currently empty otg-port would cause null-pointer dereferences.

Changes in v5:
 - Added 'reg' in the data block to match the different phy-blocks in dt.

Changes in v4:
 - Removed some processes related to 'vbus_host-supply'.

Changes in v3:
 - Resolved the mapping defect between fixed value in driver and the property
   in devicetree.
 - Optimized 480m output clock register function.
 - Code cleanup.

Changes in v2:
 - Changed vbus_host operation from gpio to regulator in *_probe.
 - Improved the fault treatment relate to 480m clock register.
 - Cleaned up some meaningless codes in *_clk480m_disable.
 - made more clear the comment of *_sm_work.

 drivers/phy/Kconfig                  |    7 +
 drivers/phy/Makefile                 |    1 +
 drivers/phy/phy-rockchip-inno-usb2.c |  707 ++++++++++++++++++++++++++++++++++
 3 files changed, 715 insertions(+)
 create mode 100644 drivers/phy/phy-rockchip-inno-usb2.c

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index b869b98..29ef15c 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -347,6 +347,13 @@ config PHY_ROCKCHIP_USB
 	help
 	  Enable this to support the Rockchip USB 2.0 PHY.
 
+config PHY_ROCKCHIP_INNO_USB2
+	tristate "Rockchip INNO USB2PHY Driver"
+	depends on ARCH_ROCKCHIP && OF
+	select GENERIC_PHY
+	help
+	  Support for Rockchip USB2.0 PHY with Innosilicon IP block.
+
 config PHY_ROCKCHIP_EMMC
 	tristate "Rockchip EMMC PHY Driver"
 	depends on ARCH_ROCKCHIP && OF
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 9c3e73c..4963fbc 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -38,6 +38,7 @@ phy-exynos-usb2-$(CONFIG_PHY_S5PV210_USB2)	+= phy-s5pv210-usb2.o
 obj-$(CONFIG_PHY_EXYNOS5_USBDRD)	+= phy-exynos5-usbdrd.o
 obj-$(CONFIG_PHY_QCOM_APQ8064_SATA)	+= phy-qcom-apq8064-sata.o
 obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o
+obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2)	+= phy-rockchip-inno-usb2.o
 obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o
 obj-$(CONFIG_PHY_ROCKCHIP_DP)		+= phy-rockchip-dp.o
 obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA)	+= phy-qcom-ipq806x-sata.o
diff --git a/drivers/phy/phy-rockchip-inno-usb2.c b/drivers/phy/phy-rockchip-inno-usb2.c
new file mode 100644
index 0000000..ac20310
--- /dev/null
+++ b/drivers/phy/phy-rockchip-inno-usb2.c
@@ -0,0 +1,707 @@
+/*
+ * Rockchip USB2.0 PHY with Innosilicon IP block driver
+ *
+ * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/gpio/consumer.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#define BIT_WRITEABLE_SHIFT	16
+#define SCHEDULE_DELAY	(60 * HZ)
+
+enum rockchip_usb2phy_port_id {
+	USB2PHY_PORT_OTG,
+	USB2PHY_PORT_HOST,
+	USB2PHY_NUM_PORTS,
+};
+
+enum rockchip_usb2phy_host_state {
+	PHY_STATE_HS_ONLINE	= 0,
+	PHY_STATE_DISCONNECT	= 1,
+	PHY_STATE_CONNECT	= 2,
+	PHY_STATE_FS_LS_ONLINE	= 4,
+};
+
+struct usb2phy_reg {
+	unsigned int	offset;
+	unsigned int	bitend;
+	unsigned int	bitstart;
+	unsigned int	disable;
+	unsigned int	enable;
+};
+
+/**
+ * struct rockchip_usb2phy_port_cfg: usb-phy port configuration.
+ * @phy_sus: phy suspend register.
+ * @ls_det_en: linestate detection enable register.
+ * @ls_det_st: linestate detection state register.
+ * @ls_det_clr: linestate detection clear register.
+ * @utmi_ls: utmi linestate state register.
+ * @utmi_hstdet: utmi host disconnect register.
+ */
+struct rockchip_usb2phy_port_cfg {
+	struct usb2phy_reg	phy_sus;
+	struct usb2phy_reg	ls_det_en;
+	struct usb2phy_reg	ls_det_st;
+	struct usb2phy_reg	ls_det_clr;
+	struct usb2phy_reg	utmi_ls;
+	struct usb2phy_reg	utmi_hstdet;
+};
+
+/**
+ * struct rockchip_usb2phy_cfg: usb-phy configuration.
+ * @reg: the address offset of grf for usb-phy config.
+ * @num_ports: specify how many ports that the phy has.
+ * @clkout_ctl: keep on/turn off output clk of phy.
+ */
+struct rockchip_usb2phy_cfg {
+	unsigned int	reg;
+	unsigned int	num_ports;
+	struct usb2phy_reg	clkout_ctl;
+	const struct rockchip_usb2phy_port_cfg	port_cfgs[USB2PHY_NUM_PORTS];
+};
+
+/**
+ * struct rockchip_usb2phy_port: usb-phy port data.
+ * @port_id: flag for otg port or host port.
+ * @suspended: phy suspended flag.
+ * @ls_irq: IRQ number assigned for linestate detection.
+ * @mutex: for register updating in sm_work.
+ * @sm_work: OTG state machine work.
+ * @phy_cfg: port register configuration, assigned by driver data.
+ */
+struct rockchip_usb2phy_port {
+	struct phy	*phy;
+	unsigned int	port_id;
+	bool		suspended;
+	int		ls_irq;
+	struct mutex	mutex;
+	struct		delayed_work sm_work;
+	const struct	rockchip_usb2phy_port_cfg *port_cfg;
+};
+
+/**
+ * struct rockchip_usb2phy: usb2.0 phy driver data.
+ * @grf: General Register Files regmap.
+ * @clk: clock struct of phy input clk.
+ * @clk480m: clock struct of phy output clk.
+ * @clk_hw: clock struct of phy output clk management.
+ * @phy_cfg: phy register configuration, assigned by driver data.
+ * @ports: phy port instance.
+ */
+struct rockchip_usb2phy {
+	struct device	*dev;
+	struct regmap	*grf;
+	struct clk	*clk;
+	struct clk	*clk480m;
+	struct clk_hw	clk480m_hw;
+	const struct rockchip_usb2phy_cfg	*phy_cfg;
+	struct rockchip_usb2phy_port	ports[USB2PHY_NUM_PORTS];
+};
+
+static inline int property_enable(struct rockchip_usb2phy *rphy,
+				  const struct usb2phy_reg *reg, bool en)
+{
+	unsigned int val, mask, tmp;
+
+	tmp = en ? reg->enable : reg->disable;
+	mask = GENMASK(reg->bitend, reg->bitstart);
+	val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT);
+
+	return regmap_write(rphy->grf, reg->offset, val);
+}
+
+static inline bool property_enabled(struct rockchip_usb2phy *rphy,
+				    const struct usb2phy_reg *reg)
+{
+	int ret;
+	unsigned int tmp, orig;
+	unsigned int mask = GENMASK(reg->bitend, reg->bitstart);
+
+	ret = regmap_read(rphy->grf, reg->offset, &orig);
+	if (ret)
+		return false;
+
+	tmp = (orig & mask) >> reg->bitstart;
+	return tmp == reg->enable;
+}
+
+static int rockchip_usb2phy_clk480m_enable(struct clk_hw *hw)
+{
+	struct rockchip_usb2phy *rphy =
+		container_of(hw, struct rockchip_usb2phy, clk480m_hw);
+	int ret;
+
+	/* turn on 480m clk output if it is off */
+	if (!property_enabled(rphy, &rphy->phy_cfg->clkout_ctl)) {
+		ret = property_enable(rphy, &rphy->phy_cfg->clkout_ctl, true);
+		if (ret)
+			return ret;
+
+		/* waitting for the clk become stable */
+		mdelay(1);
+	}
+
+	return 0;
+}
+
+static void rockchip_usb2phy_clk480m_disable(struct clk_hw *hw)
+{
+	struct rockchip_usb2phy *rphy =
+		container_of(hw, struct rockchip_usb2phy, clk480m_hw);
+
+	/* turn off 480m clk output */
+	property_enable(rphy, &rphy->phy_cfg->clkout_ctl, false);
+}
+
+static int rockchip_usb2phy_clk480m_enabled(struct clk_hw *hw)
+{
+	struct rockchip_usb2phy *rphy =
+		container_of(hw, struct rockchip_usb2phy, clk480m_hw);
+
+	return property_enabled(rphy, &rphy->phy_cfg->clkout_ctl);
+}
+
+static unsigned long
+rockchip_usb2phy_clk480m_recalc_rate(struct clk_hw *hw,
+				     unsigned long parent_rate)
+{
+	return 480000000;
+}
+
+static const struct clk_ops rockchip_usb2phy_clkout_ops = {
+	.enable = rockchip_usb2phy_clk480m_enable,
+	.disable = rockchip_usb2phy_clk480m_disable,
+	.is_enabled = rockchip_usb2phy_clk480m_enabled,
+	.recalc_rate = rockchip_usb2phy_clk480m_recalc_rate,
+};
+
+static void rockchip_usb2phy_clk480m_unregister(void *data)
+{
+	struct rockchip_usb2phy *rphy = data;
+
+	of_clk_del_provider(rphy->dev->of_node);
+	clk_unregister(rphy->clk480m);
+}
+
+static int
+rockchip_usb2phy_clk480m_register(struct rockchip_usb2phy *rphy)
+{
+	struct device_node *node = rphy->dev->of_node;
+	struct clk_init_data init;
+	const char *clk_name;
+	int ret;
+
+	init.flags = 0;
+	init.name = "clk_usbphy_480m";
+	init.ops = &rockchip_usb2phy_clkout_ops;
+
+	/* optional override of the clockname */
+	of_property_read_string(node, "clock-output-names", &init.name);
+
+	if (rphy->clk) {
+		clk_name = __clk_get_name(rphy->clk);
+		init.parent_names = &clk_name;
+		init.num_parents = 1;
+	} else {
+		init.parent_names = NULL;
+		init.num_parents = 0;
+	}
+
+	rphy->clk480m_hw.init = &init;
+
+	/* register the clock */
+	rphy->clk480m = clk_register(rphy->dev, &rphy->clk480m_hw);
+	if (IS_ERR(rphy->clk480m)) {
+		ret = PTR_ERR(rphy->clk480m);
+		goto err_ret;
+	}
+
+	ret = of_clk_add_provider(node, of_clk_src_simple_get, rphy->clk480m);
+	if (ret < 0)
+		goto err_clk_provider;
+
+	ret = devm_add_action(rphy->dev, rockchip_usb2phy_clk480m_unregister,
+			      rphy);
+	if (ret < 0)
+		goto err_unreg_action;
+
+	return 0;
+
+err_unreg_action:
+	of_clk_del_provider(node);
+err_clk_provider:
+	clk_unregister(rphy->clk480m);
+err_ret:
+	return ret;
+}
+
+static int rockchip_usb2phy_init(struct phy *phy)
+{
+	struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
+	struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent);
+	int ret;
+
+	if (rport->port_id == USB2PHY_PORT_HOST) {
+		/* clear linestate and enable linestate detect irq */
+		mutex_lock(&rport->mutex);
+
+		ret = property_enable(rphy, &rport->port_cfg->ls_det_clr, true);
+		if (ret) {
+			mutex_unlock(&rport->mutex);
+			return ret;
+		}
+
+		ret = property_enable(rphy, &rport->port_cfg->ls_det_en, true);
+		if (ret) {
+			mutex_unlock(&rport->mutex);
+			return ret;
+		}
+
+		mutex_unlock(&rport->mutex);
+		schedule_delayed_work(&rport->sm_work, SCHEDULE_DELAY);
+	}
+
+	return 0;
+}
+
+static int rockchip_usb2phy_power_on(struct phy *phy)
+{
+	struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
+	struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent);
+	int ret;
+
+	dev_dbg(&rport->phy->dev, "port power on\n");
+
+	if (!rport->suspended)
+		return 0;
+
+	ret = clk_prepare_enable(rphy->clk480m);
+	if (ret)
+		return ret;
+
+	ret = property_enable(rphy, &rport->port_cfg->phy_sus, false);
+	if (ret)
+		return ret;
+
+	rport->suspended = false;
+	return 0;
+}
+
+static int rockchip_usb2phy_power_off(struct phy *phy)
+{
+	struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
+	struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent);
+	int ret;
+
+	dev_dbg(&rport->phy->dev, "port power off\n");
+
+	if (rport->suspended)
+		return 0;
+
+	ret = property_enable(rphy, &rport->port_cfg->phy_sus, true);
+	if (ret)
+		return ret;
+
+	rport->suspended = true;
+	clk_disable_unprepare(rphy->clk480m);
+
+	return 0;
+}
+
+static int rockchip_usb2phy_exit(struct phy *phy)
+{
+	struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
+
+	if (rport->port_id == USB2PHY_PORT_HOST)
+		cancel_delayed_work_sync(&rport->sm_work);
+
+	return 0;
+}
+
+static const struct phy_ops rockchip_usb2phy_ops = {
+	.init		= rockchip_usb2phy_init,
+	.exit		= rockchip_usb2phy_exit,
+	.power_on	= rockchip_usb2phy_power_on,
+	.power_off	= rockchip_usb2phy_power_off,
+	.owner		= THIS_MODULE,
+};
+
+/*
+ * The function manage host-phy port state and suspend/resume phy port
+ * to save power.
+ *
+ * we rely on utmi_linestate and utmi_hostdisconnect to identify whether
+ * devices is disconnect or not. Besides, we do not need care it is FS/LS
+ * disconnected or HS disconnected, actually, we just only need get the
+ * device is disconnected at last through rearm the delayed work,
+ * to suspend the phy port in _PHY_STATE_DISCONNECT_ case.
+ *
+ * NOTE: It may invoke *phy_powr_off or *phy_power_on which will invoke
+ * some clk related APIs, so do not invoke it from interrupt context directly.
+ */
+static void rockchip_usb2phy_sm_work(struct work_struct *work)
+{
+	struct rockchip_usb2phy_port *rport =
+		container_of(work, struct rockchip_usb2phy_port, sm_work.work);
+	struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
+	unsigned int sh = rport->port_cfg->utmi_hstdet.bitend -
+			  rport->port_cfg->utmi_hstdet.bitstart + 1;
+	unsigned int ul, uhd, state;
+	unsigned int ul_mask, uhd_mask;
+	int ret;
+
+	mutex_lock(&rport->mutex);
+
+	ret = regmap_read(rphy->grf, rport->port_cfg->utmi_ls.offset, &ul);
+	if (ret < 0)
+		goto next_schedule;
+
+	ret = regmap_read(rphy->grf, rport->port_cfg->utmi_hstdet.offset,
+			  &uhd);
+	if (ret < 0)
+		goto next_schedule;
+
+	uhd_mask = GENMASK(rport->port_cfg->utmi_hstdet.bitend,
+			   rport->port_cfg->utmi_hstdet.bitstart);
+	ul_mask = GENMASK(rport->port_cfg->utmi_ls.bitend,
+			  rport->port_cfg->utmi_ls.bitstart);
+
+	/* stitch on utmi_ls and utmi_hstdet as phy state */
+	state = ((uhd & uhd_mask) >> rport->port_cfg->utmi_hstdet.bitstart) |
+		(((ul & ul_mask) >> rport->port_cfg->utmi_ls.bitstart) << sh);
+
+	switch (state) {
+	case PHY_STATE_HS_ONLINE:
+		dev_dbg(&rport->phy->dev, "HS online\n");
+		break;
+	case PHY_STATE_FS_LS_ONLINE:
+		/*
+		 * For FS/LS device, the online state share with connect state
+		 * from utmi_ls and utmi_hstdet register, so we distinguish
+		 * them via suspended flag.
+		 *
+		 * Plus, there are two cases, one is D- Line pull-up, and D+
+		 * line pull-down, the state is 4; another is D+ line pull-up,
+		 * and D- line pull-down, the state is 2.
+		 */
+		if (!rport->suspended) {
+			/* D- line pull-up, D+ line pull-down */
+			dev_dbg(&rport->phy->dev, "FS/LS online\n");
+			break;
+		}
+		/* fall through */
+	case PHY_STATE_CONNECT:
+		if (rport->suspended) {
+			dev_dbg(&rport->phy->dev, "Connected\n");
+			rockchip_usb2phy_power_on(rport->phy);
+			rport->suspended = false;
+		} else {
+			/* D+ line pull-up, D- line pull-down */
+			dev_dbg(&rport->phy->dev, "FS/LS online\n");
+		}
+		break;
+	case PHY_STATE_DISCONNECT:
+		if (!rport->suspended) {
+			dev_dbg(&rport->phy->dev, "Disconnected\n");
+			rockchip_usb2phy_power_off(rport->phy);
+			rport->suspended = true;
+		}
+
+		/*
+		 * activate the linestate detection to get the next device
+		 * plug-in irq.
+		 */
+		property_enable(rphy, &rport->port_cfg->ls_det_clr, true);
+		property_enable(rphy, &rport->port_cfg->ls_det_en, true);
+
+		/*
+		 * we don't need to rearm the delayed work when the phy port
+		 * is suspended.
+		 */
+		mutex_unlock(&rport->mutex);
+		return;
+	default:
+		dev_dbg(&rport->phy->dev, "unknown phy state\n");
+		break;
+	}
+
+next_schedule:
+	mutex_unlock(&rport->mutex);
+	schedule_delayed_work(&rport->sm_work, SCHEDULE_DELAY);
+}
+
+static irqreturn_t rockchip_usb2phy_linestate_irq(int irq, void *data)
+{
+	struct rockchip_usb2phy_port *rport = data;
+	struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
+
+	if (!property_enabled(rphy, &rport->port_cfg->ls_det_st))
+		return IRQ_NONE;
+
+	mutex_lock(&rport->mutex);
+
+	/* disable linestate detect irq and clear its status */
+	property_enable(rphy, &rport->port_cfg->ls_det_en, false);
+	property_enable(rphy, &rport->port_cfg->ls_det_clr, true);
+
+	mutex_unlock(&rport->mutex);
+
+	/*
+	 * In this case for host phy port, a new device is plugged in,
+	 * meanwhile, if the phy port is suspended, we need rearm the work to
+	 * resume it and mange its states; otherwise, we do nothing about that.
+	 */
+	if (rport->suspended && rport->port_id == USB2PHY_PORT_HOST)
+		rockchip_usb2phy_sm_work(&rport->sm_work.work);
+
+	return IRQ_HANDLED;
+}
+
+static int rockchip_usb2phy_host_port_init(struct rockchip_usb2phy *rphy,
+					   struct rockchip_usb2phy_port *rport,
+					   struct device_node *child_np)
+{
+	int ret;
+
+	rport->port_id = USB2PHY_PORT_HOST;
+	rport->port_cfg = &rphy->phy_cfg->port_cfgs[USB2PHY_PORT_HOST];
+	rport->suspended = true;
+
+	mutex_init(&rport->mutex);
+	INIT_DELAYED_WORK(&rport->sm_work, rockchip_usb2phy_sm_work);
+
+	rport->ls_irq = of_irq_get_byname(child_np, "linestate");
+	if (rport->ls_irq < 0) {
+		dev_err(rphy->dev, "no linestate irq provided\n");
+		return rport->ls_irq;
+	}
+
+	ret = devm_request_threaded_irq(rphy->dev, rport->ls_irq, NULL,
+					rockchip_usb2phy_linestate_irq,
+					IRQF_ONESHOT,
+					"rockchip_usb2phy", rport);
+	if (ret) {
+		dev_err(rphy->dev, "failed to request irq handle\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int rockchip_usb2phy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct device_node *child_np;
+	struct phy_provider *provider;
+	struct rockchip_usb2phy *rphy;
+	const struct rockchip_usb2phy_cfg *phy_cfgs;
+	const struct of_device_id *match;
+	unsigned int reg;
+	int index, ret;
+
+	rphy = devm_kzalloc(dev, sizeof(*rphy), GFP_KERNEL);
+	if (!rphy)
+		return -ENOMEM;
+
+	match = of_match_device(dev->driver->of_match_table, dev);
+	if (!match || !match->data) {
+		dev_err(dev, "phy configs are not assigned!\n");
+		return -EINVAL;
+	}
+
+	if (!dev->parent || !dev->parent->of_node)
+		return -EINVAL;
+
+	rphy->grf = syscon_node_to_regmap(dev->parent->of_node);
+	if (IS_ERR(rphy->grf))
+		return PTR_ERR(rphy->grf);
+
+	if (of_property_read_u32(np, "reg", &reg)) {
+		dev_err(dev, "the reg property is not assigned in %s node\n",
+			np->name);
+		return -EINVAL;
+	}
+
+	rphy->dev = dev;
+	phy_cfgs = match->data;
+	platform_set_drvdata(pdev, rphy);
+
+	/* find out a proper config which can be matched with dt. */
+	index = 0;
+	while (phy_cfgs[index].reg) {
+		if (phy_cfgs[index].reg == reg) {
+			rphy->phy_cfg = &phy_cfgs[index];
+			break;
+		}
+
+		++index;
+	}
+
+	if (!rphy->phy_cfg) {
+		dev_err(dev, "no phy-config can be matched with %s node\n",
+			np->name);
+		return -EINVAL;
+	}
+
+	rphy->clk = of_clk_get_by_name(np, "phyclk");
+	if (!IS_ERR(rphy->clk)) {
+		clk_prepare_enable(rphy->clk);
+	} else {
+		dev_info(&pdev->dev, "no phyclk specified\n");
+		rphy->clk = NULL;
+	}
+
+	ret = rockchip_usb2phy_clk480m_register(rphy);
+	if (ret) {
+		dev_err(dev, "failed to register 480m output clock\n");
+		goto disable_clks;
+	}
+
+	index = 0;
+	for_each_available_child_of_node(np, child_np) {
+		struct rockchip_usb2phy_port *rport = &rphy->ports[index];
+		struct phy *phy;
+
+		/*
+		 * This driver aim to support both otg-port and host-port,
+		 * but unfortunately, the otg part is not ready in current,
+		 * so this comments and below codes are interim, which should
+		 * be changed after otg-port is supplied soon.
+		 */
+		if (of_node_cmp(child_np->name, "host-port"))
+			goto next_child;
+
+		phy = devm_phy_create(dev, child_np, &rockchip_usb2phy_ops);
+		if (IS_ERR(phy)) {
+			dev_err(dev, "failed to create phy\n");
+			ret = PTR_ERR(phy);
+			goto put_child;
+		}
+
+		rport->phy = phy;
+		phy_set_drvdata(rport->phy, rport);
+
+		ret = rockchip_usb2phy_host_port_init(rphy, rport, child_np);
+		if (ret)
+			goto put_child;
+
+next_child:
+		/* to prevent out of boundary */
+		if (++index >= rphy->phy_cfg->num_ports)
+			break;
+	}
+
+	provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+	return PTR_ERR_OR_ZERO(provider);
+
+put_child:
+	of_node_put(child_np);
+disable_clks:
+	if (rphy->clk) {
+		clk_disable_unprepare(rphy->clk);
+		clk_put(rphy->clk);
+	}
+	return ret;
+}
+
+static const struct rockchip_usb2phy_cfg rk3366_phy_cfgs[] = {
+	{
+		.reg = 0x700,
+		.num_ports	= 2,
+		.clkout_ctl	= { 0x0724, 15, 15, 1, 0 },
+		.port_cfgs	= {
+			[USB2PHY_PORT_HOST] = {
+				.phy_sus	= { 0x0728, 15, 0, 0, 0x1d1 },
+				.ls_det_en	= { 0x0680, 4, 4, 0, 1 },
+				.ls_det_st	= { 0x0690, 4, 4, 0, 1 },
+				.ls_det_clr	= { 0x06a0, 4, 4, 0, 1 },
+				.utmi_ls	= { 0x049c, 14, 13, 0, 1 },
+				.utmi_hstdet	= { 0x049c, 12, 12, 0, 1 }
+			}
+		},
+	},
+	{ /* sentinel */ }
+};
+
+static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = {
+	{
+		.reg = 0xe450,
+		.num_ports	= 2,
+		.clkout_ctl	= { 0xe450, 4, 4, 1, 0 },
+		.port_cfgs	= {
+			[USB2PHY_PORT_HOST] = {
+				.phy_sus	= { 0xe458, 1, 0, 0x2, 0x1 },
+				.ls_det_en	= { 0xe3c0, 6, 6, 0, 1 },
+				.ls_det_st	= { 0xe3e0, 6, 6, 0, 1 },
+				.ls_det_clr	= { 0xe3d0, 6, 6, 0, 1 },
+				.utmi_ls	= { 0xe2ac, 22, 21, 0, 1 },
+				.utmi_hstdet	= { 0xe2ac, 23, 23, 0, 1 }
+			}
+		},
+	},
+	{
+		.reg = 0xe460,
+		.num_ports	= 2,
+		.clkout_ctl	= { 0xe460, 4, 4, 1, 0 },
+		.port_cfgs	= {
+			[USB2PHY_PORT_HOST] = {
+				.phy_sus	= { 0xe468, 1, 0, 0x2, 0x1 },
+				.ls_det_en	= { 0xe3c0, 11, 11, 0, 1 },
+				.ls_det_st	= { 0xe3e0, 11, 11, 0, 1 },
+				.ls_det_clr	= { 0xe3d0, 11, 11, 0, 1 },
+				.utmi_ls	= { 0xe2ac, 26, 25, 0, 1 },
+				.utmi_hstdet	= { 0xe2ac, 27, 27, 0, 1 }
+			}
+		},
+	},
+	{ /* sentinel */ }
+};
+
+static const struct of_device_id rockchip_usb2phy_dt_match[] = {
+	{ .compatible = "rockchip,rk3366-usb2phy", .data = &rk3366_phy_cfgs },
+	{ .compatible = "rockchip,rk3399-usb2phy", .data = &rk3399_phy_cfgs },
+	{}
+};
+MODULE_DEVICE_TABLE(of, rockchip_usb2phy_dt_match);
+
+static struct platform_driver rockchip_usb2phy_driver = {
+	.probe		= rockchip_usb2phy_probe,
+	.driver		= {
+		.name	= "rockchip-usb2phy",
+		.of_match_table = rockchip_usb2phy_dt_match,
+	},
+};
+module_platform_driver(rockchip_usb2phy_driver);
+
+MODULE_AUTHOR("Frank Wang <frank.wang at rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip USB2.0 PHY driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.9.5




From frank.wang at rock-chips.com  Tue Jul 19 00:28:31 2016
From: frank.wang at rock-chips.com (Frank Wang)
Date: Tue, 19 Jul 2016 15:28:31 +0800
Subject: [PATCH v8 3/3] arm64: dts: rockchip: add usb2-phy support for rk3399
Message-ID: <1468913311-23977-1-git-send-email-frank.wang@rock-chips.com>

Signed-off-by: Frank Wang <frank.wang at rock-chips.com>
---
 arch/arm64/boot/dts/rockchip/rk3399-evb.dts |   19 +++++++++++
 arch/arm64/boot/dts/rockchip/rk3399.dtsi    |   47 ++++++++++++++++++++++++++-
 2 files changed, 65 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/rockchip/rk3399-evb.dts b/arch/arm64/boot/dts/rockchip/rk3399-evb.dts
index 1a3eb14..31d4828 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-evb.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-evb.dts
@@ -69,6 +69,15 @@
 		regulator-max-microvolt = <3300000>;
 	};
 
+	vbus_host: vbus-host-regulator {
+		compatible = "regulator-fixed";
+		enable-active-high;
+		gpio = <&gpio4 25 GPIO_ACTIVE_HIGH>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&host_vbus_drv>;
+		regulator-name = "vbus_host";
+	};
+
 	vcc_phy: vcc-phy-regulator {
 		compatible = "regulator-fixed";
 		regulator-name = "vcc_phy";
@@ -93,6 +102,16 @@
 	status = "okay";
 };
 
+&u2phy0_host {
+	phy-supply = <&vbus_host>;
+	status = "okay";
+};
+
+&u2phy1_host {
+	phy-supply = <&vbus_host>;
+	status = "okay";
+};
+
 &usb_host0_ehci {
 	status = "okay";
 };
diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index d7f8e06..0383785 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -221,6 +221,8 @@
 		interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
 		clocks = <&cru HCLK_HOST0>, <&cru HCLK_HOST0_ARB>;
 		clock-names = "hclk_host0", "hclk_host0_arb";
+		phys = <&u2phy0_host>;
+		phy-names = "u2phy0";
 		status = "disabled";
 	};
 
@@ -239,6 +241,8 @@
 		interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
 		clocks = <&cru HCLK_HOST1>, <&cru HCLK_HOST1_ARB>;
 		clock-names = "hclk_host1", "hclk_host1_arb";
+		phys = <&u2phy1_host>;
+		phy-names = "u2phy1";
 		status = "disabled";
 	};
 
@@ -481,8 +485,42 @@
 	};
 
 	grf: syscon at ff770000 {
-		compatible = "rockchip,rk3399-grf", "syscon";
+		compatible = "rockchip,rk3399-grf", "syscon", "simple-mfd";
 		reg = <0x0 0xff770000 0x0 0x10000>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		u2phy0: usb2-phy at e450 {
+			compatible = "rockchip,rk3399-usb2phy";
+			reg = <0xe450 0x10>;
+			clocks = <&cru SCLK_USB2PHY0_REF>;
+			clock-names = "phyclk";
+			#clock-cells = <0>;
+			clock-output-names = "clk_usbphy0_480m";
+
+			u2phy0_host: host-port {
+				#phy-cells = <0>;
+				interrupts = <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>;
+				interrupt-names = "linestate";
+				status = "disabled";
+			};
+		};
+
+		u2phy1: usb2-phy at e460 {
+			compatible = "rockchip,rk3399-usb2phy";
+			reg = <0xe460 0x10>;
+			clocks = <&cru SCLK_USB2PHY1_REF>;
+			clock-names = "phyclk";
+			#clock-cells = <0>;
+			clock-output-names = "clk_usbphy1_480m";
+
+			u2phy1_host: host-port {
+				#phy-cells = <0>;
+				interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
+				interrupt-names = "linestate";
+				status = "disabled";
+			};
+		};
 	};
 
 	watchdog at ff840000 {
@@ -1009,5 +1047,12 @@
 					<1 14 RK_FUNC_1 &pcfg_pull_none>;
 			};
 		};
+
+		usb2 {
+			host_vbus_drv: host-vbus-drv {
+				rockchip,pins =
+					<4 25 RK_FUNC_GPIO &pcfg_pull_none>;
+			};
+		};
 	};
 };
-- 
1.7.9.5




From shawn.lin at rock-chips.com  Tue Jul 19 01:58:34 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Tue, 19 Jul 2016 16:58:34 +0800
Subject: [PATCH 1/2] mmc: core: move freqs table into core.h
Message-ID: <1468918715-24914-1-git-send-email-shawn.lin@rock-chips.com>

We will reuse it outside the core.c file, so let's
move it to the header file.

Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
---

 drivers/mmc/core/core.c | 2 --
 drivers/mmc/core/core.h | 2 ++
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 94cbf4e..0cec7f4 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -58,8 +58,6 @@
  */
 #define MMC_BKOPS_MAX_TIMEOUT	(4 * 60 * 1000) /* max time to wait in ms */
 
-static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
-
 /*
  * Enabling software CRCs on the data blocks can be a significant (30%)
  * performance cost, and for other reasons may not always be desired.
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index 0fa86a2..5456000 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -54,6 +54,8 @@ void mmc_power_off(struct mmc_host *host);
 void mmc_power_cycle(struct mmc_host *host, u32 ocr);
 void mmc_set_initial_state(struct mmc_host *host);
 
+static const unsigned int freqs[] = { 400000, 300000, 200000, 100000 };
+
 static inline void mmc_delay(unsigned int ms)
 {
 	if (ms < 1000 / HZ) {
-- 
2.3.7




From shawn.lin at rock-chips.com  Tue Jul 19 01:58:35 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Tue, 19 Jul 2016 16:58:35 +0800
Subject: [PATCH 2/2] mmc: core: fall back host->f_init if failing to init mmc
 card after resume
In-Reply-To: <1468918715-24914-1-git-send-email-shawn.lin@rock-chips.com>
References: <1468918715-24914-1-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <1468918715-24914-2-git-send-email-shawn.lin@rock-chips.com>

We observed the failure of initializing card after resume
accidentally. It's hard to reproduce but we did get report from
the suspend/resume test of our RK3399 mp test farm . Unfortunately,
we still fail to figure out what was going wrong at that time.
Also we can't achieve it by retrying the host->f_init without falling
back it. But this patch will solve the problem as we could add some log
there and see that we resume the mmc card successfully after falling
back the host->f_init. There is no obvious side effect found, so it seems
this patch will improve the stability.

[   93.405085] mmc1: unexpected status 0x800900 after switch
[   93.408474] mmc1: switch to bus width 1 failed
[   93.408482] mmc1: mmc_select_hs200 failed, error -110
[   93.408492] mmc1: error -110 during resume (card was removed?)
[   93.408705] PM: resume of devices complete after 213.453 msecs

Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
---

 drivers/mmc/core/mmc.c | 22 ++++++++++++++++++++--
 1 file changed, 20 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 403b97b..bef40c8 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -1945,6 +1945,7 @@ static int mmc_suspend(struct mmc_host *host)
 static int _mmc_resume(struct mmc_host *host)
 {
 	int err = 0;
+	int i;
 
 	BUG_ON(!host);
 	BUG_ON(!host->card);
@@ -1954,8 +1955,25 @@ static int _mmc_resume(struct mmc_host *host)
 	if (!mmc_card_suspended(host->card))
 		goto out;
 
-	mmc_power_up(host, host->card->ocr);
-	err = mmc_init_card(host, host->card->ocr, host->card);
+	/*
+	 * Let's try to fallback the host->f_init
+	 * if failing to init card after resume.
+	 */
+
+	for (i = 0; i < ARRAY_SIZE(freqs); i++) {
+		if (host->f_init < freqs[i])
+			continue;
+		else
+			host->f_init = freqs[i];
+
+		mmc_power_up(host, host->card->ocr);
+		err = mmc_init_card(host, host->card->ocr, host->card);
+		if (!err)
+			break;
+
+		mmc_power_off(host);
+	}
+
 	mmc_card_clr_suspended(host->card);
 
 out:
-- 
2.3.7




From ulf.hansson at linaro.org  Tue Jul 19 03:31:24 2016
From: ulf.hansson at linaro.org (Ulf Hansson)
Date: Tue, 19 Jul 2016 12:31:24 +0200
Subject: [PATCH 2/2] mmc: core: fall back host->f_init if failing to init
 mmc card after resume
In-Reply-To: <1468918715-24914-2-git-send-email-shawn.lin@rock-chips.com>
References: <1468918715-24914-1-git-send-email-shawn.lin@rock-chips.com>
 <1468918715-24914-2-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <CAPDyKFq9ZogJjGyR6gB3QxnJeP_5XH-GXKY6+-HXsOBDDTAMqQ@mail.gmail.com>

On 19 July 2016 at 10:58, Shawn Lin <shawn.lin at rock-chips.com> wrote:
> We observed the failure of initializing card after resume
> accidentally. It's hard to reproduce but we did get report from
> the suspend/resume test of our RK3399 mp test farm . Unfortunately,
> we still fail to figure out what was going wrong at that time.
> Also we can't achieve it by retrying the host->f_init without falling
> back it. But this patch will solve the problem as we could add some log
> there and see that we resume the mmc card successfully after falling
> back the host->f_init. There is no obvious side effect found, so it seems
> this patch will improve the stability.

What f_init did the original rescan work find out being successful?

Did you then verify that it was *another* frequency that made the
initialization to work in the resume?

>
> [   93.405085] mmc1: unexpected status 0x800900 after switch
> [   93.408474] mmc1: switch to bus width 1 failed
> [   93.408482] mmc1: mmc_select_hs200 failed, error -110
> [   93.408492] mmc1: error -110 during resume (card was removed?)
> [   93.408705] PM: resume of devices complete after 213.453 msecs
>
> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
> ---
>
>  drivers/mmc/core/mmc.c | 22 ++++++++++++++++++++--
>  1 file changed, 20 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
> index 403b97b..bef40c8 100644
> --- a/drivers/mmc/core/mmc.c
> +++ b/drivers/mmc/core/mmc.c
> @@ -1945,6 +1945,7 @@ static int mmc_suspend(struct mmc_host *host)
>  static int _mmc_resume(struct mmc_host *host)
>  {
>         int err = 0;
> +       int i;
>
>         BUG_ON(!host);
>         BUG_ON(!host->card);
> @@ -1954,8 +1955,25 @@ static int _mmc_resume(struct mmc_host *host)
>         if (!mmc_card_suspended(host->card))
>                 goto out;
>
> -       mmc_power_up(host, host->card->ocr);
> -       err = mmc_init_card(host, host->card->ocr, host->card);
> +       /*
> +        * Let's try to fallback the host->f_init
> +        * if failing to init card after resume.
> +        */
> +
> +       for (i = 0; i < ARRAY_SIZE(freqs); i++) {
> +               if (host->f_init < freqs[i])
> +                       continue;
> +               else
> +                       host->f_init = freqs[i];

This loop is wrong, as you don't consider that host->f_min may not
exactly match the frequencies in the freqs array.

> +
> +               mmc_power_up(host, host->card->ocr);
> +               err = mmc_init_card(host, host->card->ocr, host->card);
> +               if (!err)
> +                       break;
> +
> +               mmc_power_off(host);

The mmc core expects the host/card to be powered up after a resume, as
it may send commands to it without first invoking mmc_power_up().
Even if those commands may fail (or timeout), at least those doesn't
hang which may be the case if the host/card isn't powered up first.

So, you must *not* leave the host/card in "mmc_power_off()" state in
_mmc_resume().

[...]

Kind regards
Uffe


From weiyj_lk at 163.com  Tue Jul 19 04:30:36 2016
From: weiyj_lk at 163.com (Wei Yongjun)
Date: Tue, 19 Jul 2016 11:30:36 +0000
Subject: [PATCH -next] drm/rockchip: analogix_dp: remove .owner field for
 driver
Message-ID: <1468927836-877-1-git-send-email-weiyj_lk@163.com>

From: Wei Yongjun <yongjun_wei at trendmicro.com.cn>

Remove .owner field if calls are used which set it automatically.

Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci

Signed-off-by: Wei Yongjun <yongjun_wei at trendmicro.com.cn>
---
 drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
index e81e19a..6e36e0f 100644
--- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
@@ -443,7 +443,6 @@ static struct platform_driver rockchip_dp_driver = {
 	.remove = rockchip_dp_remove,
 	.driver = {
 		   .name = "rockchip-dp",
-		   .owner = THIS_MODULE,
 		   .pm = &rockchip_dp_pm_ops,
 		   .of_match_table = of_match_ptr(rockchip_dp_dt_ids),
 	},






From weiyj_lk at 163.com  Tue Jul 19 04:32:43 2016
From: weiyj_lk at 163.com (Wei Yongjun)
Date: Tue, 19 Jul 2016 11:32:43 +0000
Subject: [PATCH -next] drm/rockchip: analogix_dp: add missing
 clk_disable_unprepare() on error
Message-ID: <1468927963-2947-1-git-send-email-weiyj_lk@163.com>

From: Wei Yongjun <yongjun_wei at trendmicro.com.cn>

Add the missing clk_disable_unprepare() before return in the
error handling case.

Signed-off-by: Wei Yongjun <yongjun_wei at trendmicro.com.cn>
---
 drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
index e81e19a..89aadbf 100644
--- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
@@ -96,6 +96,7 @@ static int rockchip_dp_poweron(struct analogix_dp_plat_data *plat_data)
 	ret = rockchip_dp_pre_init(dp);
 	if (ret < 0) {
 		dev_err(dp->dev, "failed to dp pre init %d\n", ret);
+		clk_disable_unprepare(dp->pclk);
 		return ret;
 	}
 
@@ -272,6 +273,7 @@ static int rockchip_dp_init(struct rockchip_dp_device *dp)
 	ret = rockchip_dp_pre_init(dp);
 	if (ret < 0) {
 		dev_err(dp->dev, "failed to pre init %d\n", ret);
+		clk_disable_unprepare(dp->pclk);
 		return ret;
 	}




From geert at linux-m68k.org  Tue Jul 19 06:25:30 2016
From: geert at linux-m68k.org (Geert Uytterhoeven)
Date: Tue, 19 Jul 2016 15:25:30 +0200
Subject: [PATCH v2] drivers/perf: arm-pmu: Handle per-interrupt affinity
 mask
In-Reply-To: <1467815627-4061-1-git-send-email-marc.zyngier@arm.com>
References: <1467815627-4061-1-git-send-email-marc.zyngier@arm.com>
Message-ID: <CAMuHMdXU6C=jFD16Jj3NFNawiY0YEpQDZSbL+T4bLgUOG3nTUQ@mail.gmail.com>

Hi Marc, Catalin, Will,

On Wed, Jul 6, 2016 at 4:33 PM, Marc Zyngier <marc.zyngier at arm.com> wrote:
> On a big-little system, PMUs can be wired to CPUs using per CPU
> interrups (PPI). In this case, it is important to make sure that
> the enable/disable do happen on the right set of CPUs.
>
> So instead of relying on the interrupt-affinity property, we can
> use the actual percpu affinity that DT exposes as part of the
> interrupt specifier. The DT binding is also updated to reflect
> the fact that the interrupt-affinity property shouldn't be used
> in that case.
>
> Signed-off-by: Marc Zyngier <marc.zyngier at arm.com>
> ---
> * From v1:
>   - propagate the error if irq_get_percpu_devid_partition fails

This patch, which is commit 19a469a58720ea96 in arm64/for-next/core, broke
the PMU on r8a7740/armadillo800eva:

    -hw perfevents: enabled with armv7_cortex_a9 PMU driver, 7
counters available
    +hw perfevents: /pmu: failed to probe PMU!
    +hw perfevents: /pmu: failed to register PMU devices!
    +armv7-pmu: probe of pmu failed with error -22

This is a single-core Cortex A9.

>  Documentation/devicetree/bindings/arm/pmu.txt |  4 +++-
>  drivers/perf/arm_pmu.c                        | 27 ++++++++++++++++++++++-----
>  2 files changed, 25 insertions(+), 6 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/arm/pmu.txt b/Documentation/devicetree/bindings/arm/pmu.txt
> index 74d5417..61c8b46 100644
> --- a/Documentation/devicetree/bindings/arm/pmu.txt
> +++ b/Documentation/devicetree/bindings/arm/pmu.txt
> @@ -39,7 +39,9 @@ Optional properties:
>                         When using a PPI, specifies a list of phandles to CPU
>                        nodes corresponding to the set of CPUs which have
>                        a PMU of this type signalling the PPI listed in the
> -                      interrupts property.
> +                      interrupts property, unless this is already specified
> +                      by the PPI interrupt specifier itself (in which case
> +                      the interrupt-affinity property shouldn't be present).
>
>                         This property should be present when there is more than
>                        a single SPI.

On a single core, there's only a single SPI, hence there's no need for an
"interrupt-affinity" property.

> diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
> index 140436a..8e4d7f5 100644
> --- a/drivers/perf/arm_pmu.c
> +++ b/drivers/perf/arm_pmu.c
> @@ -961,9 +964,23 @@ static int of_pmu_irq_cfg(struct arm_pmu *pmu)
>                 i++;
>         } while (1);
>
> -       /* If we didn't manage to parse anything, claim to support all CPUs */
> -       if (cpumask_weight(&pmu->supported_cpus) == 0)
> -               cpumask_setall(&pmu->supported_cpus);
> +       /* If we didn't manage to parse anything, try the interrupt affinity */
> +       if (cpumask_weight(&pmu->supported_cpus) == 0) {
> +               if (!using_spi) {

However, using_spi is never set to true in the absence of that property,
causing the wrong branch to be taken...

> +                       /* If using PPIs, check the affinity of the partition */
> +                       int ret, irq;
> +
> +                       irq = platform_get_irq(pdev, 0);
> +                       ret = irq_get_percpu_devid_partition(irq, &pmu->supported_cpus);

... and ret to become -22 here.

> +                       if (ret) {
> +                               kfree(irqs);
> +                               return ret;
> +                       }
> +               } else {
> +                       /* Otherwise default to all CPUs */
> +                       cpumask_setall(&pmu->supported_cpus);
> +               }
> +       }
>
>         /* If we matched up the IRQ affinities, use them to route the SPIs */
>         if (using_spi && i == pdev->num_resources)

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert at linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds


From marc.zyngier at arm.com  Tue Jul 19 06:46:40 2016
From: marc.zyngier at arm.com (Marc Zyngier)
Date: Tue, 19 Jul 2016 14:46:40 +0100
Subject: [PATCH v2] drivers/perf: arm-pmu: Handle per-interrupt affinity
 mask
In-Reply-To: <CAMuHMdXU6C=jFD16Jj3NFNawiY0YEpQDZSbL+T4bLgUOG3nTUQ@mail.gmail.com>
References: <1467815627-4061-1-git-send-email-marc.zyngier@arm.com>
 <CAMuHMdXU6C=jFD16Jj3NFNawiY0YEpQDZSbL+T4bLgUOG3nTUQ@mail.gmail.com>
Message-ID: <578E2F40.1000309@arm.com>

Hi Geert,

On 19/07/16 14:25, Geert Uytterhoeven wrote:
> Hi Marc, Catalin, Will,
> 
> On Wed, Jul 6, 2016 at 4:33 PM, Marc Zyngier <marc.zyngier at arm.com> wrote:
>> On a big-little system, PMUs can be wired to CPUs using per CPU
>> interrups (PPI). In this case, it is important to make sure that
>> the enable/disable do happen on the right set of CPUs.
>>
>> So instead of relying on the interrupt-affinity property, we can
>> use the actual percpu affinity that DT exposes as part of the
>> interrupt specifier. The DT binding is also updated to reflect
>> the fact that the interrupt-affinity property shouldn't be used
>> in that case.
>>
>> Signed-off-by: Marc Zyngier <marc.zyngier at arm.com>
>> ---
>> * From v1:
>>   - propagate the error if irq_get_percpu_devid_partition fails
> 
> This patch, which is commit 19a469a58720ea96 in arm64/for-next/core, broke
> the PMU on r8a7740/armadillo800eva:
> 
>     -hw perfevents: enabled with armv7_cortex_a9 PMU driver, 7
> counters available
>     +hw perfevents: /pmu: failed to probe PMU!
>     +hw perfevents: /pmu: failed to register PMU devices!
>     +armv7-pmu: probe of pmu failed with error -22
> 
> This is a single-core Cortex A9.
> 
>>  Documentation/devicetree/bindings/arm/pmu.txt |  4 +++-
>>  drivers/perf/arm_pmu.c                        | 27 ++++++++++++++++++++++-----
>>  2 files changed, 25 insertions(+), 6 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/arm/pmu.txt b/Documentation/devicetree/bindings/arm/pmu.txt
>> index 74d5417..61c8b46 100644
>> --- a/Documentation/devicetree/bindings/arm/pmu.txt
>> +++ b/Documentation/devicetree/bindings/arm/pmu.txt
>> @@ -39,7 +39,9 @@ Optional properties:
>>                         When using a PPI, specifies a list of phandles to CPU
>>                        nodes corresponding to the set of CPUs which have
>>                        a PMU of this type signalling the PPI listed in the
>> -                      interrupts property.
>> +                      interrupts property, unless this is already specified
>> +                      by the PPI interrupt specifier itself (in which case
>> +                      the interrupt-affinity property shouldn't be present).
>>
>>                         This property should be present when there is more than
>>                        a single SPI.
> 
> On a single core, there's only a single SPI, hence there's no need for an
> "interrupt-affinity" property.
> 
>> diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
>> index 140436a..8e4d7f5 100644
>> --- a/drivers/perf/arm_pmu.c
>> +++ b/drivers/perf/arm_pmu.c
>> @@ -961,9 +964,23 @@ static int of_pmu_irq_cfg(struct arm_pmu *pmu)
>>                 i++;
>>         } while (1);
>>
>> -       /* If we didn't manage to parse anything, claim to support all CPUs */
>> -       if (cpumask_weight(&pmu->supported_cpus) == 0)
>> -               cpumask_setall(&pmu->supported_cpus);
>> +       /* If we didn't manage to parse anything, try the interrupt affinity */
>> +       if (cpumask_weight(&pmu->supported_cpus) == 0) {
>> +               if (!using_spi) {
> 
> However, using_spi is never set to true in the absence of that property,
> causing the wrong branch to be taken...
> 
>> +                       /* If using PPIs, check the affinity of the partition */
>> +                       int ret, irq;
>> +
>> +                       irq = platform_get_irq(pdev, 0);
>> +                       ret = irq_get_percpu_devid_partition(irq, &pmu->supported_cpus);
> 
> ... and ret to become -22 here.

Thanks for the thorough analysis. Could you please give the following
patchlet a go:

diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
index 2513365..9275e08 100644
--- a/drivers/perf/arm_pmu.c
+++ b/drivers/perf/arm_pmu.c
@@ -958,11 +958,12 @@ static int of_pmu_irq_cfg(struct arm_pmu *pmu)
 
 	/* If we didn't manage to parse anything, try the interrupt affinity */
 	if (cpumask_weight(&pmu->supported_cpus) == 0) {
-		if (!using_spi) {
+		int irq = platform_get_irq(pdev, 0);
+
+		if (irq_is_percpu(irq)) {
 			/* If using PPIs, check the affinity of the partition */
-			int ret, irq;
+			int ret;
 
-			irq = platform_get_irq(pdev, 0);
 			ret = irq_get_percpu_devid_partition(irq, &pmu->supported_cpus);
 			if (ret) {
 				kfree(irqs);


and let me know if that helps?

Thanks,

	M.
-- 
Jazz is not dead. It just smells funny...


From geert at linux-m68k.org  Tue Jul 19 07:24:07 2016
From: geert at linux-m68k.org (Geert Uytterhoeven)
Date: Tue, 19 Jul 2016 16:24:07 +0200
Subject: [PATCH v2] drivers/perf: arm-pmu: Handle per-interrupt affinity
 mask
In-Reply-To: <578E2F40.1000309@arm.com>
References: <1467815627-4061-1-git-send-email-marc.zyngier@arm.com>
 <CAMuHMdXU6C=jFD16Jj3NFNawiY0YEpQDZSbL+T4bLgUOG3nTUQ@mail.gmail.com>
 <578E2F40.1000309@arm.com>
Message-ID: <CAMuHMdV6tUvegGebYkdL0Z+h0a7qf=hFDW2xUJESt_68-SV2Yg@mail.gmail.com>

Hi Marc,

On Tue, Jul 19, 2016 at 3:46 PM, Marc Zyngier <marc.zyngier at arm.com> wrote:
> On 19/07/16 14:25, Geert Uytterhoeven wrote:
>> On Wed, Jul 6, 2016 at 4:33 PM, Marc Zyngier <marc.zyngier at arm.com> wrote:
>>> On a big-little system, PMUs can be wired to CPUs using per CPU
>>> interrups (PPI). In this case, it is important to make sure that
>>> the enable/disable do happen on the right set of CPUs.
>>>
>>> So instead of relying on the interrupt-affinity property, we can
>>> use the actual percpu affinity that DT exposes as part of the
>>> interrupt specifier. The DT binding is also updated to reflect
>>> the fact that the interrupt-affinity property shouldn't be used
>>> in that case.
>>>
>>> Signed-off-by: Marc Zyngier <marc.zyngier at arm.com>
>>> ---
>>> * From v1:
>>>   - propagate the error if irq_get_percpu_devid_partition fails
>>
>> This patch, which is commit 19a469a58720ea96 in arm64/for-next/core, broke
>> the PMU on r8a7740/armadillo800eva:
>>
>>     -hw perfevents: enabled with armv7_cortex_a9 PMU driver, 7
>> counters available
>>     +hw perfevents: /pmu: failed to probe PMU!
>>     +hw perfevents: /pmu: failed to register PMU devices!
>>     +armv7-pmu: probe of pmu failed with error -22

> Thanks for the thorough analysis. Could you please give the following
> patchlet a go:
>
> diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c
> index 2513365..9275e08 100644
> --- a/drivers/perf/arm_pmu.c
> +++ b/drivers/perf/arm_pmu.c
> @@ -958,11 +958,12 @@ static int of_pmu_irq_cfg(struct arm_pmu *pmu)
>
>         /* If we didn't manage to parse anything, try the interrupt affinity */
>         if (cpumask_weight(&pmu->supported_cpus) == 0) {
> -               if (!using_spi) {
> +               int irq = platform_get_irq(pdev, 0);
> +
> +               if (irq_is_percpu(irq)) {
>                         /* If using PPIs, check the affinity of the partition */
> -                       int ret, irq;
> +                       int ret;
>
> -                       irq = platform_get_irq(pdev, 0);
>                         ret = irq_get_percpu_devid_partition(irq, &pmu->supported_cpus);
>                         if (ret) {
>                                 kfree(irqs);
>
>
> and let me know if that helps?

Thanks, that fixes it, as expected.

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert at linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds


From seanpaul at chromium.org  Tue Jul 19 09:44:00 2016
From: seanpaul at chromium.org (Sean Paul)
Date: Tue, 19 Jul 2016 12:44:00 -0400
Subject: [v5.2 PATCH 5/5] drm/rockchip: cdn-dp: add cdn DP support for
 rk3399
In-Reply-To: <1468570693-25198-1-git-send-email-zyw@rock-chips.com>
References: <CAOw6vbKCCoQHO-4TB3qCOuAA09aheXx0H6jRjQQnrkb4KqmtCg@mail.gmail.com>
 <1468570693-25198-1-git-send-email-zyw@rock-chips.com>
Message-ID: <20160719164400.GA9958@seanpaul0.roam.corp.google.com>

On Fri, Jul 15, 2016 at 04:18:13PM +0800, Chris Zhong wrote:
> Add support for cdn DP controller which is embedded in the rk3399
> SoCs. The DP is compliant with DisplayPort Specification,
> Version 1.3, This IP is compatible with the rockchip type-c PHY IP.
> There is a uCPU in DP controller, it need a firmware to work,
> please put the firmware file to /lib/firmware/cdn/dptx.bin. The
> uCPU in charge of aux communication and link training, the host use
> mailbox to communicate with the ucpu.
> The dclk pin_pol of vop must not be invert for DP.
> 
> Signed-off-by: Chris Zhong <zyw at rock-chips.com>
> 

I still think there are gremlins lurking in the workqueue code, but I think
it's probably worth merging this initial version and iterating on it. I'm going
to play around with it this week.

Reviewed-by: Sean Paul <seanpaul at chromium.org>


> ---
> 
> Changes in v5.2:
> - fixed the fw_wait always 0
> 
> Changes in v5.1:
> - modify according to Sean Paul's comments
> 
> Changes in v5:
> - alphabetical order
> - do not use long, use u32 or u64
> - return MODE_CLOCK_HIGH when requested > actual
> - Optimized Coding Style
> - add a formula to get better tu size and symbol value.
> 
> Changes in v4:
> - use phy framework to control DP phy
> - support 2 phys
> 
> Changes in v3:
> - use EXTCON_DISP_DP and EXTCON_DISP_DP_ALT cable to get dp port state.
> - reset spdif before config it
> - modify the firmware clk to 100Mhz
> - retry load firmware if fw file is requested too early
> 
> Changes in v2:
> - Alphabetic order
> - remove excess error message
> - use define clk_rate
> - check all return value
> - remove dev_set_name(dp->dev, "cdn-dp");
> - use schedule_delayed_work
> - remove never-called functions
> - remove some unnecessary ()
> 
> Changes in v1:
> - use extcon API
> - use hdmi-codec for the DP Asoc
> - do not initialize the "ret"
> - printk a err log when drm_of_encoder_active_endpoint_id
> - modify the dclk pin_pol to a single line
> 
>  drivers/gpu/drm/rockchip/Kconfig            |   9 +
>  drivers/gpu/drm/rockchip/Makefile           |   1 +
>  drivers/gpu/drm/rockchip/cdn-dp-core.c      | 842 ++++++++++++++++++++++++++
>  drivers/gpu/drm/rockchip/cdn-dp-core.h      |  99 +++
>  drivers/gpu/drm/rockchip/cdn-dp-reg.c       | 898 ++++++++++++++++++++++++++++
>  drivers/gpu/drm/rockchip/cdn-dp-reg.h       | 479 +++++++++++++++
>  drivers/gpu/drm/rockchip/rockchip_drm_vop.c |  11 +-
>  drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   9 +
>  drivers/gpu/drm/rockchip/rockchip_vop_reg.c |   2 +
>  9 files changed, 2347 insertions(+), 3 deletions(-)
>  create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.c
>  create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.h
>  create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.c
>  create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.h
> 
> diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
> index d30bdc3..a04446a 100644
> --- a/drivers/gpu/drm/rockchip/Kconfig
> +++ b/drivers/gpu/drm/rockchip/Kconfig
> @@ -25,6 +25,15 @@ config ROCKCHIP_ANALOGIX_DP
>  	  for the Analogix Core DP driver. If you want to enable DP
>  	  on RK3288 based SoC, you should selet this option.
>  
> +config ROCKCHIP_CDN_DP
> +        tristate "Rockchip cdn DP"
> +        depends on DRM_ROCKCHIP
> +        help
> +	  This selects support for Rockchip SoC specific extensions
> +	  for the cdn DP driver. If you want to enable Dp on
> +	  RK3399 based SoC, you should select this
> +	  option.
> +
>  config ROCKCHIP_DW_HDMI
>          tristate "Rockchip specific extensions for Synopsys DW HDMI"
>          depends on DRM_ROCKCHIP
> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
> index 05d0713..abdecd5 100644
> --- a/drivers/gpu/drm/rockchip/Makefile
> +++ b/drivers/gpu/drm/rockchip/Makefile
> @@ -7,6 +7,7 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
>  rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
>  
>  obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
> +obj-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
>  obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
>  obj-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
>  obj-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
> new file mode 100644
> index 0000000..ed38dbc
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
> @@ -0,0 +1,842 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author: Chris Zhong <zyw at rock-chips.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * 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 <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_dp_helper.h>
> +#include <drm/drm_edid.h>
> +#include <drm/drm_of.h>
> +
> +#include <linux/clk.h>
> +#include <linux/component.h>
> +#include <linux/extcon.h>
> +#include <linux/firmware.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/phy/phy.h>
> +
> +#include <sound/hdmi-codec.h>
> +
> +#include "cdn-dp-core.h"
> +#include "cdn-dp-reg.h"
> +#include "rockchip_drm_vop.h"
> +
> +#define connector_to_dp(c) \
> +		container_of(c, struct cdn_dp_device, connector)
> +
> +#define encoder_to_dp(c) \
> +		container_of(c, struct cdn_dp_device, encoder)
> +
> +/* dp grf register offset */
> +#define DP_VOP_SEL		0x6224
> +#define DP_SEL_VOP_LIT		BIT(12)
> +#define MAX_FW_WAIT_SECS	64
> +#define CDN_DP_FIRMWARE		"rockchip/dptx.bin"
> +
> +struct cdn_dp_data {
> +	u8 max_phy;
> +};
> +
> +struct cdn_dp_data rk3399_cdn_dp = {
> +	.max_phy = 2,
> +};
> +
> +static const struct of_device_id cdn_dp_dt_ids[] = {
> +	{ .compatible = "rockchip,rk3399-cdn-dp",
> +		.data = (void *)&rk3399_cdn_dp },
> +	{}
> +};
> +
> +MODULE_DEVICE_TABLE(of, cdn_dp_dt_ids);
> +
> +static int cdn_dp_clk_enable(struct cdn_dp_device *dp)
> +{
> +	int ret;
> +	u32 rate;
> +
> +	ret = clk_prepare_enable(dp->pclk);
> +	if (ret < 0) {
> +		dev_err(dp->dev, "cannot enable dp pclk %d\n", ret);
> +		goto err_pclk;
> +	}
> +
> +	ret = clk_prepare_enable(dp->core_clk);
> +	if (ret < 0) {
> +		dev_err(dp->dev, "cannot enable core_clk %d\n", ret);
> +		goto err_core_clk;
> +	}
> +
> +	rate = clk_get_rate(dp->core_clk);
> +	if (rate < 0) {
> +		dev_err(dp->dev, "get clk rate failed: %d\n", rate);
> +		goto err_set_rate;
> +	}
> +
> +	cdn_dp_set_fw_clk(dp, rate);
> +
> +	return 0;
> +
> +err_set_rate:
> +	clk_disable_unprepare(dp->core_clk);
> +err_core_clk:
> +	clk_disable_unprepare(dp->pclk);
> +err_pclk:
> +	return ret;
> +}
> +
> +static enum drm_connector_status
> +cdn_dp_connector_detect(struct drm_connector *connector, bool force)
> +{
> +	struct cdn_dp_device *dp = connector_to_dp(connector);
> +
> +	return dp->hpd_status;
> +}
> +
> +static void cdn_dp_connector_destroy(struct drm_connector *connector)
> +{
> +	drm_connector_unregister(connector);
> +	drm_connector_cleanup(connector);
> +}
> +
> +static struct drm_connector_funcs cdn_dp_atomic_connector_funcs = {
> +	.dpms = drm_atomic_helper_connector_dpms,
> +	.detect = cdn_dp_connector_detect,
> +	.destroy = cdn_dp_connector_destroy,
> +	.fill_modes = drm_helper_probe_single_connector_modes,
> +	.reset = drm_atomic_helper_connector_reset,
> +	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +static int cdn_dp_connector_get_modes(struct drm_connector *connector)
> +{
> +	struct cdn_dp_device *dp = connector_to_dp(connector);
> +	struct edid *edid;
> +	int ret = 0;
> +
> +	edid = drm_do_get_edid(connector, cdn_dp_get_edid_block, dp);
> +	if (edid) {
> +		dev_dbg(dp->dev, "got edid: width[%d] x height[%d]\n",
> +			edid->width_cm, edid->height_cm);
> +
> +		dp->sink_has_audio = drm_detect_monitor_audio(edid);
> +		ret = drm_add_edid_modes(connector, edid);
> +		if (ret) {
> +			drm_mode_connector_update_edid_property(connector,
> +								edid);
> +			drm_edid_to_eld(connector, edid);
> +		}
> +		kfree(edid);
> +	} else {
> +		dev_warn(dp->dev, "failed to get edid\n");
> +	}
> +
> +	return ret;
> +}
> +
> +static struct drm_encoder *
> +	cdn_dp_connector_best_encoder(struct drm_connector *connector)
> +{
> +	struct cdn_dp_device *dp = connector_to_dp(connector);
> +
> +	return &dp->encoder;
> +}
> +
> +static int cdn_dp_connector_mode_valid(struct drm_connector *connector,
> +				       struct drm_display_mode *mode)
> +{
> +	struct cdn_dp_device *dp = connector_to_dp(connector);
> +	struct drm_display_info *display_info = &dp->connector.display_info;
> +	u32 requested = mode->clock * display_info->bpc * 3 / 1000;
> +	u32 sink_max, source_max, rate;
> +	u8 lanes, id;
> +	u32 actual;
> +
> +	/* find the running port id */
> +	for (id = 0; id < dp->max_phy; id++) {
> +		if (dp->phy_status[id] == 1)
> +			break;
> +	}
> +
> +	sink_max = drm_dp_max_lane_count(dp->dpcd);
> +	source_max = dp->cap_lanes[id];
> +	lanes = min(sink_max, source_max);
> +
> +	sink_max = drm_dp_max_link_rate(dp->dpcd);
> +	source_max = drm_dp_bw_code_to_link_rate(CDN_DP_MAX_LINK_RATE);
> +	rate = min(sink_max, source_max);
> +
> +	actual = rate * lanes / 100;
> +
> +	/* efficiency is about 0.8 */
> +	actual = actual * 8 / 10;
> +
> +	if (requested > actual) {
> +		dev_dbg(dp->dev, "requested=%d, actual=%d, clock=%d\n",
> +			requested, actual, mode->clock);
> +		return MODE_CLOCK_HIGH;
> +	}
> +
> +	return MODE_OK;
> +}
> +
> +static struct drm_connector_helper_funcs cdn_dp_connector_helper_funcs = {
> +	.get_modes = cdn_dp_connector_get_modes,
> +	.best_encoder = cdn_dp_connector_best_encoder,
> +	.mode_valid = cdn_dp_connector_mode_valid,
> +};
> +
> +static void cdn_dp_commit(struct drm_encoder *encoder)
> +{
> +	struct cdn_dp_device *dp = encoder_to_dp(encoder);
> +	int ret;
> +
> +	ret = cdn_dp_training_start(dp);
> +	if (ret) {
> +		dev_err(dp->dev, "link training failed: %d\n", ret);
> +		return;
> +	}
> +
> +	ret = cdn_dp_get_training_status(dp);
> +	if (ret) {
> +		dev_err(dp->dev, "get link training status failed: %d\n", ret);
> +		return;
> +	}
> +
> +	dev_info(dp->dev, "rate:%d, lanes:%d\n",
> +		 dp->link.rate, dp->link.num_lanes);
> +
> +	if (cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE))
> +		return;
> +
> +	if (cdn_dp_config_video(dp)) {
> +		dev_err(dp->dev, "unable to config video\n");
> +		return;
> +	}
> +
> +	if (cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID))
> +		return;
> +
> +	dp->dpms_mode = DRM_MODE_DPMS_ON;
> +}
> +
> +static void cdn_dp_encoder_mode_set(struct drm_encoder *encoder,
> +				    struct drm_display_mode *mode,
> +				    struct drm_display_mode *adjusted)
> +{
> +	struct cdn_dp_device *dp = encoder_to_dp(encoder);
> +	struct drm_display_info *display_info = &dp->connector.display_info;
> +	struct rockchip_crtc_state *state;
> +	struct video_info *video = &dp->video_info;
> +	int ret, val;
> +
> +	switch (display_info->bpc) {
> +	case 16:
> +	case 12:
> +	case 10:
> +		video->color_depth = 10;
> +		break;
> +	case 6:
> +		video->color_depth = 6;
> +		break;
> +	default:
> +		video->color_depth = 8;
> +		break;
> +	}
> +
> +	video->color_fmt = PXL_RGB;
> +
> +	video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC);
> +	video->h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC);
> +
> +	ret = drm_of_encoder_active_endpoint_id(dp->dev->of_node, encoder);
> +	if (ret < 0) {
> +		dev_err(dp->dev, "Could not get vop id, %d", ret);
> +		return;
> +	}
> +
> +	dev_dbg(dp->dev, "vop %s output to cdn-dp\n", (ret) ? "LIT" : "BIG");
> +	state = to_rockchip_crtc_state(encoder->crtc->state);
> +	if (ret) {
> +		val = DP_SEL_VOP_LIT | (DP_SEL_VOP_LIT << 16);
> +		state->output_mode = ROCKCHIP_OUT_MODE_P888;
> +	} else {
> +		val = DP_SEL_VOP_LIT << 16;
> +		state->output_mode = ROCKCHIP_OUT_MODE_AAAA;
> +	}
> +	ret = regmap_write(dp->grf, DP_VOP_SEL, val);
> +	if (ret != 0)
> +		dev_err(dp->dev, "Could not write to GRF: %d\n", ret);
> +
> +	memcpy(&dp->mode, adjusted, sizeof(*mode));
> +}
> +
> +static void cdn_dp_encoder_enable(struct drm_encoder *encoder)
> +{
> +	struct cdn_dp_device *dp = encoder_to_dp(encoder);
> +
> +	if (dp->dpms_mode != DRM_MODE_DPMS_ON)
> +		cdn_dp_commit(encoder);
> +}
> +
> +static void cdn_dp_encoder_disable(struct drm_encoder *encoder)
> +{
> +	struct cdn_dp_device *dp = encoder_to_dp(encoder);
> +
> +	if (dp->dpms_mode != DRM_MODE_DPMS_OFF) {
> +		cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE);
> +		dp->dpms_mode = DRM_MODE_DPMS_OFF;
> +	}
> +}
> +
> +static int
> +cdn_dp_encoder_atomic_check(struct drm_encoder *encoder,
> +			    struct drm_crtc_state *crtc_state,
> +			    struct drm_connector_state *conn_state)
> +{
> +	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
> +
> +	s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
> +	s->output_type = DRM_MODE_CONNECTOR_DisplayPort;
> +
> +	return 0;
> +}
> +
> +static struct drm_encoder_helper_funcs cdn_dp_encoder_helper_funcs = {
> +	.mode_set = cdn_dp_encoder_mode_set,
> +	.enable = cdn_dp_encoder_enable,
> +	.disable = cdn_dp_encoder_disable,
> +	.atomic_check = cdn_dp_encoder_atomic_check,
> +};
> +
> +static struct drm_encoder_funcs cdn_dp_encoder_funcs = {
> +	.destroy = drm_encoder_cleanup,
> +};
> +
> +static int cdn_dp_firmware_init(struct cdn_dp_device *dp)
> +{
> +	int ret;
> +	const u32 *iram_data, *dram_data;
> +	const struct firmware *fw = dp->fw;
> +	const struct cdn_firmware_header *hdr;
> +
> +	hdr = (struct cdn_firmware_header *)fw->data;
> +	if (fw->size != le32_to_cpu(hdr->size_bytes)) {
> +		dev_err(dp->dev, "firmware is invalid\n");
> +		return -EINVAL;
> +	}
> +
> +	iram_data = (const u32 *)(fw->data + hdr->header_size);
> +	dram_data = (const u32 *)(fw->data + hdr->header_size + hdr->iram_size);
> +
> +	ret = cdn_dp_load_firmware(dp, iram_data, hdr->iram_size,
> +				   dram_data, hdr->dram_size);
> +	if (ret)
> +		return ret;
> +
> +	dp->fw_loaded = 1;
> +
> +	ret = cdn_dp_active(dp, true);
> +	if (ret) {
> +		dev_err(dp->dev, "active ucpu failed: %d\n", ret);
> +		return ret;
> +	}
> +
> +	return cdn_dp_event_config(dp);
> +}
> +
> +static int cdn_dp_init(struct cdn_dp_device *dp)
> +{
> +	struct device *dev = dp->dev;
> +	struct device_node *np = dev->of_node;
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct resource *res;
> +	int ret;
> +
> +	dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
> +	if (IS_ERR(dp->grf)) {
> +		dev_err(dev, "cdn-dp needs rockchip,grf property\n");
> +		return PTR_ERR(dp->grf);
> +	}
> +
> +	dp->irq = platform_get_irq(pdev, 0);
> +	if (dp->irq < 0) {
> +		dev_err(dev, "cdn-dp can not get irq\n");
> +		return dp->irq;
> +	}
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	dp->regs = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(dp->regs)) {
> +		dev_err(dev, "ioremap reg failed\n");
> +		return PTR_ERR(dp->regs);
> +	}
> +
> +	dp->core_clk = devm_clk_get(dev, "core-clk");
> +	if (IS_ERR(dp->core_clk)) {
> +		dev_err(dev, "cannot get core_clk_dp\n");
> +		return PTR_ERR(dp->core_clk);
> +	}
> +
> +	dp->pclk = devm_clk_get(dev, "pclk");
> +	if (IS_ERR(dp->pclk)) {
> +		dev_err(dev, "cannot get pclk\n");
> +		return PTR_ERR(dp->pclk);
> +	}
> +
> +	dp->spdif_clk = devm_clk_get(dev, "spdif");
> +	if (IS_ERR(dp->spdif_clk)) {
> +		dev_err(dev, "cannot get spdif_clk\n");
> +		return PTR_ERR(dp->spdif_clk);
> +	}
> +
> +	dp->spdif_rst = devm_reset_control_get(dev, "spdif");
> +	if (IS_ERR(dp->spdif_rst)) {
> +		dev_err(dev, "no spdif reset control found\n");
> +		return PTR_ERR(dp->spdif_rst);
> +	}
> +
> +	dp->dpms_mode = DRM_MODE_DPMS_OFF;
> +
> +	ret = cdn_dp_clk_enable(dp);
> +	if (ret < 0)
> +		return ret;
> +
> +	cdn_dp_clock_reset(dp);
> +
> +	return 0;
> +}
> +
> +static int cdn_dp_audio_hw_params(struct device *dev,
> +				  struct hdmi_codec_daifmt *daifmt,
> +				  struct hdmi_codec_params *params)
> +{
> +	struct cdn_dp_device *dp = dev_get_drvdata(dev);
> +	struct audio_info audio = {
> +		.sample_width = params->sample_width,
> +		.sample_rate = params->sample_rate,
> +		.channels = params->channels,
> +	};
> +	int ret;
> +
> +	if (!dp->encoder.crtc)
> +		return -ENODEV;
> +
> +	switch (daifmt->fmt) {
> +	case HDMI_I2S:
> +		audio.format = AFMT_I2S;
> +		break;
> +	case HDMI_SPDIF:
> +		audio.format = AFMT_SPDIF;
> +		break;
> +	default:
> +		dev_err(dev, "%s: Invalid format %d\n", __func__, daifmt->fmt);
> +		return -EINVAL;
> +	}
> +
> +	ret = cdn_dp_audio_config_set(dp, &audio);
> +	if (!ret)
> +		dp->audio_info = audio;
> +
> +	return ret;
> +}
> +
> +static void cdn_dp_audio_shutdown(struct device *dev)
> +{
> +	struct cdn_dp_device *dp = dev_get_drvdata(dev);
> +	int ret = cdn_dp_audio_stop(dp, &dp->audio_info);
> +
> +	if (!ret)
> +		dp->audio_info.format = AFMT_UNUSED;
> +}
> +
> +static int cdn_dp_audio_digital_mute(struct device *dev, bool enable)
> +{
> +	struct cdn_dp_device *dp = dev_get_drvdata(dev);
> +
> +	return cdn_dp_audio_mute(dp, enable);
> +}
> +
> +static int cdn_dp_audio_get_eld(struct device *dev, uint8_t *buf, size_t len)
> +{
> +	struct cdn_dp_device *dp = dev_get_drvdata(dev);
> +	struct drm_mode_config *config = &dp->encoder.dev->mode_config;
> +	struct drm_connector *connector;
> +	int ret = -ENODEV;
> +
> +	mutex_lock(&config->mutex);
> +	list_for_each_entry(connector, &config->connector_list, head) {
> +		if (&dp->encoder == connector->encoder) {
> +			memcpy(buf, connector->eld,
> +			       min(sizeof(connector->eld), len));
> +			ret = 0;
> +		}
> +	}
> +	mutex_unlock(&config->mutex);
> +
> +	return ret;
> +}
> +
> +static const struct hdmi_codec_ops audio_codec_ops = {
> +	.hw_params = cdn_dp_audio_hw_params,
> +	.audio_shutdown = cdn_dp_audio_shutdown,
> +	.digital_mute = cdn_dp_audio_digital_mute,
> +	.get_eld = cdn_dp_audio_get_eld,
> +};
> +
> +static int cdn_dp_audio_codec_init(struct cdn_dp_device *dp,
> +				   struct device *dev)
> +{
> +	struct hdmi_codec_pdata codec_data = {
> +		.i2s = 1,
> +		.spdif = 1,
> +		.ops = &audio_codec_ops,
> +		.max_i2s_channels = 8,
> +	};
> +
> +	dp->audio_pdev = platform_device_register_data(
> +			 dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO,
> +			 &codec_data, sizeof(codec_data));
> +
> +	return PTR_ERR_OR_ZERO(dp->audio_pdev);
> +}
> +
> +static int cdn_dp_get_cap_lanes(struct cdn_dp_device *dp,
> +				struct extcon_dev *edev)
> +{
> +	bool dfp, dptx;
> +	u8 lanes;
> +
> +	dfp = extcon_get_cable_state_(edev, EXTCON_USB_HOST);
> +	dptx = extcon_get_cable_state_(edev, EXTCON_DISP_DP);
> +
> +	if (dfp && dptx)
> +		lanes = 2;
> +	else if (dptx)
> +		lanes = 4;
> +	else
> +		lanes = 0;
> +
> +	return lanes;
> +}
> +
> +static int cdn_dp_pd_event(struct notifier_block *nb,
> +			   unsigned long event, void *priv)
> +{
> +	struct cdn_dp_device *dp;
> +	struct extcon_dev *edev = priv;
> +	u8 i;
> +
> +	dp = container_of(nb, struct cdn_dp_device, event_nb);
> +
> +	for (i = 0; i < dp->max_phy; i++) {
> +		if (edev == dp->extcon[i]) {
> +			dp->port_id = i;
> +			break;
> +		}
> +	}
> +
> +	schedule_delayed_work(&dp->event_wq, 0);
> +
> +	return 0;
> +}
> +
> +static void cdn_dp_pd_event_wq(struct work_struct *work)
> +{
> +	struct cdn_dp_device *dp = container_of(work, struct cdn_dp_device,
> +						event_wq.work);
> +	u8 id = dp->port_id, i;
> +	u8 old_cap_lanes = dp->cap_lanes[id];
> +	int ret;
> +
> +	dp->cap_lanes[id] = cdn_dp_get_cap_lanes(dp, dp->extcon[id]);
> +	if (dp->cap_lanes[id] == old_cap_lanes)
> +		return;
> +
> +	/* if phy is on, need reset it when dp lanes count is changed */
> +	if (dp->phy_status[id]) {
> +		ret = phy_power_off(dp->phy[id]);
> +		if (ret) {
> +			dev_err(dp->dev, "phy power off failed: %d", ret);
> +			goto err_phy_power_off;
> +		}
> +		dp->phy_status[id] = 0;
> +	}
> +
> +	/* if other phy is running, do not touch the hpd_status, and return */
> +	for (i = 0; i < dp->max_phy; i++) {
> +		if (dp->phy_status[i] == 1) {
> +			dev_warn(dp->dev, "busy, phy[%d] is running", i);
> +			goto err_phy_power_off;
> +		}
> +	}
> +
> +	if (!dp->cap_lanes[id]) {
> +		dp->hpd_status = connector_status_disconnected;
> +		drm_helper_hpd_irq_event(dp->drm_dev);
> +		return;
> +	}
> +
> +	if (!dp->fw_loaded) {
> +		ret = request_firmware(&dp->fw, CDN_DP_FIRMWARE, dp->dev);
> +		if (ret == -ENOENT && dp->fw_wait <= MAX_FW_WAIT_SECS) {
> +			unsigned long time = msecs_to_jiffies(dp->fw_wait * HZ);
> +
> +			/*
> +			 * If can not find the file, retry to load the firmware
> +			 * in 1 second, if still failed after 1 minute, give up.
> +			 */
> +			schedule_delayed_work(&dp->event_wq, time);
> +			dev_warn(dp->dev, "retry to request firmware in %d\n",
> +				 dp->fw_wait);
> +			dp->fw_wait *= 2;
> +			goto err_phy_power_off;
> +		} else if (ret) {
> +			dev_err(dp->dev, "failed to request firmware: %d\n",
> +				ret);
> +			goto err_phy_power_off;
> +		}
> +	}
> +
> +	ret = phy_power_on(dp->phy[id]);
> +	if (ret) {
> +		dev_err(dp->dev, "phy power on failed: %d", ret);
> +		if (!dp->fw_loaded)
> +			release_firmware(dp->fw);
> +		goto err_phy_power_off;
> +	}
> +
> +	dp->phy_status[id] = 1;
> +
> +	if (!dp->fw_loaded) {
> +		ret = cdn_dp_firmware_init(dp);
> +		release_firmware(dp->fw);
> +		if (ret) {
> +			dev_err(dp->dev, "firmware init failed: %d", ret);
> +			goto err_firmware;
> +		}
> +	}
> +
> +	/* read hpd status failed, or the hpd does not exist. */
> +	ret = cdn_dp_get_hpd_status(dp);
> +	if (ret <= 0) {
> +		dev_err(dp->dev, "get hpd failed: %d", ret);
> +		goto err_firmware;
> +	}
> +
> +	ret = cdn_dp_set_host_cap(dp, dp->cap_lanes[id]);
> +	if (ret) {
> +		dev_err(dp->dev, "set host capabilities failed: %d\n", ret);
> +		goto err_firmware;
> +	}
> +
> +	/*
> +	 * Native read with retry for link status and receiver capability reads
> +	 * for cases where the sink may still be asleep.
> +	 *
> +	 * Sinks are *supposed* to come up within 1ms from an off state, but
> +	 * we're also supposed to retry 3 times per the spec.
> +	 */
> +	for (i = 0; i < 3; i++) {
> +		ret = cdn_dp_dpcd_read(dp, 0x000, dp->dpcd,
> +				       DP_RECEIVER_CAP_SIZE);
> +		if (!ret) {
> +			dp->hpd_status = connector_status_connected;
> +			drm_helper_hpd_irq_event(dp->drm_dev);
> +			return;
> +		}
> +		usleep_range(1000, 1050);
> +	}
> +
> +err_firmware:
> +	ret = phy_power_off(dp->phy[id]);
> +	if (ret)
> +		dev_err(dp->dev, "phy power off failed: %d", ret);
> +	else
> +		dp->phy_status[id] = 0;
> +
> +err_phy_power_off:
> +	dp->cap_lanes[id] = 0;
> +}
> +
> +static int cdn_dp_bind(struct device *dev, struct device *master,
> +		       void *data)
> +{
> +	struct cdn_dp_device *dp = dev_get_drvdata(dev);
> +	struct drm_encoder *encoder;
> +	struct drm_connector *connector;
> +	struct drm_device *drm_dev = data;
> +	int ret, i;
> +
> +	ret = cdn_dp_init(dp);
> +	if (ret < 0)
> +		return ret;
> +
> +	dp->drm_dev = drm_dev;
> +	dp->hpd_status = connector_status_disconnected;
> +	dp->fw_wait = 1;
> +
> +	encoder = &dp->encoder;
> +
> +	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
> +							     dev->of_node);
> +	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
> +
> +	ret = drm_encoder_init(drm_dev, encoder, &cdn_dp_encoder_funcs,
> +			       DRM_MODE_ENCODER_TMDS, NULL);
> +	if (ret) {
> +		DRM_ERROR("failed to initialize encoder with drm\n");
> +		return ret;
> +	} > +
> +	drm_encoder_helper_add(encoder, &cdn_dp_encoder_helper_funcs);
> +
> +	connector = &dp->connector;
> +	connector->polled = DRM_CONNECTOR_POLL_HPD;
> +	connector->dpms = DRM_MODE_DPMS_OFF;
> +
> +	ret = drm_connector_init(drm_dev, connector,
> +				 &cdn_dp_atomic_connector_funcs,
> +				 DRM_MODE_CONNECTOR_DisplayPort);
> +	if (ret) {
> +		DRM_ERROR("failed to initialize connector with drm\n");
> +		goto err_free_encoder;
> +	}
> +
> +	drm_connector_helper_add(connector, &cdn_dp_connector_helper_funcs);
> +
> +	ret = drm_mode_connector_attach_encoder(connector, encoder);
> +	if (ret) {
> +		DRM_ERROR("failed to attach connector and encoder\n");
> +		goto err_free_connector;
> +	}
> +
> +	cdn_dp_audio_codec_init(dp, dev);
> +
> +	dp->event_nb.notifier_call = cdn_dp_pd_event;
> +	INIT_DELAYED_WORK(&dp->event_wq, cdn_dp_pd_event_wq);
> +
> +	for (i = 0; i < dp->max_phy; i++) {
> +		if (IS_ERR(dp->extcon[i]))
> +			continue;
> +
> +		ret = extcon_register_notifier(dp->extcon[i], EXTCON_DISP_DP,
> +					       &dp->event_nb);
> +		if (ret) {
> +			dev_err(dev, "regitster EXTCON_DISP_DP notifier err\n");
> +			return ret;
> +		}
> +
> +		dp->port_id = i;
> +		schedule_delayed_work(&dp->event_wq, 0);
> +	}
> +
> +	return 0;
> +
> +err_free_connector:
> +	drm_connector_cleanup(connector);
> +err_free_encoder:
> +	drm_encoder_cleanup(encoder);
> +	return ret;
> +}
> +
> +static void cdn_dp_unbind(struct device *dev, struct device *master, void *data)
> +{
> +	struct cdn_dp_device *dp = dev_get_drvdata(dev);
> +	struct drm_encoder *encoder = &dp->encoder;
> +	struct drm_connector *connector = &dp->connector;
> +	int i;
> +
> +	platform_device_unregister(dp->audio_pdev);
> +	cdn_dp_encoder_disable(encoder);
> +	encoder->funcs->destroy(encoder);
> +	connector->funcs->destroy(connector);
> +
> +	for (i = 0; i < dp->max_phy; i++) {
> +		if (!IS_ERR(dp->extcon[i]))
> +			extcon_unregister_notifier(dp->extcon[i], EXTCON_USB,
> +						   &dp->event_nb);
> +	}
> +}
> +
> +static const struct component_ops cdn_dp_component_ops = {
> +	.bind = cdn_dp_bind,
> +	.unbind = cdn_dp_unbind,
> +};
> +
> +static int cdn_dp_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	const struct of_device_id *match;
> +	struct cdn_dp_data *dp_data;
> +	struct cdn_dp_device *dp;
> +	u8 count = 0;
> +	int i;
> +
> +	dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
> +	if (!dp)
> +		return -ENOMEM;
> +	dp->dev = dev;
> +
> +	match = of_match_node(cdn_dp_dt_ids, pdev->dev.of_node);
> +	dp_data = (struct cdn_dp_data *)match->data;
> +
> +	dp->max_phy = dp_data->max_phy;
> +
> +	for (i = 0; i < dp->max_phy; i++) {
> +		dp->extcon[i] = extcon_get_edev_by_phandle(dev, i);
> +		dp->phy[i] = devm_of_phy_get_by_index(dev, dev->of_node, i);
> +
> +		if (!IS_ERR(dp->extcon[i]) && !IS_ERR(dp->phy[i]))
> +			count++;
> +
> +		if (PTR_ERR(dp->extcon[i]) == -EPROBE_DEFER ||
> +		    PTR_ERR(dp->phy[i]) == -EPROBE_DEFER)
> +			return -EPROBE_DEFER;
> +	}
> +
> +	if (!count) {
> +		dev_err(dev, "missing extcon or phy\n");
> +		return -EINVAL;
> +	}
> +
> +	dev_set_drvdata(dev, dp);
> +
> +	return component_add(dev, &cdn_dp_component_ops);
> +}
> +
> +static int cdn_dp_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &cdn_dp_component_ops);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver cdn_dp_driver = {
> +	.probe = cdn_dp_probe,
> +	.remove = cdn_dp_remove,
> +	.driver = {
> +		   .name = "cdn-dp",
> +		   .owner = THIS_MODULE,
> +		   .of_match_table = of_match_ptr(cdn_dp_dt_ids),
> +	},
> +};
> +
> +module_platform_driver(cdn_dp_driver);
> +
> +MODULE_AUTHOR("Chris Zhong <zyw at rock-chips.com>");
> +MODULE_DESCRIPTION("cdn DP Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
> new file mode 100644
> index 0000000..1c577bf
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
> @@ -0,0 +1,99 @@
> +/*
> + * Copyright (C) 2016 Chris Zhong <zyw at rock-chips.com>
> + * Copyright (C) 2016 ROCKCHIP, Inc.
> + *
> + * 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.
> + */
> +
> +#ifndef _CDN_DP_CORE_H
> +#define _CDN_DP_CORE_H
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_dp_helper.h>
> +#include <drm/drm_panel.h>
> +#include "rockchip_drm_drv.h"
> +
> +#define MAX_PHY		2
> +
> +enum audio_format {
> +	AFMT_I2S = 0,
> +	AFMT_SPDIF = 1,
> +	AFMT_UNUSED,
> +};
> +
> +struct audio_info {
> +	enum audio_format format;
> +	int sample_rate;
> +	int channels;
> +	int sample_width;
> +};
> +
> +enum vic_pxl_encoding_format {
> +	PXL_RGB = 0x1,
> +	YCBCR_4_4_4 = 0x2,
> +	YCBCR_4_2_2 = 0x4,
> +	YCBCR_4_2_0 = 0x8,
> +	Y_ONLY = 0x10,
> +};
> +
> +struct video_info {
> +	bool h_sync_polarity;
> +	bool v_sync_polarity;
> +	bool interlaced;
> +	int color_depth;
> +	enum vic_pxl_encoding_format color_fmt;
> +};
> +
> +struct cdn_firmware_header {
> +	u32 size_bytes; /* size of the entire header+image(s) in bytes */
> +	u32 header_size; /* size of just the header in bytes */
> +	u32 iram_size; /* size of iram */
> +	u32 dram_size; /* size of dram */
> +};
> +
> +struct cdn_dp_device {
> +	struct device *dev;
> +	struct drm_device *drm_dev;
> +	struct drm_connector connector;
> +	struct drm_encoder encoder;
> +	struct drm_display_mode mode;
> +	struct platform_device *audio_pdev;
> +
> +	const struct firmware *fw;	/* cdn dp firmware */
> +	unsigned int fw_version;	/* cdn fw version */
> +	u32 fw_wait;
> +	bool fw_loaded;
> +	void __iomem *regs;
> +	struct regmap *grf;
> +	unsigned int irq;
> +	struct clk *core_clk;
> +	struct clk *pclk;
> +	struct clk *spdif_clk;
> +	struct reset_control *spdif_rst;
> +	struct audio_info audio_info;
> +	struct video_info video_info;
> +	struct extcon_dev *extcon[MAX_PHY];
> +	struct phy *phy[MAX_PHY];
> +	struct notifier_block event_nb;
> +	struct delayed_work event_wq;
> +	struct drm_dp_link link;
> +
> +	u8 cap_lanes[MAX_PHY];
> +	u8 dpcd[DP_RECEIVER_CAP_SIZE];
> +	u8 port_id;
> +	u8 max_phy;
> +	bool phy_status[MAX_PHY];
> +	enum drm_connector_status hpd_status;
> +
> +	int dpms_mode;
> +	bool sink_has_audio;
> +};
> +#endif  /* _CDN_DP_CORE_H */
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
> new file mode 100644
> index 0000000..afc8ae9
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
> @@ -0,0 +1,898 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author: Chris Zhong <zyw at rock-chips.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * 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/clk.h>
> +#include <linux/device.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/reset.h>
> +
> +#include "cdn-dp-core.h"
> +#include "cdn-dp-reg.h"
> +
> +#define CDN_DP_SPDIF_CLK		200000000
> +#define FW_ALIVE_TIMEOUT_US		1000000
> +#define MAILBOX_RETRY_US		1000
> +#define MAILBOX_TIMEOUT_US		5000000
> +#define LINK_TRAINING_RETRY_MS		20
> +#define LINK_TRAINING_TIMEOUT_MS	500
> +
> +void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, u32 clk)
> +{
> +	writel(clk / 1000000, dp->regs + SW_CLK_H);
> +}
> +
> +void cdn_dp_clock_reset(struct cdn_dp_device *dp)
> +{
> +	u32 val;
> +
> +	val = DPTX_FRMR_DATA_CLK_RSTN_EN |
> +	      DPTX_FRMR_DATA_CLK_EN |
> +	      DPTX_PHY_DATA_RSTN_EN |
> +	      DPTX_PHY_DATA_CLK_EN |
> +	      DPTX_PHY_CHAR_RSTN_EN |
> +	      DPTX_PHY_CHAR_CLK_EN |
> +	      SOURCE_AUX_SYS_CLK_RSTN_EN |
> +	      SOURCE_AUX_SYS_CLK_EN |
> +	      DPTX_SYS_CLK_RSTN_EN |
> +	      DPTX_SYS_CLK_EN |
> +	      CFG_DPTX_VIF_CLK_RSTN_EN |
> +	      CFG_DPTX_VIF_CLK_EN;
> +	writel(val, dp->regs + SOURCE_DPTX_CAR);
> +
> +	val = SOURCE_PHY_RSTN_EN | SOURCE_PHY_CLK_EN;
> +	writel(val, dp->regs + SOURCE_PHY_CAR);
> +
> +	val = SOURCE_PKT_SYS_RSTN_EN |
> +	      SOURCE_PKT_SYS_CLK_EN |
> +	      SOURCE_PKT_DATA_RSTN_EN |
> +	      SOURCE_PKT_DATA_CLK_EN;
> +	writel(val, dp->regs + SOURCE_PKT_CAR);
> +
> +	val = SPDIF_CDR_CLK_RSTN_EN |
> +	      SPDIF_CDR_CLK_EN |
> +	      SOURCE_AIF_SYS_RSTN_EN |
> +	      SOURCE_AIF_SYS_CLK_EN |
> +	      SOURCE_AIF_CLK_RSTN_EN |
> +	      SOURCE_AIF_CLK_EN;
> +	writel(val, dp->regs + SOURCE_AIF_CAR);
> +
> +	val = SOURCE_CIPHER_SYSTEM_CLK_RSTN_EN |
> +	      SOURCE_CIPHER_SYS_CLK_EN |
> +	      SOURCE_CIPHER_CHAR_CLK_RSTN_EN |
> +	      SOURCE_CIPHER_CHAR_CLK_EN;
> +	writel(val, dp->regs + SOURCE_CIPHER_CAR);
> +
> +	val = SOURCE_CRYPTO_SYS_CLK_RSTN_EN |
> +	      SOURCE_CRYPTO_SYS_CLK_EN;
> +	writel(val, dp->regs + SOURCE_CRYPTO_CAR);
> +
> +	val = ~(MAILBOX_INT_MASK_BIT | PIF_INT_MASK_BIT) & ALL_INT_MASK;
> +	writel(val, dp->regs + APB_INT_MASK);
> +}
> +
> +static int cdn_dp_mailbox_read(struct cdn_dp_device *dp)
> +{
> +	int val, ret;
> +
> +	if (!dp->fw_loaded)
> +		return -EPERM;
> +
> +	ret = readx_poll_timeout(readl, dp->regs + MAILBOX_EMPTY_ADDR,
> +				 val, !val, MAILBOX_RETRY_US,
> +				 MAILBOX_TIMEOUT_US);
> +	if (ret < 0) {
> +		dev_err(dp->dev, "failed to read mailbox, keep alive = %x\n",
> +			readl(dp->regs + KEEP_ALIVE));
> +		return ret;
> +	}
> +
> +	return readl(dp->regs + MAILBOX0_RD_DATA) & 0xff;
> +}
> +
> +static int cdp_dp_mailbox_write(struct cdn_dp_device *dp, u8 val)
> +{
> +	int ret, full;
> +
> +	if (!dp->fw_loaded)
> +		return -EPERM;
> +
> +	ret = readx_poll_timeout(readl, dp->regs + MAILBOX_FULL_ADDR,
> +				 full, !full, MAILBOX_RETRY_US,
> +				 MAILBOX_TIMEOUT_US);
> +	if (ret < 0) {
> +		dev_err(dp->dev, "mailbox is full, keep alive = %x\n",
> +			readl(dp->regs + KEEP_ALIVE));
> +		return ret;
> +	}
> +
> +	writel(val, dp->regs + MAILBOX0_WR_DATA);
> +
> +	return 0;
> +}
> +
> +static int cdn_dp_mailbox_validate_receive(struct cdn_dp_device *dp,
> +					   u8 module_id, u8 opcode,
> +					   u8 req_size)
> +{
> +	u32 mbox_size, i;
> +	u8 header[4];
> +	int ret;
> +
> +	/* read the header of the message */
> +	for (i = 0; i < 4; i++)  {
> +		ret = cdn_dp_mailbox_read(dp);
> +		if (ret < 0)
> +			return ret;
> +
> +		header[i] = ret;
> +	}
> +
> +	mbox_size = (header[2] << 8) | header[3];
> +
> +	if (opcode != header[0] || module_id != header[1] ||
> +	    req_size != mbox_size) {
> +		dump_stack();
> +		dev_err(dp->dev, "mailbox validate failed");
> +
> +		/*
> +		 * If the message in mailbox is not what we want, we need to
> +		 * clear the mailbox by read.
> +		 */
> +		for (i = 0; i < mbox_size; i++)
> +			if (cdn_dp_mailbox_read(dp) < 0)
> +				break;
> +
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int cdn_dp_mailbox_read_receive(struct cdn_dp_device *dp,
> +				       u8 *buff, u8 buff_size)
> +{
> +	u32 i;
> +	int ret;
> +
> +	for (i = 0; i < buff_size; i++) {
> +		ret = cdn_dp_mailbox_read(dp);
> +		if (ret < 0)
> +			return ret;
> +
> +		buff[i] = ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int cdn_dp_mailbox_send(struct cdn_dp_device *dp, u8 module_id,
> +			       u8 opcode, u16 size, u8 *message)
> +{
> +	u8 header[4];
> +	int ret, i;
> +
> +	header[0] = opcode;
> +	header[1] = module_id;
> +	header[2] = (size >> 8) & 0xff;
> +	header[3] = size & 0xff;
> +
> +	for (i = 0; i < 4; i++) {
> +		ret = cdp_dp_mailbox_write(dp, header[i]);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	for (i = 0; i < size; i++) {
> +		ret = cdp_dp_mailbox_write(dp, message[i]);
> +		if (ret) {
> +			dev_err(dp->dev, "mailbox send failed: %d", ret);
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
> +{
> +	u8 msg[6];
> +
> +	msg[0] = (addr >> 8) & 0xff;
> +	msg[1] = addr & 0xff;
> +	msg[2] = (val >> 24) & 0xff;
> +	msg[3] = (val >> 16) & 0xff;
> +	msg[4] = (val >> 8) & 0xff;
> +	msg[5] = val & 0xff;
> +	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_REGISTER,
> +				   sizeof(msg), msg);
> +}
> +
> +int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
> +{
> +	u8 msg[5], reg[5];
> +	int ret;
> +
> +	msg[0] = (len >> 8) & 0xff;
> +	msg[1] = len & 0xff;
> +	msg[2] = (addr >> 16) & 0xff;
> +	msg[3] = (addr >> 8) & 0xff;
> +	msg[4] = addr & 0xff;
> +	cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_READ_DPCD,
> +			    sizeof(msg), msg);
> +
> +	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
> +					      DPTX_READ_DPCD,
> +					      sizeof(reg) + len);
> +	if (ret)
> +		return ret;
> +
> +	ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg));
> +	if (ret)
> +		return ret;
> +
> +	return cdn_dp_mailbox_read_receive(dp, data, len);
> +}
> +
> +int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
> +{
> +	u8 msg[6], reg[5];
> +	int ret;
> +
> +	msg[0] = 0;
> +	msg[1] = 1;
> +	msg[2] = (addr >> 16) & 0xff;
> +	msg[3] = (addr >> 8) & 0xff;
> +	msg[4] = addr & 0xff;
> +	msg[5] = value;
> +	cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_DPCD,
> +			    sizeof(msg), msg);
> +
> +	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
> +					      DPTX_WRITE_DPCD, sizeof(reg));
> +	if (ret)
> +		return ret;
> +
> +	ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg));
> +	if (ret)
> +		return ret;
> +
> +	if (addr != (reg[2] << 16 | reg[3] << 8 | reg[4])) {
> +		dev_err(dp->dev, "write dpcd failed\n");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
> +			 u32 i_size, const u32 *d_mem, u32 d_size)
> +{
> +	int i, ret;
> +	u32 reg;
> +
> +	/* reset ucpu before load firmware*/
> +	writel(APB_IRAM_PATH | APB_DRAM_PATH | APB_XT_RESET,
> +	       dp->regs + APB_CTRL);
> +
> +	for (i = 0; i < i_size; i += 4)
> +		writel(*i_mem++, dp->regs + ADDR_IMEM + i);
> +
> +	for (i = 0; i < d_size; i += 4)
> +		writel(*d_mem++, dp->regs + ADDR_DMEM + i);
> +
> +	/* un-reset ucpu */
> +	writel(0, dp->regs + APB_CTRL);
> +
> +	/* check the keep alive register to make sure fw working */
> +	ret = readx_poll_timeout(readl, dp->regs + KEEP_ALIVE,
> +				 reg, reg, 2000, FW_ALIVE_TIMEOUT_US);
> +	if (ret < 0) {
> +		dev_err(dp->dev, "failed to loaded the FW reg = %x\n", reg);
> +		return -EINVAL;
> +	}
> +
> +	reg = readl(dp->regs + VER_L) & 0xff;
> +	dp->fw_version = reg;
> +	reg = readl(dp->regs + VER_H) & 0xff;
> +	dp->fw_version |= reg << 8;
> +	reg = readl(dp->regs + VER_LIB_L_ADDR) & 0xff;
> +	dp->fw_version |= reg << 16;
> +	reg = readl(dp->regs + VER_LIB_H_ADDR) & 0xff;
> +	dp->fw_version |= reg << 24;
> +
> +	dev_dbg(dp->dev, "firmware version: %x\n", dp->fw_version);
> +
> +	return 0;
> +}
> +
> +int cdn_dp_reg_write_bit(struct cdn_dp_device *dp, u16 addr, u8 start_bit,
> +			 u8 bits_no, u32 val)
> +{
> +	u8 field[8];
> +
> +	field[0] = (addr >> 8) & 0xff;
> +	field[1] = addr & 0xff;
> +	field[2] = start_bit;
> +	field[3] = bits_no;
> +	field[4] = (val >> 24) & 0xff;
> +	field[5] = (val >> 16) & 0xff;
> +	field[6] = (val >> 8) & 0xff;
> +	field[7] = val & 0xff;
> +
> +	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_FIELD,
> +				   sizeof(field), field);
> +}
> +
> +int cdn_dp_active(struct cdn_dp_device *dp, bool enable)
> +{
> +	u8 active = enable ? 1 : 0;
> +	u8 resp;
> +	int ret;
> +
> +	/* set firmware status, 1: active; 0: standby */
> +	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_GENERAL,
> +				  GENERAL_MAIN_CONTROL, sizeof(active),
> +				  &active);
> +	if (ret)
> +		return ret;
> +
> +	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_GENERAL,
> +					      GENERAL_MAIN_CONTROL,
> +					      sizeof(resp));
> +	if (ret)
> +		return ret;
> +
> +	ret = cdn_dp_mailbox_read_receive(dp, &resp, sizeof(resp));
> +	if (ret)
> +		return ret;
> +
> +	return resp ? 0 : -EINVAL;
> +}
> +
> +int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes)
> +{
> +	u8 msg[8];
> +	int ret;
> +
> +	msg[0] = CDN_DP_MAX_LINK_RATE;
> +	msg[1] = lanes;
> +	msg[2] = VOLTAGE_LEVEL_2;
> +	msg[3] = PRE_EMPHASIS_LEVEL_3;
> +	msg[4] = PTS1 | PTS2 | PTS3 | PTS4;
> +	msg[5] = FAST_LT_NOT_SUPPORT;
> +	msg[6] = LANE_MAPPING_NORMAL;
> +	msg[7] = ENHANCED;
> +
> +	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
> +				  DPTX_SET_HOST_CAPABILITIES,
> +				  sizeof(msg), msg);
> +	if (ret)
> +		return ret;
> +
> +	ret = cdn_dp_reg_write(dp, DP_AUX_SWAP_INVERSION_CONTROL,
> +			       AUX_HOST_INVERT);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +int cdn_dp_event_config(struct cdn_dp_device *dp)
> +{
> +	u8 msg[5];
> +
> +	memset(msg, 0, sizeof(msg));
> +
> +	msg[0] = DPTX_EVENT_ENABLE_HPD | DPTX_EVENT_ENABLE_TRAINING;
> +
> +	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_ENABLE_EVENT,
> +				   sizeof(msg), msg);
> +}
> +
> +u32 cdn_dp_get_event(struct cdn_dp_device *dp)
> +{
> +	return readl(dp->regs + SW_EVENTS0);
> +}
> +
> +int cdn_dp_get_hpd_status(struct cdn_dp_device *dp)
> +{
> +	u8 status;
> +	int ret;
> +
> +	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_HPD_STATE,
> +				  0, NULL);
> +	if (ret)
> +		return ret;
> +
> +	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
> +					      DPTX_HPD_STATE, sizeof(status));
> +	if (ret)
> +		return ret;
> +
> +	ret = cdn_dp_mailbox_read_receive(dp, &status, sizeof(status));
> +	if (ret)
> +		return ret;
> +
> +	return status;
> +}
> +
> +int cdn_dp_get_edid_block(void *data, u8 *edid,
> +			  unsigned int block, size_t length)
> +{
> +	struct cdn_dp_device *dp = data;
> +	u8 msg[2], reg[2];
> +	int ret;
> +
> +	msg[0] = block / 2;
> +	msg[1] = block % 2;
> +
> +	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_GET_EDID,
> +				  sizeof(msg), msg);
> +	if (ret)
> +		return ret;
> +
> +	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
> +					      DPTX_GET_EDID,
> +					      sizeof(reg) + length);
> +	if (ret)
> +		return ret;
> +
> +	ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg));
> +	if (ret)
> +		return ret;
> +
> +	ret = cdn_dp_mailbox_read_receive(dp, edid, length);
> +	if (ret)
> +		return ret;
> +
> +	if (reg[0] != length || reg[1] != block / 2) {
> +		dev_err(dp->dev, "edid block size err\n");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +int cdn_dp_training_start(struct cdn_dp_device *dp)
> +{
> +	u8 msg, event[2];
> +	unsigned long timeout;
> +	int ret;
> +
> +	msg = LINK_TRAINING_RUN;
> +
> +	/* start training */
> +	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_TRAINING_CONTROL,
> +				  sizeof(msg), &msg);
> +	if (ret)
> +		return ret;
> +
> +	timeout = jiffies + msecs_to_jiffies(LINK_TRAINING_TIMEOUT_MS);
> +	while (time_before(jiffies, timeout)) {
> +		msleep(LINK_TRAINING_RETRY_MS);
> +		ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
> +					  DPTX_READ_EVENT, 0, NULL);
> +		if (ret)
> +			return ret;
> +
> +		ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
> +						      DPTX_READ_EVENT,
> +						      sizeof(event));
> +		if (ret)
> +			return ret;
> +
> +		ret = cdn_dp_mailbox_read_receive(dp, event, sizeof(event));
> +		if (ret)
> +			return ret;
> +
> +		if (event[1] & EQ_PHASE_FINISHED)
> +			return 0;
> +	}
> +
> +	return -ETIMEDOUT;
> +}
> +
> +int cdn_dp_get_training_status(struct cdn_dp_device *dp)
> +{
> +	u8 status[10];
> +	int ret;
> +
> +	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_READ_LINK_STAT,
> +				  0, NULL);
> +	if (ret)
> +		return ret;
> +
> +	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
> +					      DPTX_READ_LINK_STAT,
> +					      sizeof(status));
> +	if (ret)
> +		return ret;
> +
> +	ret = cdn_dp_mailbox_read_receive(dp, status, sizeof(status));
> +	if (ret)
> +		return ret;
> +
> +	dp->link.rate = status[0];
> +	dp->link.num_lanes = status[1];
> +
> +	return 0;
> +}
> +
> +int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active)
> +{
> +	u8 msg;
> +
> +	msg = !!active;
> +
> +	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_SET_VIDEO,
> +				   sizeof(msg), &msg);
> +}
> +
> +static int cdn_dp_get_msa_misc(struct video_info *video,
> +			       struct drm_display_mode *mode)
> +{
> +	u8 val[2];
> +	u32 msa_misc;
> +
> +	switch (video->color_fmt) {
> +	case PXL_RGB:
> +	case Y_ONLY:
> +		val[0] = 0;
> +		break;
> +	/* set YUV default color space conversion to BT601 */
> +	case YCBCR_4_4_4:
> +		val[0] = 6 + BT_601 * 8;
> +		break;
> +	case YCBCR_4_2_2:
> +		val[0] = 5 + BT_601 * 8;
> +		break;
> +	case YCBCR_4_2_0:
> +		val[0] = 5;
> +		break;
> +	};
> +
> +	switch (video->color_depth) {
> +	case 6:
> +		val[1] = 0;
> +		break;
> +	case 8:
> +		val[1] = 1;
> +		break;
> +	case 10:
> +		val[1] = 2;
> +		break;
> +	case 12:
> +		val[1] = 3;
> +		break;
> +	case 16:
> +		val[1] = 4;
> +		break;
> +	};
> +
> +	msa_misc = 2 * val[0] + 32 * val[1] +
> +		   ((video->color_fmt == Y_ONLY) ? (1 << 14) : 0);
> +
> +	return msa_misc;
> +}
> +
> +int cdn_dp_config_video(struct cdn_dp_device *dp)
> +{
> +	struct video_info *video = &dp->video_info;
> +	struct drm_display_mode *mode = &dp->mode;
> +	u64 symbol;
> +	u32 val, link_rate;
> +	u8 bit_per_pix;
> +	u8 tu_size_reg = TU_SIZE;
> +	int ret;
> +
> +	bit_per_pix = (video->color_fmt == YCBCR_4_2_2) ?
> +		      (video->color_depth * 2) : (video->color_depth * 3);
> +
> +	link_rate = drm_dp_bw_code_to_link_rate(dp->link.rate);
> +
> +	val = VIF_BYPASS_INTERLACE;
> +	ret = cdn_dp_reg_write(dp, BND_HSYNC2VSYNC, val);
> +	if (ret)
> +		return ret;
> +
> +	ret = cdn_dp_reg_write(dp, HSYNC2VSYNC_POL_CTRL, 0);
> +	if (ret)
> +		return ret;
> +
> +	/* get a best tu_size and symbol */
> +	do {
> +		tu_size_reg += 2;
> +		symbol = tu_size_reg * mode->clock * bit_per_pix;
> +		symbol /= dp->link.num_lanes * link_rate * 8;
> +	} while ((symbol == 1) || (tu_size_reg - symbol < 4));
> +
> +	val = symbol + (tu_size_reg << 8);
> +	ret = cdn_dp_reg_write(dp, DP_FRAMER_TU, val);
> +	if (ret)
> +		return ret;
> +
> +	switch (video->color_depth) {
> +	case 6:
> +		val = BCS_6;
> +		break;
> +	case 8:
> +		val = BCS_8;
> +		break;
> +	case 10:
> +		val = BCS_10;
> +		break;
> +	case 12:
> +		val = BCS_12;
> +		break;
> +	case 16:
> +		val = BCS_16;
> +		break;
> +	};
> +
> +	val += video->color_fmt << 8;
> +	ret = cdn_dp_reg_write(dp, DP_FRAMER_PXL_REPR, val);
> +	if (ret)
> +		return ret;
> +
> +	val = video->h_sync_polarity ? DP_FRAMER_SP_HSP : 0;
> +	val |= video->v_sync_polarity ? DP_FRAMER_SP_VSP : 0;
> +	ret = cdn_dp_reg_write(dp, DP_FRAMER_SP, val);
> +	if (ret)
> +		return ret;
> +
> +	val = (mode->hsync_start - mode->hdisplay) << 16;
> +	val |= mode->htotal - mode->hsync_end;
> +	ret = cdn_dp_reg_write(dp, DP_FRONT_BACK_PORCH, val);
> +	if (ret)
> +		return ret;
> +
> +	val = mode->hdisplay * bit_per_pix / 8;
> +	ret = cdn_dp_reg_write(dp, DP_BYTE_COUNT, val);
> +	if (ret)
> +		return ret;
> +
> +	val = mode->htotal | ((mode->htotal - mode->hsync_start) << 16);
> +	ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_0, val);
> +	if (ret)
> +		return ret;
> +
> +	val = mode->hsync_end - mode->hsync_start;
> +	val |= (mode->hdisplay << 16) | (video->h_sync_polarity << 15);
> +	ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_1, val);
> +	if (ret)
> +		return ret;
> +
> +	val = mode->vtotal;
> +	val |= ((mode->vtotal - mode->vsync_start) << 16);
> +	ret = cdn_dp_reg_write(dp, MSA_VERTICAL_0, val);
> +	if (ret)
> +		return ret;
> +
> +	val = mode->vsync_end - mode->vsync_start;
> +	val |= mode->vdisplay << 16 | (video->v_sync_polarity << 15);
> +	ret = cdn_dp_reg_write(dp, MSA_VERTICAL_1, val);
> +	if (ret)
> +		return ret;
> +
> +	val = cdn_dp_get_msa_misc(video, mode);
> +	ret = cdn_dp_reg_write(dp, MSA_MISC, val);
> +	if (ret)
> +		return ret;
> +
> +	ret = cdn_dp_reg_write(dp, STREAM_CONFIG, 1);
> +	if (ret)
> +		return ret;
> +
> +	val = mode->hsync_end - mode->hsync_start;
> +	val |= (mode->hdisplay << 16);
> +	ret = cdn_dp_reg_write(dp, DP_HORIZONTAL, val);
> +	if (ret)
> +		return ret;
> +
> +	val = mode->vtotal;
> +	val -= (mode->vtotal - mode->vdisplay);
> +	val |= (mode->vtotal - mode->vsync_start) << 16;
> +	ret = cdn_dp_reg_write(dp, DP_VERTICAL_0, val);
> +	if (ret)
> +		return ret;
> +
> +	val = mode->vtotal;
> +	ret = cdn_dp_reg_write(dp, DP_VERTICAL_1, val);
> +	if (ret)
> +		return ret;
> +
> +	val =  0;
> +	return cdn_dp_reg_write_bit(dp, DP_VB_ID, 2, 1, val);
> +}
> +
> +int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio)
> +{
> +	u32 val;
> +	int ret;
> +
> +	ret = cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, 0);
> +	if (ret)
> +		return ret;
> +
> +	val = SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS;
> +	val |= SPDIF_FIFO_MID_RANGE(0xe0);
> +	val |= SPDIF_JITTER_THRSH(0xe0);
> +	val |= SPDIF_JITTER_AVG_WIN(7);
> +	writel(val, dp->regs + SPDIF_CTRL_ADDR);
> +
> +	/* clearn the audio config and reset */
> +	writel(0, dp->regs + AUDIO_SRC_CNTL);
> +	writel(0, dp->regs + AUDIO_SRC_CNFG);
> +	writel(AUDIO_SW_RST, dp->regs + AUDIO_SRC_CNTL);
> +	writel(0, dp->regs + AUDIO_SRC_CNTL);
> +
> +	/* reset smpl2pckt component  */
> +	writel(0, dp->regs + SMPL2PKT_CNTL);
> +	writel(AUDIO_SW_RST, dp->regs + SMPL2PKT_CNTL);
> +	writel(0, dp->regs + SMPL2PKT_CNTL);
> +
> +	/* reset FIFO */
> +	writel(AUDIO_SW_RST, dp->regs + FIFO_CNTL);
> +	writel(0, dp->regs + FIFO_CNTL);
> +
> +	if (audio->format == AFMT_SPDIF)
> +		clk_disable_unprepare(dp->spdif_clk);
> +
> +	return 0;
> +}
> +
> +int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable)
> +{
> +	return cdn_dp_reg_write_bit(dp, DP_VB_ID, 4, 1, enable);
> +}
> +
> +static void cdn_dp_audio_config_i2s(struct cdn_dp_device *dp,
> +				    struct audio_info *audio)
> +{
> +	int sub_pckt_num = 1, i2s_port_en_val = 0xf, i;
> +	u32 val;
> +
> +	if (audio->channels == 2) {
> +		if (dp->link.num_lanes == 1)
> +			sub_pckt_num = 2;
> +		else
> +			sub_pckt_num = 4;
> +
> +		i2s_port_en_val = 1;
> +	} else if (audio->channels == 4) {
> +		i2s_port_en_val = 3;
> +	}
> +
> +	writel(0x0, dp->regs + SPDIF_CTRL_ADDR);
> +
> +	writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL);
> +
> +	val = MAX_NUM_CH(audio->channels);
> +	val |= NUM_OF_I2S_PORTS(audio->channels);
> +	val |= AUDIO_TYPE_LPCM;
> +	val |= CFG_SUB_PCKT_NUM(sub_pckt_num);
> +	writel(val, dp->regs + SMPL2PKT_CNFG);
> +
> +	if (audio->sample_width == 16)
> +		val = 0;
> +	else if (audio->sample_width == 24)
> +		val = 1 << 9;
> +	else
> +		val = 2 << 9;
> +
> +	val |= AUDIO_CH_NUM(audio->channels);
> +	val |= I2S_DEC_PORT_EN(i2s_port_en_val);
> +	val |= TRANS_SMPL_WIDTH_32;
> +	writel(val, dp->regs + AUDIO_SRC_CNFG);
> +
> +	for (i = 0; i < (audio->channels + 1) / 2; i++) {
> +		if (audio->sample_width == 16)
> +			val = (0x08 << 8) | (0x08 << 20);
> +		else if (audio->sample_width == 24)
> +			val = (0x0b << 8) | (0x0b << 20);
> +
> +		val |= ((2 * i) << 4) | ((2 * i + 1) << 16);
> +		writel(val, dp->regs + STTS_BIT_CH(i));
> +	}
> +
> +	switch (audio->sample_rate) {
> +	case 32000:
> +		val = SAMPLING_FREQ(3) |
> +		      ORIGINAL_SAMP_FREQ(0xc);
> +		break;
> +	case 44100:
> +		val = SAMPLING_FREQ(0) |
> +		      ORIGINAL_SAMP_FREQ(0xf);
> +		break;
> +	case 48000:
> +		val = SAMPLING_FREQ(2) |
> +		      ORIGINAL_SAMP_FREQ(0xd);
> +		break;
> +	case 88200:
> +		val = SAMPLING_FREQ(8) |
> +		      ORIGINAL_SAMP_FREQ(0x7);
> +		break;
> +	case 96000:
> +		val = SAMPLING_FREQ(0xa) |
> +		      ORIGINAL_SAMP_FREQ(5);
> +		break;
> +	case 176400:
> +		val = SAMPLING_FREQ(0xc) |
> +		      ORIGINAL_SAMP_FREQ(3);
> +		break;
> +	case 192000:
> +		val = SAMPLING_FREQ(0xe) |
> +		      ORIGINAL_SAMP_FREQ(1);
> +		break;
> +	}
> +	val |= 4;
> +	writel(val, dp->regs + COM_CH_STTS_BITS);
> +
> +	writel(SMPL2PKT_EN, dp->regs + SMPL2PKT_CNTL);
> +	writel(I2S_DEC_START, dp->regs + AUDIO_SRC_CNTL);
> +}
> +
> +static void cdn_dp_audio_config_spdif(struct cdn_dp_device *dp)
> +{
> +	u32 val;
> +
> +	val = SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS;
> +	val |= SPDIF_FIFO_MID_RANGE(0xe0);
> +	val |= SPDIF_JITTER_THRSH(0xe0);
> +	val |= SPDIF_JITTER_AVG_WIN(7);
> +	writel(val, dp->regs + SPDIF_CTRL_ADDR);
> +
> +	writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL);
> +
> +	val = MAX_NUM_CH(2) | AUDIO_TYPE_LPCM | CFG_SUB_PCKT_NUM(4);
> +	writel(val, dp->regs + SMPL2PKT_CNFG);
> +	writel(SMPL2PKT_EN, dp->regs + SMPL2PKT_CNTL);
> +
> +	val = SPDIF_ENABLE | SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS;
> +	val |= SPDIF_FIFO_MID_RANGE(0xe0);
> +	val |= SPDIF_JITTER_THRSH(0xe0);
> +	val |= SPDIF_JITTER_AVG_WIN(7);
> +	writel(val, dp->regs + SPDIF_CTRL_ADDR);
> +
> +	clk_prepare_enable(dp->spdif_clk);
> +	clk_set_rate(dp->spdif_clk, CDN_DP_SPDIF_CLK);
> +}
> +
> +int cdn_dp_audio_config_set(struct cdn_dp_device *dp, struct audio_info *audio)
> +{
> +	int ret;
> +
> +	/* reset the spdif clk before config */
> +	if (audio->format == AFMT_SPDIF) {
> +		reset_control_assert(dp->spdif_rst);
> +		reset_control_deassert(dp->spdif_rst);
> +	}
> +
> +	ret = cdn_dp_reg_write(dp, CM_LANE_CTRL, LANE_REF_CYC);
> +	if (ret)
> +		return ret;
> +
> +	ret = cdn_dp_reg_write(dp, CM_CTRL, 0);
> +	if (ret)
> +		return ret;
> +
> +	if (audio->format == AFMT_I2S)
> +		cdn_dp_audio_config_i2s(dp, audio);
> +	else
> +		cdn_dp_audio_config_spdif(dp);
> +
> +	return cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, AUDIO_PACK_EN);
> +}
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
> new file mode 100644
> index 0000000..1affeed
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
> @@ -0,0 +1,479 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author: Chris Zhong <zyw at rock-chips.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * 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.
> + */
> +
> +#ifndef _CDN_DP_REG_H
> +#define _CDN_DP_REG_H
> +
> +#include <linux/bitops.h>
> +
> +#define ADDR_IMEM		0x10000
> +#define ADDR_DMEM		0x20000
> +
> +/* APB CFG addr */
> +#define APB_CTRL			0
> +#define XT_INT_CTRL			0x04
> +#define MAILBOX_FULL_ADDR		0x08
> +#define MAILBOX_EMPTY_ADDR		0x0c
> +#define MAILBOX0_WR_DATA		0x10
> +#define MAILBOX0_RD_DATA		0x14
> +#define KEEP_ALIVE			0x18
> +#define VER_L				0x1c
> +#define VER_H				0x20
> +#define VER_LIB_L_ADDR			0x24
> +#define VER_LIB_H_ADDR			0x28
> +#define SW_DEBUG_L			0x2c
> +#define SW_DEBUG_H			0x30
> +#define MAILBOX_INT_MASK		0x34
> +#define MAILBOX_INT_STATUS		0x38
> +#define SW_CLK_L			0x3c
> +#define SW_CLK_H			0x40
> +#define SW_EVENTS0			0x44
> +#define SW_EVENTS1			0x48
> +#define SW_EVENTS2			0x4c
> +#define SW_EVENTS3			0x50
> +#define XT_OCD_CTRL			0x60
> +#define APB_INT_MASK			0x6c
> +#define APB_STATUS_MASK			0x70
> +
> +/* audio decoder addr */
> +#define AUDIO_SRC_CNTL			0x30000
> +#define AUDIO_SRC_CNFG			0x30004
> +#define COM_CH_STTS_BITS		0x30008
> +#define STTS_BIT_CH(x)			(0x3000c + ((x) << 2))
> +#define SPDIF_CTRL_ADDR			0x3004c
> +#define SPDIF_CH1_CS_3100_ADDR		0x30050
> +#define SPDIF_CH1_CS_6332_ADDR		0x30054
> +#define SPDIF_CH1_CS_9564_ADDR		0x30058
> +#define SPDIF_CH1_CS_12796_ADDR		0x3005c
> +#define SPDIF_CH1_CS_159128_ADDR	0x30060
> +#define SPDIF_CH1_CS_191160_ADDR	0x30064
> +#define SPDIF_CH2_CS_3100_ADDR		0x30068
> +#define SPDIF_CH2_CS_6332_ADDR		0x3006c
> +#define SPDIF_CH2_CS_9564_ADDR		0x30070
> +#define SPDIF_CH2_CS_12796_ADDR		0x30074
> +#define SPDIF_CH2_CS_159128_ADDR	0x30078
> +#define SPDIF_CH2_CS_191160_ADDR	0x3007c
> +#define SMPL2PKT_CNTL			0x30080
> +#define SMPL2PKT_CNFG			0x30084
> +#define FIFO_CNTL			0x30088
> +#define FIFO_STTS			0x3008c
> +
> +/* source pif addr */
> +#define SOURCE_PIF_WR_ADDR		0x30800
> +#define SOURCE_PIF_WR_REQ		0x30804
> +#define SOURCE_PIF_RD_ADDR		0x30808
> +#define SOURCE_PIF_RD_REQ		0x3080c
> +#define SOURCE_PIF_DATA_WR		0x30810
> +#define SOURCE_PIF_DATA_RD		0x30814
> +#define SOURCE_PIF_FIFO1_FLUSH		0x30818
> +#define SOURCE_PIF_FIFO2_FLUSH		0x3081c
> +#define SOURCE_PIF_STATUS		0x30820
> +#define SOURCE_PIF_INTERRUPT_SOURCE	0x30824
> +#define SOURCE_PIF_INTERRUPT_MASK	0x30828
> +#define SOURCE_PIF_PKT_ALLOC_REG	0x3082c
> +#define SOURCE_PIF_PKT_ALLOC_WR_EN	0x30830
> +#define SOURCE_PIF_SW_RESET		0x30834
> +
> +/* bellow registers need access by mailbox */
> +/* source car addr */
> +#define SOURCE_HDTX_CAR			0x0900
> +#define SOURCE_DPTX_CAR			0x0904
> +#define SOURCE_PHY_CAR			0x0908
> +#define SOURCE_CEC_CAR			0x090c
> +#define SOURCE_CBUS_CAR			0x0910
> +#define SOURCE_PKT_CAR			0x0918
> +#define SOURCE_AIF_CAR			0x091c
> +#define SOURCE_CIPHER_CAR		0x0920
> +#define SOURCE_CRYPTO_CAR		0x0924
> +
> +/* clock meters addr */
> +#define CM_CTRL				0x0a00
> +#define CM_I2S_CTRL			0x0a04
> +#define CM_SPDIF_CTRL			0x0a08
> +#define CM_VID_CTRL			0x0a0c
> +#define CM_LANE_CTRL			0x0a10
> +#define I2S_NM_STABLE			0x0a14
> +#define I2S_NCTS_STABLE			0x0a18
> +#define SPDIF_NM_STABLE			0x0a1c
> +#define SPDIF_NCTS_STABLE		0x0a20
> +#define NMVID_MEAS_STABLE		0x0a24
> +#define I2S_MEAS			0x0a40
> +#define SPDIF_MEAS			0x0a80
> +#define NMVID_MEAS			0x0ac0
> +
> +/* source vif addr */
> +#define BND_HSYNC2VSYNC			0x0b00
> +#define HSYNC2VSYNC_F1_L1		0x0b04
> +#define HSYNC2VSYNC_F2_L1		0x0b08
> +#define HSYNC2VSYNC_STATUS		0x0b0c
> +#define HSYNC2VSYNC_POL_CTRL		0x0b10
> +
> +/* dptx phy addr */
> +#define DP_TX_PHY_CONFIG_REG		0x2000
> +#define DP_TX_PHY_STATUS_REG		0x2004
> +#define DP_TX_PHY_SW_RESET		0x2008
> +#define DP_TX_PHY_SCRAMBLER_SEED	0x200c
> +#define DP_TX_PHY_TRAINING_01_04	0x2010
> +#define DP_TX_PHY_TRAINING_05_08	0x2014
> +#define DP_TX_PHY_TRAINING_09_10	0x2018
> +#define TEST_COR			0x23fc
> +
> +/* dptx hpd addr */
> +#define HPD_IRQ_DET_MIN_TIMER		0x2100
> +#define HPD_IRQ_DET_MAX_TIMER		0x2104
> +#define HPD_UNPLGED_DET_MIN_TIMER	0x2108
> +#define HPD_STABLE_TIMER		0x210c
> +#define HPD_FILTER_TIMER		0x2110
> +#define HPD_EVENT_MASK			0x211c
> +#define HPD_EVENT_DET			0x2120
> +
> +/* dpyx framer addr */
> +#define DP_FRAMER_GLOBAL_CONFIG		0x2200
> +#define DP_SW_RESET			0x2204
> +#define DP_FRAMER_TU			0x2208
> +#define DP_FRAMER_PXL_REPR		0x220c
> +#define DP_FRAMER_SP			0x2210
> +#define AUDIO_PACK_CONTROL		0x2214
> +#define DP_VC_TABLE(x)			(0x2218 + ((x) << 2))
> +#define DP_VB_ID			0x2258
> +#define DP_MTPH_LVP_CONTROL		0x225c
> +#define DP_MTPH_SYMBOL_VALUES		0x2260
> +#define DP_MTPH_ECF_CONTROL		0x2264
> +#define DP_MTPH_ACT_CONTROL		0x2268
> +#define DP_MTPH_STATUS			0x226c
> +#define DP_INTERRUPT_SOURCE		0x2270
> +#define DP_INTERRUPT_MASK		0x2274
> +#define DP_FRONT_BACK_PORCH		0x2278
> +#define DP_BYTE_COUNT			0x227c
> +
> +/* dptx stream addr */
> +#define MSA_HORIZONTAL_0		0x2280
> +#define MSA_HORIZONTAL_1		0x2284
> +#define MSA_VERTICAL_0			0x2288
> +#define MSA_VERTICAL_1			0x228c
> +#define MSA_MISC			0x2290
> +#define STREAM_CONFIG			0x2294
> +#define AUDIO_PACK_STATUS		0x2298
> +#define VIF_STATUS			0x229c
> +#define PCK_STUFF_STATUS_0		0x22a0
> +#define PCK_STUFF_STATUS_1		0x22a4
> +#define INFO_PACK_STATUS		0x22a8
> +#define RATE_GOVERNOR_STATUS		0x22ac
> +#define DP_HORIZONTAL			0x22b0
> +#define DP_VERTICAL_0			0x22b4
> +#define DP_VERTICAL_1			0x22b8
> +#define DP_BLOCK_SDP			0x22bc
> +
> +/* dptx glbl addr */
> +#define DPTX_LANE_EN			0x2300
> +#define DPTX_ENHNCD			0x2304
> +#define DPTX_INT_MASK			0x2308
> +#define DPTX_INT_STATUS			0x230c
> +
> +/* dp aux addr */
> +#define DP_AUX_HOST_CONTROL		0x2800
> +#define DP_AUX_INTERRUPT_SOURCE		0x2804
> +#define DP_AUX_INTERRUPT_MASK		0x2808
> +#define DP_AUX_SWAP_INVERSION_CONTROL	0x280c
> +#define DP_AUX_SEND_NACK_TRANSACTION	0x2810
> +#define DP_AUX_CLEAR_RX			0x2814
> +#define DP_AUX_CLEAR_TX			0x2818
> +#define DP_AUX_TIMER_STOP		0x281c
> +#define DP_AUX_TIMER_CLEAR		0x2820
> +#define DP_AUX_RESET_SW			0x2824
> +#define DP_AUX_DIVIDE_2M		0x2828
> +#define DP_AUX_TX_PREACHARGE_LENGTH	0x282c
> +#define DP_AUX_FREQUENCY_1M_MAX		0x2830
> +#define DP_AUX_FREQUENCY_1M_MIN		0x2834
> +#define DP_AUX_RX_PRE_MIN		0x2838
> +#define DP_AUX_RX_PRE_MAX		0x283c
> +#define DP_AUX_TIMER_PRESET		0x2840
> +#define DP_AUX_NACK_FORMAT		0x2844
> +#define DP_AUX_TX_DATA			0x2848
> +#define DP_AUX_RX_DATA			0x284c
> +#define DP_AUX_TX_STATUS		0x2850
> +#define DP_AUX_RX_STATUS		0x2854
> +#define DP_AUX_RX_CYCLE_COUNTER		0x2858
> +#define DP_AUX_MAIN_STATES		0x285c
> +#define DP_AUX_MAIN_TIMER		0x2860
> +#define DP_AUX_AFE_OUT			0x2864
> +
> +/* crypto addr */
> +#define CRYPTO_HDCP_REVISION		0x5800
> +#define HDCP_CRYPTO_CONFIG		0x5804
> +#define CRYPTO_INTERRUPT_SOURCE		0x5808
> +#define CRYPTO_INTERRUPT_MASK		0x580c
> +#define CRYPTO22_CONFIG			0x5818
> +#define CRYPTO22_STATUS			0x581c
> +#define SHA_256_DATA_IN			0x583c
> +#define SHA_256_DATA_OUT_(x)		(0x5850 + ((x) << 2))
> +#define AES_32_KEY_(x)			(0x5870 + ((x) << 2))
> +#define AES_32_DATA_IN			0x5880
> +#define AES_32_DATA_OUT_(x)		(0x5884 + ((x) << 2))
> +#define CRYPTO14_CONFIG			0x58a0
> +#define CRYPTO14_STATUS			0x58a4
> +#define CRYPTO14_PRNM_OUT		0x58a8
> +#define CRYPTO14_KM_0			0x58ac
> +#define CRYPTO14_KM_1			0x58b0
> +#define CRYPTO14_AN_0			0x58b4
> +#define CRYPTO14_AN_1			0x58b8
> +#define CRYPTO14_YOUR_KSV_0		0x58bc
> +#define CRYPTO14_YOUR_KSV_1		0x58c0
> +#define CRYPTO14_MI_0			0x58c4
> +#define CRYPTO14_MI_1			0x58c8
> +#define CRYPTO14_TI_0			0x58cc
> +#define CRYPTO14_KI_0			0x58d0
> +#define CRYPTO14_KI_1			0x58d4
> +#define CRYPTO14_BLOCKS_NUM		0x58d8
> +#define CRYPTO14_KEY_MEM_DATA_0		0x58dc
> +#define CRYPTO14_KEY_MEM_DATA_1		0x58e0
> +#define CRYPTO14_SHA1_MSG_DATA		0x58e4
> +#define CRYPTO14_SHA1_V_VALUE_(x)	(0x58e8 + ((x) << 2))
> +#define TRNG_CTRL			0x58fc
> +#define TRNG_DATA_RDY			0x5900
> +#define TRNG_DATA			0x5904
> +
> +/* cipher addr */
> +#define HDCP_REVISION			0x60000
> +#define INTERRUPT_SOURCE		0x60004
> +#define INTERRUPT_MASK			0x60008
> +#define HDCP_CIPHER_CONFIG		0x6000c
> +#define AES_128_KEY_0			0x60010
> +#define AES_128_KEY_1			0x60014
> +#define AES_128_KEY_2			0x60018
> +#define AES_128_KEY_3			0x6001c
> +#define AES_128_RANDOM_0		0x60020
> +#define AES_128_RANDOM_1		0x60024
> +#define CIPHER14_KM_0			0x60028
> +#define CIPHER14_KM_1			0x6002c
> +#define CIPHER14_STATUS			0x60030
> +#define CIPHER14_RI_PJ_STATUS		0x60034
> +#define CIPHER_MODE			0x60038
> +#define CIPHER14_AN_0			0x6003c
> +#define CIPHER14_AN_1			0x60040
> +#define CIPHER22_AUTH			0x60044
> +#define CIPHER14_R0_DP_STATUS		0x60048
> +#define CIPHER14_BOOTSTRAP		0x6004c
> +
> +#define DPTX_FRMR_DATA_CLK_RSTN_EN	BIT(11)
> +#define DPTX_FRMR_DATA_CLK_EN		BIT(10)
> +#define DPTX_PHY_DATA_RSTN_EN		BIT(9)
> +#define DPTX_PHY_DATA_CLK_EN		BIT(8)
> +#define DPTX_PHY_CHAR_RSTN_EN		BIT(7)
> +#define DPTX_PHY_CHAR_CLK_EN		BIT(6)
> +#define SOURCE_AUX_SYS_CLK_RSTN_EN	BIT(5)
> +#define SOURCE_AUX_SYS_CLK_EN		BIT(4)
> +#define DPTX_SYS_CLK_RSTN_EN		BIT(3)
> +#define DPTX_SYS_CLK_EN			BIT(2)
> +#define CFG_DPTX_VIF_CLK_RSTN_EN	BIT(1)
> +#define CFG_DPTX_VIF_CLK_EN		BIT(0)
> +
> +#define SOURCE_PHY_RSTN_EN		BIT(1)
> +#define SOURCE_PHY_CLK_EN		BIT(0)
> +
> +#define SOURCE_PKT_SYS_RSTN_EN		BIT(3)
> +#define SOURCE_PKT_SYS_CLK_EN		BIT(2)
> +#define SOURCE_PKT_DATA_RSTN_EN		BIT(1)
> +#define SOURCE_PKT_DATA_CLK_EN		BIT(0)
> +
> +#define SPDIF_CDR_CLK_RSTN_EN		BIT(5)
> +#define SPDIF_CDR_CLK_EN		BIT(4)
> +#define SOURCE_AIF_SYS_RSTN_EN		BIT(3)
> +#define SOURCE_AIF_SYS_CLK_EN		BIT(2)
> +#define SOURCE_AIF_CLK_RSTN_EN		BIT(1)
> +#define SOURCE_AIF_CLK_EN		BIT(0)
> +
> +#define SOURCE_CIPHER_SYSTEM_CLK_RSTN_EN	BIT(3)
> +#define SOURCE_CIPHER_SYS_CLK_EN		BIT(2)
> +#define SOURCE_CIPHER_CHAR_CLK_RSTN_EN		BIT(1)
> +#define SOURCE_CIPHER_CHAR_CLK_EN		BIT(0)
> +
> +#define SOURCE_CRYPTO_SYS_CLK_RSTN_EN	BIT(1)
> +#define SOURCE_CRYPTO_SYS_CLK_EN	BIT(0)
> +
> +#define APB_IRAM_PATH			BIT(2)
> +#define APB_DRAM_PATH			BIT(1)
> +#define APB_XT_RESET			BIT(0)
> +
> +#define MAILBOX_INT_MASK_BIT		BIT(1)
> +#define PIF_INT_MASK_BIT		BIT(0)
> +#define ALL_INT_MASK			3
> +
> +/* mailbox */
> +#define MB_OPCODE_ID			0
> +#define MB_MODULE_ID			1
> +#define MB_SIZE_MSB_ID			2
> +#define MB_SIZE_LSB_ID			3
> +#define MB_DATA_ID			4
> +
> +#define MB_MODULE_ID_DP_TX		0x01
> +#define MB_MODULE_ID_HDCP_TX		0x07
> +#define MB_MODULE_ID_HDCP_RX		0x08
> +#define MB_MODULE_ID_HDCP_GENERAL	0x09
> +#define MB_MODULE_ID_GENERAL		0x0a
> +
> +/* general opcode */
> +#define GENERAL_MAIN_CONTROL            0x01
> +#define GENERAL_TEST_ECHO               0x02
> +#define GENERAL_BUS_SETTINGS            0x03
> +#define GENERAL_TEST_ACCESS             0x04
> +
> +#define DPTX_SET_POWER_MNG			0x00
> +#define DPTX_SET_HOST_CAPABILITIES		0x01
> +#define DPTX_GET_EDID				0x02
> +#define DPTX_READ_DPCD				0x03
> +#define DPTX_WRITE_DPCD				0x04
> +#define DPTX_ENABLE_EVENT			0x05
> +#define DPTX_WRITE_REGISTER			0x06
> +#define DPTX_READ_REGISTER			0x07
> +#define DPTX_WRITE_FIELD			0x08
> +#define DPTX_TRAINING_CONTROL			0x09
> +#define DPTX_READ_EVENT				0x0a
> +#define DPTX_READ_LINK_STAT			0x0b
> +#define DPTX_SET_VIDEO				0x0c
> +#define DPTX_SET_AUDIO				0x0d
> +#define DPTX_GET_LAST_AUX_STAUS			0x0e
> +#define DPTX_SET_LINK_BREAK_POINT		0x0f
> +#define DPTX_FORCE_LANES			0x10
> +#define DPTX_HPD_STATE				0x11
> +
> +#define DPTX_EVENT_ENABLE_HPD			BIT(0)
> +#define DPTX_EVENT_ENABLE_TRAINING		BIT(1)
> +
> +#define LINK_TRAINING_NOT_ACTIVE		0
> +#define LINK_TRAINING_RUN			1
> +#define LINK_TRAINING_RESTART			2
> +
> +#define CONTROL_VIDEO_IDLE			0
> +#define CONTROL_VIDEO_VALID			1
> +
> +#define VIF_BYPASS_INTERLACE			BIT(13)
> +#define INTERLACE_FMT_DET			BIT(12)
> +#define INTERLACE_DTCT_WIN			0x20
> +
> +#define DP_FRAMER_SP_INTERLACE_EN		BIT(2)
> +#define DP_FRAMER_SP_HSP			BIT(1)
> +#define DP_FRAMER_SP_VSP			BIT(0)
> +
> +/* capability */
> +#define AUX_HOST_INVERT				3
> +#define	FAST_LT_SUPPORT				1
> +#define FAST_LT_NOT_SUPPORT			0
> +#define LANE_MAPPING_NORMAL			0xe4
> +#define LANE_MAPPING_FLIPPED			0x1b
> +#define ENHANCED				1
> +
> +#define	FULL_LT_STARTED				BIT(0)
> +#define FASE_LT_STARTED				BIT(1)
> +#define CLK_RECOVERY_FINISHED			BIT(2)
> +#define EQ_PHASE_FINISHED			BIT(3)
> +#define FASE_LT_START_FINISHED			BIT(4)
> +#define CLK_RECOVERY_FAILED			BIT(5)
> +#define EQ_PHASE_FAILED				BIT(6)
> +#define FASE_LT_FAILED				BIT(7)
> +
> +#define DPTX_HPD_EVENT				BIT(0)
> +#define DPTX_TRAINING_EVENT			BIT(1)
> +#define HDCP_TX_STATUS_EVENT			BIT(4)
> +#define HDCP2_TX_IS_KM_STORED_EVENT		BIT(5)
> +#define HDCP2_TX_STORE_KM_EVENT			BIT(6)
> +#define HDCP_TX_IS_RECEIVER_ID_VALID_EVENT	BIT(7)
> +
> +#define TU_SIZE					30
> +#define CDN_DP_MAX_LINK_RATE			DP_LINK_BW_5_4
> +
> +/* audio */
> +#define AUDIO_PACK_EN				BIT(8)
> +#define SAMPLING_FREQ(x)			(((x) & 0xf) << 16)
> +#define ORIGINAL_SAMP_FREQ(x)			(((x) & 0xf) << 24)
> +#define SYNC_WR_TO_CH_ZERO			BIT(1)
> +#define I2S_DEC_START				BIT(1)
> +#define AUDIO_SW_RST				BIT(0)
> +#define SMPL2PKT_EN				BIT(1)
> +#define MAX_NUM_CH(x)				(((x) & 0x1f) - 1)
> +#define NUM_OF_I2S_PORTS(x)			((((x) / 2 - 1) & 0x3) << 5)
> +#define AUDIO_TYPE_LPCM				(2 << 7)
> +#define CFG_SUB_PCKT_NUM(x)			((((x) - 1) & 0x7) << 11)
> +#define AUDIO_CH_NUM(x)				((((x) - 1) & 0x1f) << 2)
> +#define TRANS_SMPL_WIDTH_16			0
> +#define TRANS_SMPL_WIDTH_24			BIT(11)
> +#define TRANS_SMPL_WIDTH_32			(2 << 11)
> +#define I2S_DEC_PORT_EN(x)			(((x) & 0xf) << 17)
> +#define SPDIF_ENABLE				BIT(21)
> +#define SPDIF_AVG_SEL				BIT(20)
> +#define SPDIF_JITTER_BYPASS			BIT(19)
> +#define SPDIF_FIFO_MID_RANGE(x)			(((x) & 0xff) << 11)
> +#define SPDIF_JITTER_THRSH(x)			(((x) & 0xff) << 3)
> +#define SPDIF_JITTER_AVG_WIN(x)			((x) & 0x7)
> +
> +/* Refernce cycles when using lane clock as refernce */
> +#define LANE_REF_CYC				0x8000
> +
> +enum voltage_swing_level {
> +	VOLTAGE_LEVEL_0,
> +	VOLTAGE_LEVEL_1,
> +	VOLTAGE_LEVEL_2,
> +	VOLTAGE_LEVEL_3,
> +};
> +
> +enum pre_emphasis_level {
> +	PRE_EMPHASIS_LEVEL_0,
> +	PRE_EMPHASIS_LEVEL_1,
> +	PRE_EMPHASIS_LEVEL_2,
> +	PRE_EMPHASIS_LEVEL_3,
> +};
> +
> +enum pattern_set {
> +	PTS1		= BIT(0),
> +	PTS2		= BIT(1),
> +	PTS3		= BIT(2),
> +	PTS4		= BIT(3),
> +	DP_NONE		= BIT(4)
> +};
> +
> +enum vic_color_depth {
> +	BCS_6 = 0x1,
> +	BCS_8 = 0x2,
> +	BCS_10 = 0x4,
> +	BCS_12 = 0x8,
> +	BCS_16 = 0x10,
> +};
> +
> +enum vic_bt_type {
> +	BT_601 = 0x0,
> +	BT_709 = 0x1,
> +};
> +
> +void cdn_dp_clock_reset(struct cdn_dp_device *dp);
> +
> +void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, u32 clk);
> +int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
> +			 u32 i_size, const u32 *d_mem, u32 d_size);
> +int cdn_dp_active(struct cdn_dp_device *dp, bool enable);
> +int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes);
> +int cdn_dp_event_config(struct cdn_dp_device *dp);
> +u32 cdn_dp_get_event(struct cdn_dp_device *dp);
> +int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
> +int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value);
> +int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len);
> +int cdn_dp_get_edid_block(void *dp, u8 *edid,
> +			  unsigned int block, size_t length);
> +int cdn_dp_training_start(struct cdn_dp_device *dp);
> +int cdn_dp_get_training_status(struct cdn_dp_device *dp);
> +int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active);
> +int cdn_dp_config_video(struct cdn_dp_device *dp);
> +int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio);
> +int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable);
> +int cdn_dp_audio_config_set(struct cdn_dp_device *dp, struct audio_info *audio);
> +#endif /* _CDN_DP_REG_H */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> index edd7ec2..ceb4b86 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> @@ -969,9 +969,9 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
>  		vop_dsp_hold_valid_irq_disable(vop);
>  	}
>  
> -	pin_pol = 0x8;
> -	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1;
> -	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : (1 << 1);
> +	pin_pol = BIT(DCLK_INVERT);
> +	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : BIT(HSYNC_POSITIVE);
> +	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : BIT(VSYNC_POSITIVE);
>  	VOP_CTRL_SET(vop, pin_pol, pin_pol);
>  
>  	switch (s->output_type) {
> @@ -991,6 +991,11 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
>  		VOP_CTRL_SET(vop, mipi_pin_pol, pin_pol);
>  		VOP_CTRL_SET(vop, mipi_en, 1);
>  		break;
> +	case DRM_MODE_CONNECTOR_DisplayPort:
> +		pin_pol &= ~BIT(DCLK_INVERT);
> +		VOP_CTRL_SET(vop, dp_pin_pol, pin_pol);
> +		VOP_CTRL_SET(vop, dp_en, 1);
> +		break;
>  	default:
>  		DRM_ERROR("unsupport connector_type[%d]\n", s->output_type);
>  	}
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> index ff4f52e..4820a8b 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> @@ -45,6 +45,7 @@ struct vop_ctrl {
>  	struct vop_reg edp_en;
>  	struct vop_reg hdmi_en;
>  	struct vop_reg mipi_en;
> +	struct vop_reg dp_en;
>  	struct vop_reg out_mode;
>  	struct vop_reg dither_down;
>  	struct vop_reg dither_up;
> @@ -53,6 +54,7 @@ struct vop_ctrl {
>  	struct vop_reg hdmi_pin_pol;
>  	struct vop_reg edp_pin_pol;
>  	struct vop_reg mipi_pin_pol;
> +	struct vop_reg dp_pin_pol;
>  
>  	struct vop_reg htotal_pw;
>  	struct vop_reg hact_st_end;
> @@ -242,6 +244,13 @@ enum scale_down_mode {
>  	SCALE_DOWN_AVG = 0x1
>  };
>  
> +enum vop_pol {
> +	HSYNC_POSITIVE = 0,
> +	VSYNC_POSITIVE = 1,
> +	DEN_NEGATIVE   = 2,
> +	DCLK_INVERT    = 3
> +};
> +
>  #define FRAC_16_16(mult, div)    (((mult) << 16) / (div))
>  #define SCL_FT_DEFAULT_FIXPOINT_SHIFT	12
>  #define SCL_MAX_VSKIPLINES		4
> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> index 5b1ae1f..dcf172e 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> @@ -281,6 +281,7 @@ static const struct vop_data rk3288_vop = {
>  static const struct vop_ctrl rk3399_ctrl_data = {
>  	.standby = VOP_REG(RK3399_SYS_CTRL, 0x1, 22),
>  	.gate_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 23),
> +	.dp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 11),
>  	.rgb_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 12),
>  	.hdmi_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 13),
>  	.edp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 14),
> @@ -290,6 +291,7 @@ static const struct vop_ctrl rk3399_ctrl_data = {
>  	.data_blank = VOP_REG(RK3399_DSP_CTRL0, 0x1, 19),
>  	.out_mode = VOP_REG(RK3399_DSP_CTRL0, 0xf, 0),
>  	.rgb_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16),
> +	.dp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16),
>  	.hdmi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 20),
>  	.edp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 24),
>  	.mipi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 28),
> -- 
> 2.6.3
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel


From heiko at sntech.de  Tue Jul 19 11:56:47 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Tue, 19 Jul 2016 20:56:47 +0200
Subject: [PATCH] arm64: dts: rockchip: add spiX aliases for rk3399
In-Reply-To: <20160719063955.GE5368@pengutronix.de>
References: <1468545873-134339-1-git-send-email-briannorris@chromium.org>
 <20160719063955.GE5368@pengutronix.de>
Message-ID: <2679180.WfB7C6de5F@diego>

Hi Uwe,

Am Dienstag, 19. Juli 2016, 08:39:55 schrieb Uwe Kleine-K?nig:
> On Thu, Jul 14, 2016 at 06:24:33PM -0700, Brian Norris wrote:
> > It's nicer to see this:
> >     # ls -l /sys/bus/spi/devices/
> >     total 0
> >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi0.0 ->
> >     ../../../devices/platform/ff1c0000.spi/spi_master/spi0/spi0.0
> >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi1.0 ->
> >     ../../../devices/platform/ff1d0000.spi/spi_master/spi1/spi1.0
> >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi2.0 ->
> >     ../../../devices/platform/ff1e0000.spi/spi_master/spi2/spi2.0
> >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi5.0 ->
> >     ../../../devices/platform/ff200000.spi/spi_master/spi5/spi5.0> 
> > than this:
> >     # ls -l /sys/bus/spi/devices/
> >     total 0
> >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi32763.0 ->
> >     ../../../devices/platform/ff200000.spi/spi_master/spi32763/spi32763.0
> >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi32764.0 ->
> >     ../../../devices/platform/ff1e0000.spi/spi_master/spi32764/spi32764.0
> >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi32765.0 ->
> >     ../../../devices/platform/ff1d0000.spi/spi_master/spi32765/spi32765.0
> >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi32766.0 ->
> >     ../../../devices/platform/ff1c0000.spi/spi_master/spi32766/spi32766.0> 
> > With the latter, it's much clearer which bus devices are on.
> > 
> > Signed-off-by: Brian Norris <briannorris at chromium.org>
> > ---
> > 
> >  arch/arm64/boot/dts/rockchip/rk3399.dtsi | 6 ++++++
> >  1 file changed, 6 insertions(+)
> > 
> > diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> > b/arch/arm64/boot/dts/rockchip/rk3399.dtsi index
> > 4c84229789ef..b04abbfd4898 100644
> > --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> > +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> > @@ -70,6 +70,12 @@
> > 
> >  		serial2 = &uart2;
> >  		serial3 = &uart3;
> >  		serial4 = &uart4;
> > 
> > +		spi0 = &spi0;
> > +		spi1 = &spi1;
> > +		spi2 = &spi2;
> > +		spi3 = &spi3;
> > +		spi4 = &spi4;
> > +		spi5 = &spi5;
> 
> Note that Rob Herring (with his dt-maintainer hat on) doesn't like these
> aliases.
> See for example:
> 	http://mid.gmane.org/20160705140546.GA10601 at rob-hp-laptop

thanks for the heads up on this. I've dropped this patch again.


Thanks
Heiko


From briannorris at chromium.org  Tue Jul 19 12:27:54 2016
From: briannorris at chromium.org (Brian Norris)
Date: Tue, 19 Jul 2016 12:27:54 -0700
Subject: [PATCH] arm64: dts: rockchip: add spiX aliases for rk3399
In-Reply-To: <2679180.WfB7C6de5F@diego>
References: <1468545873-134339-1-git-send-email-briannorris@chromium.org>
 <20160719063955.GE5368@pengutronix.de> <2679180.WfB7C6de5F@diego>
Message-ID: <20160719192754.GA140860@google.com>

+ Rob

Hi,

On Tue, Jul 19, 2016 at 08:56:47PM +0200, Heiko Stuebner wrote:
> Am Dienstag, 19. Juli 2016, 08:39:55 schrieb Uwe Kleine-K?nig:
> > On Thu, Jul 14, 2016 at 06:24:33PM -0700, Brian Norris wrote:
> > > It's nicer to see this:
> > >     # ls -l /sys/bus/spi/devices/
> > >     total 0
> > >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi0.0 ->
> > >     ../../../devices/platform/ff1c0000.spi/spi_master/spi0/spi0.0
> > >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi1.0 ->
> > >     ../../../devices/platform/ff1d0000.spi/spi_master/spi1/spi1.0
> > >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi2.0 ->
> > >     ../../../devices/platform/ff1e0000.spi/spi_master/spi2/spi2.0
> > >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi5.0 ->
> > >     ../../../devices/platform/ff200000.spi/spi_master/spi5/spi5.0> 
> > > than this:
> > >     # ls -l /sys/bus/spi/devices/
> > >     total 0
> > >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi32763.0 ->
> > >     ../../../devices/platform/ff200000.spi/spi_master/spi32763/spi32763.0
> > >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi32764.0 ->
> > >     ../../../devices/platform/ff1e0000.spi/spi_master/spi32764/spi32764.0
> > >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi32765.0 ->
> > >     ../../../devices/platform/ff1d0000.spi/spi_master/spi32765/spi32765.0
> > >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi32766.0 ->
> > >     ../../../devices/platform/ff1c0000.spi/spi_master/spi32766/spi32766.0> 
> > > With the latter, it's much clearer which bus devices are on.
> > > 
> > > Signed-off-by: Brian Norris <briannorris at chromium.org>
> > > ---
> > > 
> > >  arch/arm64/boot/dts/rockchip/rk3399.dtsi | 6 ++++++
> > >  1 file changed, 6 insertions(+)
> > > 
> > > diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> > > b/arch/arm64/boot/dts/rockchip/rk3399.dtsi index
> > > 4c84229789ef..b04abbfd4898 100644
> > > --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> > > +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> > > @@ -70,6 +70,12 @@
> > > 
> > >  		serial2 = &uart2;
> > >  		serial3 = &uart3;
> > >  		serial4 = &uart4;
> > > 
> > > +		spi0 = &spi0;
> > > +		spi1 = &spi1;
> > > +		spi2 = &spi2;
> > > +		spi3 = &spi3;
> > > +		spi4 = &spi4;
> > > +		spi5 = &spi5;
> > 
> > Note that Rob Herring (with his dt-maintainer hat on) doesn't like these
> > aliases.
> > See for example:
> > 	http://mid.gmane.org/20160705140546.GA10601 at rob-hp-laptop

But why? I believe half the arguments in the linked thread were
Catch-22's -- there were no "mainline" users (which is a false target
IMO anyway, as Christer Weinigel mentioned somewhere in the thread Rob
linked), and so we couldn't accept more documentation (or users) for the
feature. FWIW, my quick grep now shows there are currently 43 mainline
users.

This feature is very useful. Some of the thread Rob pointed to argued
that the indexing isn't HW documentation; in this case, it most
certainly is. The RK3399 TRM explicitly has these SPI buses named SPI0,
SPI1, SPI2, SPI3, SPI4, and SPI5, and schematics that I've seen use the
same terminology. It is therefore *much* nicer to have my device show up
as 'spi2.0' (reflecting their correct HW name) rather than spi32764.0.
Sometimes I can't even get as far as mounting sysfs to check what this
maps to, but having spi2.0 in the kernel log can clearly tell me what
device is causing problems.

If Rob can support a better alternative solution, I'd be happy to
switch. But I don't understand why we can't use a useful (not just to
me, but presumably to at least 43 other independent users, and many
more out of tree), existing, and long-supported feature here.

> thanks for the heads up on this. I've dropped this patch again.

:(

Regards,
Brian


From briannorris at chromium.org  Tue Jul 19 12:29:14 2016
From: briannorris at chromium.org (Brian Norris)
Date: Tue, 19 Jul 2016 12:29:14 -0700
Subject: [PATCH] arm64: dts: rockchip: add spiX aliases for rk3399
In-Reply-To: <20160719192754.GA140860@google.com>
References: <1468545873-134339-1-git-send-email-briannorris@chromium.org>
 <20160719063955.GE5368@pengutronix.de> <2679180.WfB7C6de5F@diego>
 <20160719192754.GA140860@google.com>
Message-ID: <20160719192912.GA142821@google.com>

On Tue, Jul 19, 2016 at 12:27:54PM -0700, Brian Norris wrote:
> + Rob

+ Rob for real

> Hi,
> 
> On Tue, Jul 19, 2016 at 08:56:47PM +0200, Heiko Stuebner wrote:
> > Am Dienstag, 19. Juli 2016, 08:39:55 schrieb Uwe Kleine-K?nig:
> > > On Thu, Jul 14, 2016 at 06:24:33PM -0700, Brian Norris wrote:
> > > > It's nicer to see this:
> > > >     # ls -l /sys/bus/spi/devices/
> > > >     total 0
> > > >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi0.0 ->
> > > >     ../../../devices/platform/ff1c0000.spi/spi_master/spi0/spi0.0
> > > >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi1.0 ->
> > > >     ../../../devices/platform/ff1d0000.spi/spi_master/spi1/spi1.0
> > > >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi2.0 ->
> > > >     ../../../devices/platform/ff1e0000.spi/spi_master/spi2/spi2.0
> > > >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi5.0 ->
> > > >     ../../../devices/platform/ff200000.spi/spi_master/spi5/spi5.0> 
> > > > than this:
> > > >     # ls -l /sys/bus/spi/devices/
> > > >     total 0
> > > >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi32763.0 ->
> > > >     ../../../devices/platform/ff200000.spi/spi_master/spi32763/spi32763.0
> > > >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi32764.0 ->
> > > >     ../../../devices/platform/ff1e0000.spi/spi_master/spi32764/spi32764.0
> > > >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi32765.0 ->
> > > >     ../../../devices/platform/ff1d0000.spi/spi_master/spi32765/spi32765.0
> > > >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi32766.0 ->
> > > >     ../../../devices/platform/ff1c0000.spi/spi_master/spi32766/spi32766.0> 
> > > > With the latter, it's much clearer which bus devices are on.
> > > > 
> > > > Signed-off-by: Brian Norris <briannorris at chromium.org>
> > > > ---
> > > > 
> > > >  arch/arm64/boot/dts/rockchip/rk3399.dtsi | 6 ++++++
> > > >  1 file changed, 6 insertions(+)
> > > > 
> > > > diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> > > > b/arch/arm64/boot/dts/rockchip/rk3399.dtsi index
> > > > 4c84229789ef..b04abbfd4898 100644
> > > > --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> > > > +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> > > > @@ -70,6 +70,12 @@
> > > > 
> > > >  		serial2 = &uart2;
> > > >  		serial3 = &uart3;
> > > >  		serial4 = &uart4;
> > > > 
> > > > +		spi0 = &spi0;
> > > > +		spi1 = &spi1;
> > > > +		spi2 = &spi2;
> > > > +		spi3 = &spi3;
> > > > +		spi4 = &spi4;
> > > > +		spi5 = &spi5;
> > > 
> > > Note that Rob Herring (with his dt-maintainer hat on) doesn't like these
> > > aliases.
> > > See for example:
> > > 	http://mid.gmane.org/20160705140546.GA10601 at rob-hp-laptop
> 
> But why? I believe half the arguments in the linked thread were
> Catch-22's -- there were no "mainline" users (which is a false target
> IMO anyway, as Christer Weinigel mentioned somewhere in the thread Rob
> linked), and so we couldn't accept more documentation (or users) for the
> feature. FWIW, my quick grep now shows there are currently 43 mainline
> users.
> 
> This feature is very useful. Some of the thread Rob pointed to argued
> that the indexing isn't HW documentation; in this case, it most
> certainly is. The RK3399 TRM explicitly has these SPI buses named SPI0,
> SPI1, SPI2, SPI3, SPI4, and SPI5, and schematics that I've seen use the
> same terminology. It is therefore *much* nicer to have my device show up
> as 'spi2.0' (reflecting their correct HW name) rather than spi32764.0.
> Sometimes I can't even get as far as mounting sysfs to check what this
> maps to, but having spi2.0 in the kernel log can clearly tell me what
> device is causing problems.
> 
> If Rob can support a better alternative solution, I'd be happy to
> switch. But I don't understand why we can't use a useful (not just to
> me, but presumably to at least 43 other independent users, and many
> more out of tree), existing, and long-supported feature here.
> 
> > thanks for the heads up on this. I've dropped this patch again.
> 
> :(
> 
> Regards,
> Brian


From seanpaul at google.com  Tue Jul 19 13:38:15 2016
From: seanpaul at google.com (Sean Paul)
Date: Tue, 19 Jul 2016 16:38:15 -0400
Subject: [PATCH -next] drm/rockchip: analogix_dp: add missing
 clk_disable_unprepare() on error
In-Reply-To: <1468927963-2947-1-git-send-email-weiyj_lk@163.com>
References: <1468927963-2947-1-git-send-email-weiyj_lk@163.com>
Message-ID: <CAOw6vbKT--+A=wFdx2=PT7FCYga-ArDSrw4ypFDmqqaiitHyPQ@mail.gmail.com>

On Tue, Jul 19, 2016 at 7:32 AM, Wei Yongjun <weiyj_lk at 163.com> wrote:
> From: Wei Yongjun <yongjun_wei at trendmicro.com.cn>
>
> Add the missing clk_disable_unprepare() before return in the
> error handling case.
>
> Signed-off-by: Wei Yongjun <yongjun_wei at trendmicro.com.cn>


Applied to drm-misc



> ---
>  drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 2 ++
>  1 file changed, 2 insertions(+)
>
> diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
> index e81e19a..89aadbf 100644
> --- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
> +++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
> @@ -96,6 +96,7 @@ static int rockchip_dp_poweron(struct analogix_dp_plat_data *plat_data)
>         ret = rockchip_dp_pre_init(dp);
>         if (ret < 0) {
>                 dev_err(dp->dev, "failed to dp pre init %d\n", ret);
> +               clk_disable_unprepare(dp->pclk);
>                 return ret;
>         }
>
> @@ -272,6 +273,7 @@ static int rockchip_dp_init(struct rockchip_dp_device *dp)
>         ret = rockchip_dp_pre_init(dp);
>         if (ret < 0) {
>                 dev_err(dp->dev, "failed to pre init %d\n", ret);
> +               clk_disable_unprepare(dp->pclk);
>                 return ret;
>         }
>
>


From heiko at sntech.de  Tue Jul 19 15:18:40 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Wed, 20 Jul 2016 00:18:40 +0200
Subject: [PATCH] arm64: dts: rockchip: add spiX aliases for rk3399
In-Reply-To: <20160719192912.GA142821@google.com>
References: <1468545873-134339-1-git-send-email-briannorris@chromium.org>
 <20160719192754.GA140860@google.com> <20160719192912.GA142821@google.com>
Message-ID: <1841177.NDa9D09EGz@diego>

Am Dienstag, 19. Juli 2016, 12:29:14 schrieb Brian Norris:
> On Tue, Jul 19, 2016 at 12:27:54PM -0700, Brian Norris wrote:
> > + Rob
> 
> + Rob for real
> 
> > Hi,
> > 
> > On Tue, Jul 19, 2016 at 08:56:47PM +0200, Heiko Stuebner wrote:
> > > Am Dienstag, 19. Juli 2016, 08:39:55 schrieb Uwe Kleine-K?nig:
> > > > On Thu, Jul 14, 2016 at 06:24:33PM -0700, Brian Norris wrote:
> > > > > It's nicer to see this:
> > > > >     # ls -l /sys/bus/spi/devices/
> > > > >     total 0
> > > > >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi0.0 ->
> > > > >     ../../../devices/platform/ff1c0000.spi/spi_master/spi0/spi0.0
> > > > >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi1.0 ->
> > > > >     ../../../devices/platform/ff1d0000.spi/spi_master/spi1/spi1.0
> > > > >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi2.0 ->
> > > > >     ../../../devices/platform/ff1e0000.spi/spi_master/spi2/spi2.0
> > > > >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi5.0 ->
> > > > >     ../../../devices/platform/ff200000.spi/spi_master/spi5/spi5.0>
> > > > > 
> > > > > than this:
> > > > >     # ls -l /sys/bus/spi/devices/
> > > > >     total 0
> > > > >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi32763.0 ->
> > > > >     ../../../devices/platform/ff200000.spi/spi_master/spi32763/spi32
> > > > >     763.0
> > > > >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi32764.0 ->
> > > > >     ../../../devices/platform/ff1e0000.spi/spi_master/spi32764/spi32
> > > > >     764.0
> > > > >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi32765.0 ->
> > > > >     ../../../devices/platform/ff1d0000.spi/spi_master/spi32765/spi32
> > > > >     765.0
> > > > >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi32766.0 ->
> > > > >     ../../../devices/platform/ff1c0000.spi/spi_master/spi32766/spi32
> > > > >     766.0>
> > > > > 
> > > > > With the latter, it's much clearer which bus devices are on.
> > > > > 
> > > > > Signed-off-by: Brian Norris <briannorris at chromium.org>
> > > > > ---
> > > > > 
> > > > >  arch/arm64/boot/dts/rockchip/rk3399.dtsi | 6 ++++++
> > > > >  1 file changed, 6 insertions(+)
> > > > > 
> > > > > diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> > > > > b/arch/arm64/boot/dts/rockchip/rk3399.dtsi index
> > > > > 4c84229789ef..b04abbfd4898 100644
> > > > > --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> > > > > +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> > > > > @@ -70,6 +70,12 @@
> > > > > 
> > > > >  		serial2 = &uart2;
> > > > >  		serial3 = &uart3;
> > > > >  		serial4 = &uart4;
> > > > > 
> > > > > +		spi0 = &spi0;
> > > > > +		spi1 = &spi1;
> > > > > +		spi2 = &spi2;
> > > > > +		spi3 = &spi3;
> > > > > +		spi4 = &spi4;
> > > > > +		spi5 = &spi5;
> > > > 
> > > > Note that Rob Herring (with his dt-maintainer hat on) doesn't like
> > > > these
> > > > aliases.
> > > > 
> > > > See for example:
> > > > 	http://mid.gmane.org/20160705140546.GA10601 at rob-hp-laptop
> > 
> > But why? I believe half the arguments in the linked thread were
> > Catch-22's -- there were no "mainline" users (which is a false target
> > IMO anyway, as Christer Weinigel mentioned somewhere in the thread Rob
> > linked), and so we couldn't accept more documentation (or users) for the
> > feature. FWIW, my quick grep now shows there are currently 43 mainline
> > users.
> > 
> > This feature is very useful. Some of the thread Rob pointed to argued
> > that the indexing isn't HW documentation; in this case, it most
> > certainly is. The RK3399 TRM explicitly has these SPI buses named SPI0,
> > SPI1, SPI2, SPI3, SPI4, and SPI5, and schematics that I've seen use the
> > same terminology. It is therefore *much* nicer to have my device show up
> > as 'spi2.0' (reflecting their correct HW name) rather than spi32764.0.
> > Sometimes I can't even get as far as mounting sysfs to check what this
> > maps to, but having spi2.0 in the kernel log can clearly tell me what
> > device is causing problems.
> > 
> > If Rob can support a better alternative solution, I'd be happy to
> > switch. But I don't understand why we can't use a useful (not just to
> > me, but presumably to at least 43 other independent users, and many
> > more out of tree), existing, and long-supported feature here.

Personally, I don't have any real opinion on this but the argument sounds 
plausible to me (aka they are named with numbers in every manual, schematics) 
so it might be helpful to have that around - similar to i2c that is in the 
dtsi already.

I think I remember Doug having a similar discussion around mmc-aliases some 
time ago - that met opposition as well [0] - although I guess the numbering 
was a bit more arbitary there.

I guess we'll just see what Rob says.


Heiko

[0] https://lkml.org/lkml/2016/4/29/610



From shawn.lin at rock-chips.com  Tue Jul 19 18:33:10 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Wed, 20 Jul 2016 09:33:10 +0800
Subject: [PATCH 2/2] mmc: core: fall back host->f_init if failing to init
 mmc card after resume
In-Reply-To: <CAPDyKFq9ZogJjGyR6gB3QxnJeP_5XH-GXKY6+-HXsOBDDTAMqQ@mail.gmail.com>
References: <1468918715-24914-1-git-send-email-shawn.lin@rock-chips.com>
 <1468918715-24914-2-git-send-email-shawn.lin@rock-chips.com>
 <CAPDyKFq9ZogJjGyR6gB3QxnJeP_5XH-GXKY6+-HXsOBDDTAMqQ@mail.gmail.com>
Message-ID: <dda44de1-6467-802f-4b4d-9de8f604357c@rock-chips.com>

? 2016/7/19 18:31, Ulf Hansson ??:
> On 19 July 2016 at 10:58, Shawn Lin <shawn.lin at rock-chips.com> wrote:
>> We observed the failure of initializing card after resume
>> accidentally. It's hard to reproduce but we did get report from
>> the suspend/resume test of our RK3399 mp test farm . Unfortunately,
>> we still fail to figure out what was going wrong at that time.
>> Also we can't achieve it by retrying the host->f_init without falling
>> back it. But this patch will solve the problem as we could add some log
>> there and see that we resume the mmc card successfully after falling
>> back the host->f_init. There is no obvious side effect found, so it seems
>> this patch will improve the stability.
>
> What f_init did the original rescan work find out being successful?

The original f_init was 400k when booting up the system.

>
> Did you then verify that it was *another* frequency that made the
> initialization to work in the resume?

yes, I added some log and saw 100k made the initialization to work in
the resume finally with the patch applied.

Also I mentioned in the commit msg that if I just retried 400K, it
cannot made the initialization successful.

>
>>
>> [   93.405085] mmc1: unexpected status 0x800900 after switch
>> [   93.408474] mmc1: switch to bus width 1 failed
>> [   93.408482] mmc1: mmc_select_hs200 failed, error -110
>> [   93.408492] mmc1: error -110 during resume (card was removed?)
>> [   93.408705] PM: resume of devices complete after 213.453 msecs
>>
>> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
>> ---
>>
>>  drivers/mmc/core/mmc.c | 22 ++++++++++++++++++++--
>>  1 file changed, 20 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
>> index 403b97b..bef40c8 100644
>> --- a/drivers/mmc/core/mmc.c
>> +++ b/drivers/mmc/core/mmc.c
>> @@ -1945,6 +1945,7 @@ static int mmc_suspend(struct mmc_host *host)
>>  static int _mmc_resume(struct mmc_host *host)
>>  {
>>         int err = 0;
>> +       int i;
>>
>>         BUG_ON(!host);
>>         BUG_ON(!host->card);
>> @@ -1954,8 +1955,25 @@ static int _mmc_resume(struct mmc_host *host)
>>         if (!mmc_card_suspended(host->card))
>>                 goto out;
>>
>> -       mmc_power_up(host, host->card->ocr);
>> -       err = mmc_init_card(host, host->card->ocr, host->card);
>> +       /*
>> +        * Let's try to fallback the host->f_init
>> +        * if failing to init card after resume.
>> +        */
>> +
>> +       for (i = 0; i < ARRAY_SIZE(freqs); i++) {
>> +               if (host->f_init < freqs[i])
>> +                       continue;
>> +               else
>> +                       host->f_init = freqs[i];
>
> This loop is wrong, as you don't consider that host->f_min may not
> exactly match the frequencies in the freqs array.

Ah, seems I should use 'max(freqs[i], host->f_min)'.. :)

>
>> +
>> +               mmc_power_up(host, host->card->ocr);
>> +               err = mmc_init_card(host, host->card->ocr, host->card);
>> +               if (!err)
>> +                       break;
>> +
>> +               mmc_power_off(host);
>
> The mmc core expects the host/card to be powered up after a resume, as
> it may send commands to it without first invoking mmc_power_up().
> Even if those commands may fail (or timeout), at least those doesn't
> hang which may be the case if the host/card isn't powered up first.
>

I got it, thanks.

> So, you must *not* leave the host/card in "mmc_power_off()" state in
> _mmc_resume().
>
> [...]
>
> Kind regards
> Uffe
>
>
>


-- 
Best Regards
Shawn Lin



From shawn.lin at rock-chips.com  Tue Jul 19 18:57:39 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Wed, 20 Jul 2016 09:57:39 +0800
Subject: [PATCH v2 1/2] mmc: core: move freqs table into core.h
Message-ID: <1468979860-28712-1-git-send-email-shawn.lin@rock-chips.com>

We will reuse it outside the core.c file, so let's
move it to the header file.

Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
---

Changes in v2: None

 drivers/mmc/core/core.c | 2 --
 drivers/mmc/core/core.h | 2 ++
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 94cbf4e..0cec7f4 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -58,8 +58,6 @@
  */
 #define MMC_BKOPS_MAX_TIMEOUT	(4 * 60 * 1000) /* max time to wait in ms */
 
-static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
-
 /*
  * Enabling software CRCs on the data blocks can be a significant (30%)
  * performance cost, and for other reasons may not always be desired.
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index 0fa86a2..5456000 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -54,6 +54,8 @@ void mmc_power_off(struct mmc_host *host);
 void mmc_power_cycle(struct mmc_host *host, u32 ocr);
 void mmc_set_initial_state(struct mmc_host *host);
 
+static const unsigned int freqs[] = { 400000, 300000, 200000, 100000 };
+
 static inline void mmc_delay(unsigned int ms)
 {
 	if (ms < 1000 / HZ) {
-- 
2.3.7




From shawn.lin at rock-chips.com  Tue Jul 19 18:57:40 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Wed, 20 Jul 2016 09:57:40 +0800
Subject: [PATCH v2 2/2] mmc: core: fall back host->f_init if failing to init
 mmc card after resume
In-Reply-To: <1468979860-28712-1-git-send-email-shawn.lin@rock-chips.com>
References: <1468979860-28712-1-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <1468979860-28712-2-git-send-email-shawn.lin@rock-chips.com>

We observed the failure of initializing card after resume
accidentally. It's hard to reproduce but we did get report from
the suspend/resume test of our RK3399 mp test farm . Unfortunately,
we still fail to figure out what was going wrong at that time.
Also we can't achieve it by retrying the host->f_init without falling
back it. But this patch will solve the problem as we could add some log
there and see that we resume the mmc card successfully after falling
back the host->f_init. There is no obvious side effect found, so it seems
this patch will improve the stability.

[   93.405085] mmc1: unexpected status 0x800900 after switch
[   93.408474] mmc1: switch to bus width 1 failed
[   93.408482] mmc1: mmc_select_hs200 failed, error -110
[   93.408492] mmc1: error -110 during resume (card was removed?)
[   93.408705] PM: resume of devices complete after 213.453 msecs

Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>

---

Changes in v2:
- remove mmc_power_off
- take f_min into consideration

 drivers/mmc/core/mmc.c | 19 +++++++++++++++++--
 1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 403b97b..a2891c1 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -1945,6 +1945,7 @@ static int mmc_suspend(struct mmc_host *host)
 static int _mmc_resume(struct mmc_host *host)
 {
 	int err = 0;
+	int i;
 
 	BUG_ON(!host);
 	BUG_ON(!host->card);
@@ -1954,8 +1955,22 @@ static int _mmc_resume(struct mmc_host *host)
 	if (!mmc_card_suspended(host->card))
 		goto out;
 
-	mmc_power_up(host, host->card->ocr);
-	err = mmc_init_card(host, host->card->ocr, host->card);
+	/*
+	 * Let's try to fallback the host->f_init
+	 * if failing to init mmc card after resume.
+	 */
+	for (i = 0; i < ARRAY_SIZE(freqs); i++) {
+		if (host->f_init < max(freqs[i], host->f_min))
+			continue;
+		else
+			host->f_init = max(freqs[i], host->f_min);
+
+		mmc_power_up(host, host->card->ocr);
+		err = mmc_init_card(host, host->card->ocr, host->card);
+		if (!err)
+			break;
+	}
+
 	mmc_card_clr_suspended(host->card);
 
 out:
-- 
2.3.7




From mark.yao at rock-chips.com  Tue Jul 19 20:18:50 2016
From: mark.yao at rock-chips.com (Mark Yao)
Date: Wed, 20 Jul 2016 11:18:50 +0800
Subject: [PATCH 2/2] dt-bindings: add simple-panel-dsi and simple-panel
In-Reply-To: <1468984730-23186-1-git-send-email-mark.yao@rock-chips.com>
References: <1468984730-23186-1-git-send-email-mark.yao@rock-chips.com>
Message-ID: <1468984730-23186-2-git-send-email-mark.yao@rock-chips.com>

Allow user add display timing on device tree with simple-panel-dsi
or simple-panel.

Cc: Thierry Reding <thierry.reding at gmail.com>
Cc: Rob Herring <robh+dt at kernel.org>
Cc: Mark Rutland <mark.rutland at arm.com>

Signed-off-by: Mark Yao <mark.yao at rock-chips.com>
---
 .../bindings/display/panel/simple-panel.txt        | 48 ++++++++++++++++++++++
 1 file changed, 48 insertions(+)

diff --git a/Documentation/devicetree/bindings/display/panel/simple-panel.txt b/Documentation/devicetree/bindings/display/panel/simple-panel.txt
index 1341bbf..bc66970 100644
--- a/Documentation/devicetree/bindings/display/panel/simple-panel.txt
+++ b/Documentation/devicetree/bindings/display/panel/simple-panel.txt
@@ -4,10 +4,22 @@ Required properties:
 - power-supply: regulator to provide the supply voltage
 
 Optional properties:
+- compatible: value maybe one of the following
+		"simple-panel";
+		"simple-panel-dsi";
+
 - ddc-i2c-bus: phandle of an I2C controller used for DDC EDID probing
 - enable-gpios: GPIO pin to enable or disable the panel
 - backlight: phandle of the backlight device attached to the panel
 
+Required properties when compatible is "simple-panel" or "simple-panel-dsi":
+- display-timings: see display-timing.txt for information
+
+Optional properties when compatible is a dsi devices:
+- dsi,flags: dsi operation mode related flags
+- dsi,format: pixel format for video mode
+- dsi,lanes: number of active data lanes
+
 Example:
 
 	panel: panel {
@@ -19,3 +31,39 @@ Example:
 
 		backlight = <&backlight>;
 	};
+
+Or:
+	panel: panel {
+		compatible = "simple-panel-dsi";
+		ddc-i2c-bus = <&panelddc>;
+
+		power-supply = <&vdd_pnl_reg>;
+		enable-gpios = <&gpio 90 0>;
+
+		backlight = <&backlight>;
+
+		dsi,flags = <MIPI_DSI_MODE_VIDEO |
+			     MIPI_DSI_MODE_VIDEO_BURST |
+			     MIPI_DSI_MODE_VIDEO_SYNC_PULSE>;
+		dsi,format = <MIPI_DSI_FMT_RGB888>;
+		dsi,lanes = <4>;
+
+		display-timings {
+			native-mode = <&timing0>;
+			timing0: timing0 {
+				clock-frequency = <160000000>;
+				hactive = <1200>;
+				vactive = <1920>;
+				hback-porch = <21>;
+				hfront-porch = <120>;
+				vback-porch = <18>;
+				vfront-porch = <21>;
+				hsync-len = <20>;
+				vsync-len = <3>;
+				hsync-active = <0>;
+				vsync-active = <0>;
+				de-active = <0>;
+				pixelclk-active = <0>;
+			};
+		};
+	};
-- 
1.9.1




From mark.yao at rock-chips.com  Tue Jul 19 20:18:49 2016
From: mark.yao at rock-chips.com (Mark Yao)
Date: Wed, 20 Jul 2016 11:18:49 +0800
Subject: [PATCH 1/2] drm/panel: add of display timing support
Message-ID: <1468984730-23186-1-git-send-email-mark.yao@rock-chips.com>

We want add display support on loader, share the same timing
with kernel side, but the timing is hide into kernel code,
can't be share, avoid config twice display timing, add device-tree
display timing support would be good idea.

With this patch, loader and kernel can share same timing from
device node.

Cc: Thierry Reding <thierry.reding at gmail.com>

Signed-off-by: Mark Yao <mark.yao at rock-chips.com>
---
 drivers/gpu/drm/panel/panel-simple.c | 73 ++++++++++++++++++++++++++++++++----
 1 file changed, 65 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
index 85143d1..04cd797 100644
--- a/drivers/gpu/drm/panel/panel-simple.c
+++ b/drivers/gpu/drm/panel/panel-simple.c
@@ -34,6 +34,7 @@
 #include <drm/drm_panel.h>
 
 #include <video/display_timing.h>
+#include <video/of_display_timing.h>
 #include <video/videomode.h>
 
 struct panel_desc {
@@ -80,6 +81,7 @@ struct panel_simple {
 	bool prepared;
 	bool enabled;
 
+	struct device *dev;
 	const struct panel_desc *desc;
 
 	struct backlight_device *backlight;
@@ -159,6 +161,32 @@ static int panel_simple_get_fixed_modes(struct panel_simple *panel)
 	return num;
 }
 
+static int panel_simple_of_get_native_mode(struct panel_simple *panel)
+{
+	struct drm_connector *connector = panel->base.connector;
+	struct drm_device *drm = panel->base.drm;
+	struct drm_display_mode *mode;
+	int ret;
+
+	mode = drm_mode_create(drm);
+	if (!mode)
+		return 0;
+
+	ret = of_get_drm_display_mode(panel->dev->of_node, mode,
+				      OF_USE_NATIVE_MODE);
+	if (ret) {
+		dev_dbg(panel->dev, "failed to find dts display timings\n");
+		drm_mode_destroy(drm, mode);
+		return 0;
+	}
+
+	drm_mode_set_name(mode);
+	mode->type |= DRM_MODE_TYPE_PREFERRED;
+	drm_mode_probed_add(connector, mode);
+
+	return 1;
+}
+
 static int panel_simple_disable(struct drm_panel *panel)
 {
 	struct panel_simple *p = to_panel_simple(panel);
@@ -172,7 +200,7 @@ static int panel_simple_disable(struct drm_panel *panel)
 		backlight_update_status(p->backlight);
 	}
 
-	if (p->desc->delay.disable)
+	if (p->desc && p->desc->delay.disable)
 		msleep(p->desc->delay.disable);
 
 	p->enabled = false;
@@ -192,7 +220,7 @@ static int panel_simple_unprepare(struct drm_panel *panel)
 
 	regulator_disable(p->supply);
 
-	if (p->desc->delay.unprepare)
+	if (p->desc && p->desc->delay.unprepare)
 		msleep(p->desc->delay.unprepare);
 
 	p->prepared = false;
@@ -217,7 +245,7 @@ static int panel_simple_prepare(struct drm_panel *panel)
 	if (p->enable_gpio)
 		gpiod_set_value_cansleep(p->enable_gpio, 1);
 
-	if (p->desc->delay.prepare)
+	if (p->desc && p->desc->delay.prepare)
 		msleep(p->desc->delay.prepare);
 
 	p->prepared = true;
@@ -232,7 +260,7 @@ static int panel_simple_enable(struct drm_panel *panel)
 	if (p->enabled)
 		return 0;
 
-	if (p->desc->delay.enable)
+	if (p->desc && p->desc->delay.enable)
 		msleep(p->desc->delay.enable);
 
 	if (p->backlight) {
@@ -264,6 +292,9 @@ static int panel_simple_get_modes(struct drm_panel *panel)
 	/* add hard-coded panel modes */
 	num += panel_simple_get_fixed_modes(p);
 
+	/* add device node plane modes */
+	num += panel_simple_of_get_native_mode(p);
+
 	return num;
 }
 
@@ -274,6 +305,9 @@ static int panel_simple_get_timings(struct drm_panel *panel,
 	struct panel_simple *p = to_panel_simple(panel);
 	unsigned int i;
 
+	if (!p->desc)
+		return 0;
+
 	if (p->desc->num_timings < num_timings)
 		num_timings = p->desc->num_timings;
 
@@ -306,6 +340,7 @@ static int panel_simple_probe(struct device *dev, const struct panel_desc *desc)
 	panel->enabled = false;
 	panel->prepared = false;
 	panel->desc = desc;
+	panel->dev = dev;
 
 	panel->supply = devm_regulator_get(dev, "power");
 	if (IS_ERR(panel->supply))
@@ -1515,6 +1550,9 @@ static const struct panel_desc urt_umsh_8596md_parallel = {
 
 static const struct of_device_id platform_of_match[] = {
 	{
+		.compatible = "simple-panel",
+		.data = NULL,
+	}, {
 		.compatible = "ampire,am800480r3tmqwa1h",
 		.data = &ampire_am800480r3tmqwa1h,
 	}, {
@@ -1860,6 +1898,9 @@ static const struct panel_desc_dsi panasonic_vvx10f004b00 = {
 
 static const struct of_device_id dsi_of_match[] = {
 	{
+		.compatible = "simple-panel-dsi",
+		.data = NULL
+	}, {
 		.compatible = "auo,b080uan01",
 		.data = &auo_b080uan01
 	}, {
@@ -1884,6 +1925,8 @@ static int panel_simple_dsi_probe(struct mipi_dsi_device *dsi)
 {
 	const struct panel_desc_dsi *desc;
 	const struct of_device_id *id;
+	const struct panel_desc *pdesc;
+	u32 val;
 	int err;
 
 	id = of_match_node(dsi_of_match, dsi->dev.of_node);
@@ -1892,13 +1935,27 @@ static int panel_simple_dsi_probe(struct mipi_dsi_device *dsi)
 
 	desc = id->data;
 
-	err = panel_simple_probe(&dsi->dev, &desc->desc);
+	if (desc) {
+		dsi->mode_flags = desc->flags;
+		dsi->format = desc->format;
+		dsi->lanes = desc->lanes;
+		pdesc = &desc->desc;
+	} else {
+		pdesc = NULL;
+	}
+
+	err = panel_simple_probe(&dsi->dev, pdesc);
 	if (err < 0)
 		return err;
 
-	dsi->mode_flags = desc->flags;
-	dsi->format = desc->format;
-	dsi->lanes = desc->lanes;
+	if (!of_property_read_u32(dsi->dev.of_node, "dsi,flags", &val))
+		dsi->mode_flags = val;
+
+	if (!of_property_read_u32(dsi->dev.of_node, "dsi,format", &val))
+		dsi->format = val;
+
+	if (!of_property_read_u32(dsi->dev.of_node, "dsi,lanes", &val))
+		dsi->lanes = val;
 
 	return mipi_dsi_attach(dsi);
 }
-- 
1.9.1




From heiko at sntech.de  Wed Jul 20 01:11:11 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Wed, 20 Jul 2016 10:11:11 +0200
Subject: [PATCH] arm64: dts: rockchip: add spiX aliases for rk3399
In-Reply-To: <1841177.NDa9D09EGz@diego>
References: <1468545873-134339-1-git-send-email-briannorris@chromium.org>
 <20160719192912.GA142821@google.com> <1841177.NDa9D09EGz@diego>
Message-ID: <3484113.eKHnDqRbfE@diego>

Am Mittwoch, 20. Juli 2016, 00:18:40 schrieb Heiko St?bner:
> Am Dienstag, 19. Juli 2016, 12:29:14 schrieb Brian Norris:
> > On Tue, Jul 19, 2016 at 12:27:54PM -0700, Brian Norris wrote:
> > > + Rob
> > 
> > + Rob for real
> > 
> > > Hi,
> > > 
> > > On Tue, Jul 19, 2016 at 08:56:47PM +0200, Heiko Stuebner wrote:
> > > > Am Dienstag, 19. Juli 2016, 08:39:55 schrieb Uwe Kleine-K?nig:
> > > > > On Thu, Jul 14, 2016 at 06:24:33PM -0700, Brian Norris wrote:
> > > > > > It's nicer to see this:
> > > > > >     # ls -l /sys/bus/spi/devices/
> > > > > >     total 0
> > > > > >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi0.0 ->
> > > > > >     ../../../devices/platform/ff1c0000.spi/spi_master/spi0/spi0.0
> > > > > >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi1.0 ->
> > > > > >     ../../../devices/platform/ff1d0000.spi/spi_master/spi1/spi1.0
> > > > > >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi2.0 ->
> > > > > >     ../../../devices/platform/ff1e0000.spi/spi_master/spi2/spi2.0
> > > > > >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi5.0 ->
> > > > > >     ../../../devices/platform/ff200000.spi/spi_master/spi5/spi5.0>
> > > > > > 
> > > > > > than this:
> > > > > >     # ls -l /sys/bus/spi/devices/
> > > > > >     total 0
> > > > > >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi32763.0 ->
> > > > > >     ../../../devices/platform/ff200000.spi/spi_master/spi32763/spi
> > > > > >     32
> > > > > >     763.0
> > > > > >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi32764.0 ->
> > > > > >     ../../../devices/platform/ff1e0000.spi/spi_master/spi32764/spi
> > > > > >     32
> > > > > >     764.0
> > > > > >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi32765.0 ->
> > > > > >     ../../../devices/platform/ff1d0000.spi/spi_master/spi32765/spi
> > > > > >     32
> > > > > >     765.0
> > > > > >     lrwxrwxrwx 1 root root 0 Dec 31  1969 spi32766.0 ->
> > > > > >     ../../../devices/platform/ff1c0000.spi/spi_master/spi32766/spi
> > > > > >     32
> > > > > >     766.0>
> > > > > > 
> > > > > > With the latter, it's much clearer which bus devices are on.
> > > > > > 
> > > > > > Signed-off-by: Brian Norris <briannorris at chromium.org>
> > > > > > ---
> > > > > > 
> > > > > >  arch/arm64/boot/dts/rockchip/rk3399.dtsi | 6 ++++++
> > > > > >  1 file changed, 6 insertions(+)
> > > > > > 
> > > > > > diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> > > > > > b/arch/arm64/boot/dts/rockchip/rk3399.dtsi index
> > > > > > 4c84229789ef..b04abbfd4898 100644
> > > > > > --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> > > > > > +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> > > > > > @@ -70,6 +70,12 @@
> > > > > > 
> > > > > >  		serial2 = &uart2;
> > > > > >  		serial3 = &uart3;
> > > > > >  		serial4 = &uart4;
> > > > > > 
> > > > > > +		spi0 = &spi0;
> > > > > > +		spi1 = &spi1;
> > > > > > +		spi2 = &spi2;
> > > > > > +		spi3 = &spi3;
> > > > > > +		spi4 = &spi4;
> > > > > > +		spi5 = &spi5;
> > > > > 
> > > > > Note that Rob Herring (with his dt-maintainer hat on) doesn't like
> > > > > these
> > > > > aliases.
> > > > > 
> > > > > See for example:
> > > > > 	http://mid.gmane.org/20160705140546.GA10601 at rob-hp-laptop
> > > 
> > > But why? I believe half the arguments in the linked thread were
> > > Catch-22's -- there were no "mainline" users (which is a false target
> > > IMO anyway, as Christer Weinigel mentioned somewhere in the thread Rob
> > > linked), and so we couldn't accept more documentation (or users) for the
> > > feature. FWIW, my quick grep now shows there are currently 43 mainline
> > > users.
> > > 
> > > This feature is very useful. Some of the thread Rob pointed to argued
> > > that the indexing isn't HW documentation; in this case, it most
> > > certainly is. The RK3399 TRM explicitly has these SPI buses named SPI0,
> > > SPI1, SPI2, SPI3, SPI4, and SPI5, and schematics that I've seen use the
> > > same terminology. It is therefore *much* nicer to have my device show up
> > > as 'spi2.0' (reflecting their correct HW name) rather than spi32764.0.
> > > Sometimes I can't even get as far as mounting sysfs to check what this
> > > maps to, but having spi2.0 in the kernel log can clearly tell me what
> > > device is causing problems.
> > > 
> > > If Rob can support a better alternative solution, I'd be happy to
> > > switch. But I don't understand why we can't use a useful (not just to
> > > me, but presumably to at least 43 other independent users, and many
> > > more out of tree), existing, and long-supported feature here.
> 
> Personally, I don't have any real opinion on this but the argument sounds
> plausible to me (aka they are named with numbers in every manual,
> schematics) so it might be helpful to have that around - similar to i2c
> that is in the dtsi already.
> 
> I think I remember Doug having a similar discussion around mmc-aliases some
> time ago - that met opposition as well [0] - although I guess the numbering
> was a bit more arbitary there.
> 
> I guess we'll just see what Rob says.

Uwe pointed me to a very similar discussion and response from Rob about spi 
aliases at:
https://lkml.org/lkml/2016/5/25/566



From heiko at sntech.de  Wed Jul 20 04:57:19 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Wed, 20 Jul 2016 13:57:19 +0200
Subject: [PATCH v8 2/3] phy: rockchip-inno-usb2: add a new driver for
 Rockchip usb2phy
In-Reply-To: <1468913260-23938-1-git-send-email-frank.wang@rock-chips.com>
References: <1468913260-23938-1-git-send-email-frank.wang@rock-chips.com>
Message-ID: <3903669.hKdzDuZSeG@diego>

Am Dienstag, 19. Juli 2016, 15:27:40 schrieb Frank Wang:
> The newer SoCs (rk3366, rk3399) take a different usb-phy IP block
> than rk3288 and before, and most of phy-related registers are also
> different from the past, so a new phy driver is required necessarily.
> 
> Signed-off-by: Frank Wang <frank.wang at rock-chips.com>
> Suggested-by: Heiko Stuebner <heiko at sntech.de>
> Suggested-by: Guenter Roeck <linux at roeck-us.net>
> Suggested-by: Doug Anderson <dianders at chromium.org>

Reviewed-by: Heiko Stuebner <heiko at sntech.de>


From andy.yan at rock-chips.com  Wed Jul 20 06:06:58 2016
From: andy.yan at rock-chips.com (Andy Yan)
Date: Wed, 20 Jul 2016 21:06:58 +0800
Subject: [PATCH] mfd: RK808: Fetch PMIC variant from chip id register
In-Reply-To: <1468496557-26309-1-git-send-email-w.egorov@phytec.de>
References: <1468496557-26309-1-git-send-email-w.egorov@phytec.de>
Message-ID: <c9d91f26-f8d2-7450-eb2d-4750742c35af@rock-chips.com>

Wadim:

   Hi.

   I have tested this patch both on RK818/RK808 based boards, it works well.

  So Test-by: Andy Yan <andy.yan at rock-chips.com>


On 2016?07?14? 19:42, Wadim Egorov wrote:
> Add and use the chip id registers to determine the PMIC variant.
>
> Signed-off-by: Wadim Egorov <w.egorov at phytec.de>
> ---
>
> This patch is based on top of
>
> [v6,1/5] mfd: RK808: Add RK818 support
> (https://patchwork.kernel.org/patch/9172223/)
>
> ---
>   drivers/mfd/rk808.c       | 17 +++++++++--------
>   include/linux/mfd/rk808.h |  6 ++++--
>   2 files changed, 13 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c
> index 66f7b08..fbec1d8 100644
> --- a/drivers/mfd/rk808.c
> +++ b/drivers/mfd/rk808.c
> @@ -291,8 +291,8 @@ static void rk808_device_shutdown(void)
>   }
>   
>   static const struct of_device_id rk808_of_match[] = {
> -	{ .compatible = "rockchip,rk808", .data = (void *) RK808_ID },
> -	{ .compatible = "rockchip,rk818", .data = (void *) RK818_ID },
> +	{ .compatible = "rockchip,rk808" },
> +	{ .compatible = "rockchip,rk818" },
>   	{ },
>   };
>   MODULE_DEVICE_TABLE(of, rk808_of_match);
> @@ -302,7 +302,6 @@ static int rk808_probe(struct i2c_client *client,
>   {
>   	struct device_node *np = client->dev.of_node;
>   	struct rk808 *rk808;
> -	const struct of_device_id *match;
>   	const struct rk808_reg_data *pre_init_reg;
>   	const struct mfd_cell *cells;
>   	int nr_pre_init_regs;
> @@ -315,12 +314,14 @@ static int rk808_probe(struct i2c_client *client,
>   	if (!rk808)
>   		return -ENOMEM;
>   
> -	match = of_match_device(rk808_of_match, &client->dev);
> -	if (!match) {
> -		dev_err(&client->dev, "Unable to match OF ID\n");
> -		return -ENODEV;
> +	rk808->variant = i2c_smbus_read_word_data(client, RK808_ID_MSB);
> +	if (rk808->variant < 0) {
> +		dev_err(&client->dev, "Failed to read the chip id at 0x%02x\n",
> +			RK808_ID_MSB);
> +		return rk808->variant;
>   	}
> -	rk808->variant = (long)match->data;
> +
> +	dev_dbg(&client->dev, "Chip id: 0x%x\n", (unsigned int)rk808->variant);
>   
>   	switch (rk808->variant) {
>   	case RK808_ID:
> diff --git a/include/linux/mfd/rk808.h b/include/linux/mfd/rk808.h
> index e5ae21d..fc5db6f 100644
> --- a/include/linux/mfd/rk808.h
> +++ b/include/linux/mfd/rk808.h
> @@ -69,6 +69,8 @@ enum rk808_reg {
>   #define RK808_RTC_INT_REG	0x12
>   #define RK808_RTC_COMP_LSB_REG	0x13
>   #define RK808_RTC_COMP_MSB_REG	0x14
> +#define RK808_ID_MSB		0x17
> +#define RK808_ID_LSB		0x18
>   #define RK808_CLK32OUT_REG	0x20
>   #define RK808_VB_MON_REG	0x21
>   #define RK808_THERMAL_REG	0x22
> @@ -318,8 +320,8 @@ enum {
>   };
>   
>   enum {
> -	RK808_ID,
> -	RK818_ID,
> +	RK808_ID = 0x0000,
> +	RK818_ID = 0x8181,
>   };
>   
>   struct rk808 {




From broonie at kernel.org  Wed Jul 20 09:43:40 2016
From: broonie at kernel.org (Mark Brown)
Date: Wed, 20 Jul 2016 17:43:40 +0100
Subject: Applied "ASoC: rockchip: correct the spdif clk" to the asoc tree
In-Reply-To: <1468852474-15735-1-git-send-email-zyw@rock-chips.com>
Message-ID: <E1bPuay-0002iL-Ap@debutante>

The patch

   ASoC: rockchip: correct the spdif clk

has been applied to the asoc tree at

   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git 

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.  

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

>From 46dd2e28a90e48fbf1b7e253933fa3b7242e9b1b Mon Sep 17 00:00:00 2001
From: Chris Zhong <zyw at rock-chips.com>
Date: Mon, 18 Jul 2016 22:34:34 +0800
Subject: [PATCH] ASoC: rockchip: correct the spdif clk

The spdif mclk should be 128 times of sample rate, and there is a
internal divider, the real rate of spdif mclk is mclk / (div + 1).
Hence, the original driver always get the good frequency for
48000/96000/44100/192000. But for 32000, the mclk is incorrect,
it should be 32000*128, but get 48000*128. Do not use the internal
divider here, just set all mclk to 128 * sample rate directly.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
Signed-off-by: Mark Brown <broonie at kernel.org>
---
 sound/soc/rockchip/rockchip_spdif.c | 17 +----------------
 1 file changed, 1 insertion(+), 16 deletions(-)

diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c
index 100781e37848..4ca265737eda 100644
--- a/sound/soc/rockchip/rockchip_spdif.c
+++ b/sound/soc/rockchip/rockchip_spdif.c
@@ -101,21 +101,7 @@ static int rk_spdif_hw_params(struct snd_pcm_substream *substream,
 	int ret;
 
 	srate = params_rate(params);
-	switch (srate) {
-	case 32000:
-	case 48000:
-	case 96000:
-		mclk = 96000 * 128; /* 12288000 hz */
-		break;
-	case 44100:
-		mclk = 44100 * 256; /* 11289600 hz */
-		break;
-	case 192000:
-		mclk = 192000 * 128; /* 24576000 hz */
-		break;
-	default:
-		return -EINVAL;
-	}
+	mclk = srate * 128;
 
 	switch (params_format(params)) {
 	case SNDRV_PCM_FORMAT_S16_LE:
@@ -139,7 +125,6 @@ static int rk_spdif_hw_params(struct snd_pcm_substream *substream,
 		return ret;
 	}
 
-	val |= SPDIF_CFGR_CLK_DIV(mclk/(srate * 256));
 	ret = regmap_update_bits(spdif->regmap, SPDIF_CFGR,
 		SPDIF_CFGR_CLK_DIV_MASK | SPDIF_CFGR_HALFWORD_ENABLE |
 		SDPIF_CFGR_VDW_MASK,
-- 
2.8.1



From broonie at kernel.org  Wed Jul 20 09:44:04 2016
From: broonie at kernel.org (Mark Brown)
Date: Wed, 20 Jul 2016 17:44:04 +0100
Subject: Applied "spi: rockchip: limit transfers to (64K - 1) bytes" to the
 spi tree
In-Reply-To: <1468546259-136659-1-git-send-email-briannorris@chromium.org>
Message-ID: <E1bPubM-0002u3-AQ@debutante>

The patch

   spi: rockchip: limit transfers to (64K - 1) bytes

has been applied to the spi tree at

   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git 

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.  

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

>From 5185a81c02d4118b11e6cb7b5fbf6f15ff7aff90 Mon Sep 17 00:00:00 2001
From: Brian Norris <briannorris at chromium.org>
Date: Thu, 14 Jul 2016 18:30:59 -0700
Subject: [PATCH] spi: rockchip: limit transfers to (64K - 1) bytes

The Rockchip SPI controller's length register only supports 16-bits,
yielding a maximum length of 64KiB (the CTRLR1 register holds "length -
1"). Trying to transfer more than that (e.g., with a large SPI flash
read) will cause the driver to hang.

Now, it seems that while theoretically we should be able to program
CTRLR1 with 0xffff, and get a 64KiB transfer, but that also seems to
cause the core to choke, so stick with a maximum of 64K - 1 bytes --
i.e., 0xffff.

Signed-off-by: Brian Norris <briannorris at chromium.org>
Signed-off-by: Mark Brown <broonie at kernel.org>
---
 drivers/spi/spi-rockchip.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c
index cd89682065b9..cf69f4dfb8c7 100644
--- a/drivers/spi/spi-rockchip.c
+++ b/drivers/spi/spi-rockchip.c
@@ -142,6 +142,12 @@
 /* sclk_out: spi master internal logic in rk3x can support 50Mhz */
 #define MAX_SCLK_OUT		50000000
 
+/*
+ * SPI_CTRLR1 is 16-bits, so we should support lengths of 0xffff + 1. However,
+ * the controller seems to hang when given 0x10000, so stick with this for now.
+ */
+#define ROCKCHIP_SPI_MAX_TRANLEN		0xffff
+
 enum rockchip_ssi_type {
 	SSI_MOTO_SPI = 0,
 	SSI_TI_SSP,
@@ -573,6 +579,11 @@ static void rockchip_spi_config(struct rockchip_spi *rs)
 	dev_dbg(rs->dev, "cr0 0x%x, div %d\n", cr0, div);
 }
 
+static size_t rockchip_spi_max_transfer_size(struct spi_device *spi)
+{
+	return ROCKCHIP_SPI_MAX_TRANLEN;
+}
+
 static int rockchip_spi_transfer_one(
 		struct spi_master *master,
 		struct spi_device *spi,
@@ -589,6 +600,11 @@ static int rockchip_spi_transfer_one(
 		return -EINVAL;
 	}
 
+	if (xfer->len > ROCKCHIP_SPI_MAX_TRANLEN) {
+		dev_err(rs->dev, "Transfer is too long (%d)\n", xfer->len);
+		return -EINVAL;
+	}
+
 	rs->speed = xfer->speed_hz;
 	rs->bpw = xfer->bits_per_word;
 	rs->n_bytes = rs->bpw >> 3;
@@ -728,6 +744,7 @@ static int rockchip_spi_probe(struct platform_device *pdev)
 	master->prepare_message = rockchip_spi_prepare_message;
 	master->unprepare_message = rockchip_spi_unprepare_message;
 	master->transfer_one = rockchip_spi_transfer_one;
+	master->max_transfer_size = rockchip_spi_max_transfer_size;
 	master->handle_err = rockchip_spi_handle_err;
 
 	rs->dma_tx.ch = dma_request_chan(rs->dev, "tx");
-- 
2.8.1



From briannorris at chromium.org  Wed Jul 20 10:01:52 2016
From: briannorris at chromium.org (Brian Norris)
Date: Wed, 20 Jul 2016 10:01:52 -0700
Subject: Applied "spi: rockchip: limit transfers to (64K - 1) bytes" to
 the spi tree
In-Reply-To: <E1bPubM-0002u3-AQ@debutante>
References: <1468546259-136659-1-git-send-email-briannorris@chromium.org>
 <E1bPubM-0002u3-AQ@debutante>
Message-ID: <20160720170151.GA14956@google.com>

Hi Mark,

On Wed, Jul 20, 2016 at 05:44:04PM +0100, Mark Brown wrote:
> The patch
> 
>    spi: rockchip: limit transfers to (64K - 1) bytes
> 
> has been applied to the spi tree at
> 
>    git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git 
> 
> All being well this means that it will be integrated into the linux-next
> tree (usually sometime in the next 24 hours) and sent to Linus during
> the next merge window (or sooner if it is a bug fix), however if
> problems are discovered then the patch may be dropped or reverted.  
> 
> You may get further e-mails resulting from automated or manual testing
> and review of the tree, please engage with people reporting problems and
> send followup patches addressing any issues that are reported if needed.
> 
> If any updates are required or you are submitting further changes they
> should be sent as incremental updates against current git, existing
> patches will not be replaced.
> 
> Please add any relevant lists and maintainers to the CCs when replying
> to this mail.
> 
> Thanks,
> Mark
> 
> From 5185a81c02d4118b11e6cb7b5fbf6f15ff7aff90 Mon Sep 17 00:00:00 2001
> From: Brian Norris <briannorris at chromium.org>
> Date: Thu, 14 Jul 2016 18:30:59 -0700
> Subject: [PATCH] spi: rockchip: limit transfers to (64K - 1) bytes
> 
> The Rockchip SPI controller's length register only supports 16-bits,
> yielding a maximum length of 64KiB (the CTRLR1 register holds "length -
> 1"). Trying to transfer more than that (e.g., with a large SPI flash
> read) will cause the driver to hang.
> 
> Now, it seems that while theoretically we should be able to program
> CTRLR1 with 0xffff, and get a 64KiB transfer, but that also seems to
> cause the core to choke, so stick with a maximum of 64K - 1 bytes --
> i.e., 0xffff.
> 
> Signed-off-by: Brian Norris <briannorris at chromium.org>
> Signed-off-by: Mark Brown <broonie at kernel.org>

Thanks for applying! I'm OK with that, but just to be clear this might
qualify as a (very slight) hack, since I believe we should be able to
support a full 64KiB on both PIO and DMA. But it is better than the
current state of things on this driver, IMO. I suppose it's up to
Rockchip (or me) to figure out why some PIO transfers fail at exactly
64K if we want to support that. They have reproduced the failures I've
seen.

Brian


From dianders at chromium.org  Wed Jul 20 14:33:25 2016
From: dianders at chromium.org (Doug Anderson)
Date: Wed, 20 Jul 2016 14:33:25 -0700
Subject: [PATCH v8 3/3] arm64: dts: rockchip: add usb2-phy support for
 rk3399
In-Reply-To: <1468913311-23977-1-git-send-email-frank.wang@rock-chips.com>
References: <1468913311-23977-1-git-send-email-frank.wang@rock-chips.com>
Message-ID: <CAD=FV=Wc0RdgpfzWhEVsrQhj5Pjf4RF3a81mZKaZfpZS41ce5g@mail.gmail.com>

Hi,

On Tue, Jul 19, 2016 at 12:28 AM, Frank Wang <frank.wang at rock-chips.com> wrote:

You need a patch description here, even for simple patches.  All you
have now is a subject.


> Signed-off-by: Frank Wang <frank.wang at rock-chips.com>
> ---
>  arch/arm64/boot/dts/rockchip/rk3399-evb.dts |   19 +++++++++++
>  arch/arm64/boot/dts/rockchip/rk3399.dtsi    |   47 ++++++++++++++++++++++++++-

Personally I'd prefer to see EVB in a separate patch.


>  2 files changed, 65 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm64/boot/dts/rockchip/rk3399-evb.dts b/arch/arm64/boot/dts/rockchip/rk3399-evb.dts
> index 1a3eb14..31d4828 100644
> --- a/arch/arm64/boot/dts/rockchip/rk3399-evb.dts
> +++ b/arch/arm64/boot/dts/rockchip/rk3399-evb.dts
> @@ -69,6 +69,15 @@
>                 regulator-max-microvolt = <3300000>;
>         };
>
> +       vbus_host: vbus-host-regulator {
> +               compatible = "regulator-fixed";
> +               enable-active-high;
> +               gpio = <&gpio4 25 GPIO_ACTIVE_HIGH>;
> +               pinctrl-names = "default";
> +               pinctrl-0 = <&host_vbus_drv>;
> +               regulator-name = "vbus_host";
> +       };
> +

To match my schematics, this would probably be "vcc5v0_host".
Technically there are two regulators but since they are the same
voltage and enabled by the same GPIO it seems like modeling it as one
regulator is fine.

If you really wanted to model things you could also include the input
supply (VCC5V0_SYS).  Not sure how much you care to model in EVB.


>         vcc_phy: vcc-phy-regulator {
>                 compatible = "regulator-fixed";
>                 regulator-name = "vcc_phy";
> @@ -93,6 +102,16 @@
>         status = "okay";
>  };
>
> +&u2phy0_host {
> +       phy-supply = <&vbus_host>;
> +       status = "okay";
> +};
> +
> +&u2phy1_host {
> +       phy-supply = <&vbus_host>;
> +       status = "okay";
> +};
> +

Technically "u2" sorts alphabetically before "uart".


>  &usb_host0_ehci {
>         status = "okay";
>  };
> diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> index d7f8e06..0383785 100644
> --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> @@ -221,6 +221,8 @@
>                 interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
>                 clocks = <&cru HCLK_HOST0>, <&cru HCLK_HOST0_ARB>;
>                 clock-names = "hclk_host0", "hclk_host0_arb";
> +               phys = <&u2phy0_host>;
> +               phy-names = "u2phy0";

This is wrong.  From
"Documentation/devicetree/bindings/usb/usb-ehci.txt" phy-names should
be "usb".

>                 status = "disabled";
>         };
>
> @@ -239,6 +241,8 @@
>                 interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
>                 clocks = <&cru HCLK_HOST1>, <&cru HCLK_HOST1_ARB>;
>                 clock-names = "hclk_host1", "hclk_host1_arb";
> +               phys = <&u2phy1_host>;
> +               phy-names = "u2phy1";

This is wrong.  From
"Documentation/devicetree/bindings/usb/usb-ehci.txt" phy-names should
be "usb".

>                 status = "disabled";
>         };
>
> @@ -481,8 +485,42 @@
>         };
>
>         grf: syscon at ff770000 {
> -               compatible = "rockchip,rk3399-grf", "syscon";
> +               compatible = "rockchip,rk3399-grf", "syscon", "simple-mfd";
>                 reg = <0x0 0xff770000 0x0 0x10000>;
> +               #address-cells = <1>;
> +               #size-cells = <1>;
> +
> +               u2phy0: usb2-phy at e450 {
> +                       compatible = "rockchip,rk3399-usb2phy";
> +                       reg = <0xe450 0x10>;
> +                       clocks = <&cru SCLK_USB2PHY0_REF>;
> +                       clock-names = "phyclk";
> +                       #clock-cells = <0>;
> +                       clock-output-names = "clk_usbphy0_480m";

Any reason why there isn't a 'status = "disabled";' here?


> +                       u2phy0_host: host-port {
> +                               #phy-cells = <0>;
> +                               interrupts = <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>;
> +                               interrupt-names = "linestate";
> +                               status = "disabled";
> +                       };
> +               };
> +
> +               u2phy1: usb2-phy at e460 {
> +                       compatible = "rockchip,rk3399-usb2phy";
> +                       reg = <0xe460 0x10>;
> +                       clocks = <&cru SCLK_USB2PHY1_REF>;
> +                       clock-names = "phyclk";
> +                       #clock-cells = <0>;
> +                       clock-output-names = "clk_usbphy1_480m";
> +
> +                       u2phy1_host: host-port {
> +                               #phy-cells = <0>;
> +                               interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
> +                               interrupt-names = "linestate";
> +                               status = "disabled";
> +                       };
> +               };
>         };
>
>         watchdog at ff840000 {
> @@ -1009,5 +1047,12 @@
>                                         <1 14 RK_FUNC_1 &pcfg_pull_none>;
>                         };
>                 };
> +
> +               usb2 {
> +                       host_vbus_drv: host-vbus-drv {
> +                               rockchip,pins =
> +                                       <4 25 RK_FUNC_GPIO &pcfg_pull_none>;
> +                       };
> +               };

Are you certain this belongs in rk3399.dtsi?  It seems like it should
be in the EVB file.


From heiko at sntech.de  Wed Jul 20 15:06:36 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Thu, 21 Jul 2016 00:06:36 +0200
Subject: [PATCH v8 3/3] arm64: dts: rockchip: add usb2-phy support for
 rk3399
In-Reply-To: <CAD=FV=Wc0RdgpfzWhEVsrQhj5Pjf4RF3a81mZKaZfpZS41ce5g@mail.gmail.com>
References: <1468913311-23977-1-git-send-email-frank.wang@rock-chips.com>
 <CAD=FV=Wc0RdgpfzWhEVsrQhj5Pjf4RF3a81mZKaZfpZS41ce5g@mail.gmail.com>
Message-ID: <19337009.ZCcJ3zVfzs@diego>

Am Mittwoch, 20. Juli 2016, 14:33:25 schrieb Doug Anderson:
> > +               #address-cells = <1>;
> > +               #size-cells = <1>;
> > +
> > +               u2phy0: usb2-phy at e450 {
> > +                       compatible = "rockchip,rk3399-usb2phy";
> > +                       reg = <0xe450 0x10>;
> > +                       clocks = <&cru SCLK_USB2PHY0_REF>;
> > +                       clock-names = "phyclk";
> > +                       #clock-cells = <0>;
> > +                       clock-output-names = "clk_usbphy0_480m";
> 
> Any reason why there isn't a 'status = "disabled";' here?

my guess would be, because we might need the provided clock anyway, even in 
the case where the actual phy is not used.

But that is better decided by the board/board-dts designer if the clock is 
really needed, so add the disabled here and enable in the board, as Doug 
suggested.


> > +                       u2phy0_host: host-port {
> > +                               #phy-cells = <0>;
> > +                               interrupts = <GIC_SPI 27
> > IRQ_TYPE_LEVEL_HIGH>; +                               interrupt-names =
> > "linestate";
> > +                               status = "disabled";
> > +                       };
> > +               };
> > +
> > +               u2phy1: usb2-phy at e460 {
> > +                       compatible = "rockchip,rk3399-usb2phy";
> > +                       reg = <0xe460 0x10>;
> > +                       clocks = <&cru SCLK_USB2PHY1_REF>;
> > +                       clock-names = "phyclk";
> > +                       #clock-cells = <0>;
> > +                       clock-output-names = "clk_usbphy1_480m";
> > +
> > +                       u2phy1_host: host-port {
> > +                               #phy-cells = <0>;
> > +                               interrupts = <GIC_SPI 31
> > IRQ_TYPE_LEVEL_HIGH>; +                               interrupt-names =
> > "linestate";
> > +                               status = "disabled";
> > +                       };
> > +               };
> > 
> >         };
> >         
> >         watchdog at ff840000 {
> > 
> > @@ -1009,5 +1047,12 @@
> > 
> >                                         <1 14 RK_FUNC_1 &pcfg_pull_none>;
> >                         
> >                         };
> >                 
> >                 };
> > 
> > +
> > +               usb2 {
> > +                       host_vbus_drv: host-vbus-drv {
> > +                               rockchip,pins =
> > +                                       <4 25 RK_FUNC_GPIO
> > &pcfg_pull_none>; +                       };
> > +               };
> 
> Are you certain this belongs in rk3399.dtsi?  It seems like it should
> be in the EVB file.

yep, while most boards follow a reference design, this is still highly board-
specific.


From frank.wang at rock-chips.com  Wed Jul 20 19:49:53 2016
From: frank.wang at rock-chips.com (Frank Wang)
Date: Thu, 21 Jul 2016 10:49:53 +0800
Subject: [PATCH v8 3/3] arm64: dts: rockchip: add usb2-phy support for
 rk3399
In-Reply-To: <CAD=FV=Wc0RdgpfzWhEVsrQhj5Pjf4RF3a81mZKaZfpZS41ce5g@mail.gmail.com>
References: <1468913311-23977-1-git-send-email-frank.wang@rock-chips.com>
 <CAD=FV=Wc0RdgpfzWhEVsrQhj5Pjf4RF3a81mZKaZfpZS41ce5g@mail.gmail.com>
Message-ID: <d01e6b5c-8e7a-bcba-477b-cfe3a9088964@rock-chips.com>

Hi Doug,

On 2016/7/21 5:33, Doug Anderson wrote:
> Hi,
>
> On Tue, Jul 19, 2016 at 12:28 AM, Frank Wang <frank.wang at rock-chips.com> wrote:
>
> You need a patch description here, even for simple patches.  All you
> have now is a subject.
>

OK, I will describe it next version.

>> Signed-off-by: Frank Wang <frank.wang at rock-chips.com>
>> ---
>>   arch/arm64/boot/dts/rockchip/rk3399-evb.dts |   19 +++++++++++
>>   arch/arm64/boot/dts/rockchip/rk3399.dtsi    |   47 ++++++++++++++++++++++++++-
> Personally I'd prefer to see EVB in a separate patch.
>

Yep, I would like to separate them :-) .

>>   2 files changed, 65 insertions(+), 1 deletion(-)
>>
>> diff --git a/arch/arm64/boot/dts/rockchip/rk3399-evb.dts b/arch/arm64/boot/dts/rockchip/rk3399-evb.dts
>> index 1a3eb14..31d4828 100644
>> --- a/arch/arm64/boot/dts/rockchip/rk3399-evb.dts
>> +++ b/arch/arm64/boot/dts/rockchip/rk3399-evb.dts
>> @@ -69,6 +69,15 @@
>>                  regulator-max-microvolt = <3300000>;
>>          };
>>
>> +       vbus_host: vbus-host-regulator {
>> +               compatible = "regulator-fixed";
>> +               enable-active-high;
>> +               gpio = <&gpio4 25 GPIO_ACTIVE_HIGH>;
>> +               pinctrl-names = "default";
>> +               pinctrl-0 = <&host_vbus_drv>;
>> +               regulator-name = "vbus_host";
>> +       };
>> +
> To match my schematics, this would probably be "vcc5v0_host".
> Technically there are two regulators but since they are the same
> voltage and enabled by the same GPIO it seems like modeling it as one
> regulator is fine.

Yep, you are right, I will rename it.

> If you really wanted to model things you could also include the input
> supply (VCC5V0_SYS).  Not sure how much you care to model in EVB.
>

Actually, from 
"Documentation/devicetree/bindings/regulator/fixed-regulator.txt" show, 
input supply name is just optional property, and it seems that only do 
assign "vin" value for input_supply (the second member of struct 
fixed_voltage_config) if "vin-supply" is specified.

So is input supply name  (VCC5V0_SYS) required here? Would you like to 
give more comments please?

>>          vcc_phy: vcc-phy-regulator {
>>                  compatible = "regulator-fixed";
>>                  regulator-name = "vcc_phy";
>> @@ -93,6 +102,16 @@
>>          status = "okay";
>>   };
>>
>> +&u2phy0_host {
>> +       phy-supply = <&vbus_host>;
>> +       status = "okay";
>> +};
>> +
>> +&u2phy1_host {
>> +       phy-supply = <&vbus_host>;
>> +       status = "okay";
>> +};
>> +
> Technically "u2" sorts alphabetically before "uart".
>

Well, It will be sorted next version.

>>   &usb_host0_ehci {
>>          status = "okay";
>>   };
>> diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
>> index d7f8e06..0383785 100644
>> --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
>> +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
>> @@ -221,6 +221,8 @@
>>                  interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
>>                  clocks = <&cru HCLK_HOST0>, <&cru HCLK_HOST0_ARB>;
>>                  clock-names = "hclk_host0", "hclk_host0_arb";
>> +               phys = <&u2phy0_host>;
>> +               phy-names = "u2phy0";
> This is wrong.  From
> "Documentation/devicetree/bindings/usb/usb-ehci.txt" phy-names should
> be "usb".

done.

>>                  status = "disabled";
>>          };
>>
>> @@ -239,6 +241,8 @@
>>                  interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
>>                  clocks = <&cru HCLK_HOST1>, <&cru HCLK_HOST1_ARB>;
>>                  clock-names = "hclk_host1", "hclk_host1_arb";
>> +               phys = <&u2phy1_host>;
>> +               phy-names = "u2phy1";
> This is wrong.  From
> "Documentation/devicetree/bindings/usb/usb-ehci.txt" phy-names should
> be "usb".

done

>>                  status = "disabled";
>>          };
>>
>> @@ -481,8 +485,42 @@
>>          };
>>
>>          grf: syscon at ff770000 {
>> -               compatible = "rockchip,rk3399-grf", "syscon";
>> +               compatible = "rockchip,rk3399-grf", "syscon", "simple-mfd";
>>                  reg = <0x0 0xff770000 0x0 0x10000>;
>> +               #address-cells = <1>;
>> +               #size-cells = <1>;
>> +
>> +               u2phy0: usb2-phy at e450 {
>> +                       compatible = "rockchip,rk3399-usb2phy";
>> +                       reg = <0xe450 0x10>;
>> +                       clocks = <&cru SCLK_USB2PHY0_REF>;
>> +                       clock-names = "phyclk";
>> +                       #clock-cells = <0>;
>> +                       clock-output-names = "clk_usbphy0_480m";
> Any reason why there isn't a 'status = "disabled";' here?
>

Refer to some explains from Heiko in another mail which sent at 6:07 AM 
on 21th July,  anyway, I will add ' status = "disabled" ' property next 
version.

>> +                       u2phy0_host: host-port {
>> +                               #phy-cells = <0>;
>> +                               interrupts = <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>;
>> +                               interrupt-names = "linestate";
>> +                               status = "disabled";
>> +                       };
>> +               };
>> +
>> +               u2phy1: usb2-phy at e460 {
>> +                       compatible = "rockchip,rk3399-usb2phy";
>> +                       reg = <0xe460 0x10>;
>> +                       clocks = <&cru SCLK_USB2PHY1_REF>;
>> +                       clock-names = "phyclk";
>> +                       #clock-cells = <0>;
>> +                       clock-output-names = "clk_usbphy1_480m";
>> +
>> +                       u2phy1_host: host-port {
>> +                               #phy-cells = <0>;
>> +                               interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
>> +                               interrupt-names = "linestate";
>> +                               status = "disabled";
>> +                       };
>> +               };
>>          };
>>
>>          watchdog at ff840000 {
>> @@ -1009,5 +1047,12 @@
>>                                          <1 14 RK_FUNC_1 &pcfg_pull_none>;
>>                          };
>>                  };
>> +
>> +               usb2 {
>> +                       host_vbus_drv: host-vbus-drv {
>> +                               rockchip,pins =
>> +                                       <4 25 RK_FUNC_GPIO &pcfg_pull_none>;
>> +                       };
>> +               };
> Are you certain this belongs in rk3399.dtsi?  It seems like it should
> be in the EVB file.
>

All right, It will be moved to EVB file next version.

BR.
Frank




From heiko at sntech.de  Thu Jul 21 02:26:49 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Thu, 21 Jul 2016 11:26:49 +0200
Subject: [PATCH v8 3/3] arm64: dts: rockchip: add usb2-phy support for
 rk3399
In-Reply-To: <d01e6b5c-8e7a-bcba-477b-cfe3a9088964@rock-chips.com>
References: <1468913311-23977-1-git-send-email-frank.wang@rock-chips.com>
 <CAD=FV=Wc0RdgpfzWhEVsrQhj5Pjf4RF3a81mZKaZfpZS41ce5g@mail.gmail.com>
 <d01e6b5c-8e7a-bcba-477b-cfe3a9088964@rock-chips.com>
Message-ID: <3513562.kdo4J8AU27@diego>

Hi Frank,

Am Donnerstag, 21. Juli 2016, 10:49:53 schrieb Frank Wang:
> >> @@ -69,6 +69,15 @@
> >> 
> >>                  regulator-max-microvolt = <3300000>;
> >>          
> >>          };
> >> 
> >> +       vbus_host: vbus-host-regulator {
> >> +               compatible = "regulator-fixed";
> >> +               enable-active-high;
> >> +               gpio = <&gpio4 25 GPIO_ACTIVE_HIGH>;
> >> +               pinctrl-names = "default";
> >> +               pinctrl-0 = <&host_vbus_drv>;
> >> +               regulator-name = "vbus_host";
> >> +       };
> >> +
> > 
> > To match my schematics, this would probably be "vcc5v0_host".
> > Technically there are two regulators but since they are the same
> > voltage and enabled by the same GPIO it seems like modeling it as one
> > regulator is fine.
> 
> Yep, you are right, I will rename it.
> 
> > If you really wanted to model things you could also include the input
> > supply (VCC5V0_SYS).  Not sure how much you care to model in EVB.
> 
> Actually, from
> "Documentation/devicetree/bindings/regulator/fixed-regulator.txt" show,
> input supply name is just optional property, and it seems that only do
> assign "vin" value for input_supply (the second member of struct
> fixed_voltage_config) if "vin-supply" is specified.
> 
> So is input supply name  (VCC5V0_SYS) required here? Would you like to
> give more comments please?

While vin-supply is optional, I think that is meant for real top-level 
regulators (our vcc_sys or whatever) that really don't have a parent 
regulator.

It is always nicer to model the whole power-tree [in a sane way], as it makes 
following the schematics a lot easier. If you mount a debugfs these days you 
can even get a nice tree graph of the regulator infrastructure ... where the 
parent-relationship is also needed to create something meaningful.


Heiko


From adrian.hunter at intel.com  Thu Jul 21 03:09:18 2016
From: adrian.hunter at intel.com (Adrian Hunter)
Date: Thu, 21 Jul 2016 13:09:18 +0300
Subject: [PATCH 1/3] mmc: sdhci-of-arasan: Revert: Always power the PHY
 off/on when clock changes
In-Reply-To: <1467049167-14628-2-git-send-email-dianders@chromium.org>
References: <1467049167-14628-1-git-send-email-dianders@chromium.org>
 <1467049167-14628-2-git-send-email-dianders@chromium.org>
Message-ID: <57909F4E.7090608@intel.com>

On 27/06/16 20:39, Douglas Anderson wrote:
> This reverts commit 4ac0d5f245e1 ("mmc: sdhci-of-arasan: Always power
> the PHY off/on when clock changes"), resolving conflicts with other
> patches that have come after.  It appears that on some boards / with
> some eMMC devices that the patch is causing problems.
> 
> Presumably turning the phy off and on again at the wrong time while
> initially setting up the card is confusing the card, the host, or the
> PHY.  We have lots of power cycles while initially setting up the card
> because the main sdhci driver often turns off the clock by clearing
> SDHCI_CLOCK_CARD_EN and then calls host->ops->set_clock() to set the
> clock again.  With all of those, we ended up with lots of power cycles.
> 
> Presumably the arguments made in the original patch still hold.  That
> is, whenever the card clock is turned off and on again (or changed) we
> really should wait for the DLL to lock again.  However, perhaps it's
> really not that critical for the lower speeds.
> 
> It's possible that the right answer here is:
> * Whenever set_clock() is called we should double-check that the DLL is
>   locked.
> * Whenever set_clock() is called and we're actually changing clocks we
>   should do a power cycle around that.
> * When we're doing a power cycle just because the clock changed, we
>   probably shouldn't do quite as many things (maybe don't need to
>   recalibarate, etc).
> 
> Unfortunately the interaction between SDHCI and the PHY is extremely
> limited because of the limited PHY API.  The PHY does have a reference
> to the card clock and could theoretically register for notifications,
> except that our clock is query only (it uses CLK_GET_RATE_NOCACHE) and
> so can't really be notified about updates.  I believe we would need a
> major redesign of clock handling in SDHCI core to do better than that,
> or we would need to make our one fake notifications.  :(
> 
> Let's hope that we can eventually get more information from Arasan on
> how all this should be handled before doing tons more work.  Until then,
> let's get back to a known working state.  Note that the rest of the
> patches in the 150 MHz series should still work fine even without this
> one.
> 
> Signed-off-by: Douglas Anderson <dianders at chromium.org>


Acked-by: Adrian Hunter <adrian.hunter at intel.com>



> ---
>  drivers/mmc/host/sdhci-of-arasan.c | 21 ++++++++++++++-------
>  1 file changed, 14 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c
> index 678f316702e0..e0f193f7e3e5 100644
> --- a/drivers/mmc/host/sdhci-of-arasan.c
> +++ b/drivers/mmc/host/sdhci-of-arasan.c
> @@ -77,7 +77,6 @@ struct sdhci_arasan_soc_ctl_map {
>   * @host:		Pointer to the main SDHCI host structure.
>   * @clk_ahb:		Pointer to the AHB clock
>   * @phy:		Pointer to the generic phy
> - * @phy_on:		True if the PHY is turned on.
>   * @sdcardclk_hw:	Struct for the clock we might provide to a PHY.
>   * @sdcardclk:		Pointer to normal 'struct clock' for sdcardclk_hw.
>   * @soc_ctl_base:	Pointer to regmap for syscon for soc_ctl registers.
> @@ -87,7 +86,6 @@ struct sdhci_arasan_data {
>  	struct sdhci_host *host;
>  	struct clk	*clk_ahb;
>  	struct phy	*phy;
> -	bool		phy_on;
>  
>  	struct clk_hw	sdcardclk_hw;
>  	struct clk      *sdcardclk;
> @@ -170,10 +168,12 @@ static void sdhci_arasan_set_clock(struct sdhci_host *host, unsigned int clock)
>  {
>  	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>  	struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
> +	bool ctrl_phy = false;
>  
> -	if (sdhci_arasan->phy_on && !IS_ERR(sdhci_arasan->phy)) {
> -		sdhci_arasan->phy_on = false;
> +	if (clock > MMC_HIGH_52_MAX_DTR && (!IS_ERR(sdhci_arasan->phy)))
> +		ctrl_phy = true;
>  
> +	if (ctrl_phy) {
>  		spin_unlock_irq(&host->lock);
>  		phy_power_off(sdhci_arasan->phy);
>  		spin_lock_irq(&host->lock);
> @@ -181,9 +181,7 @@ static void sdhci_arasan_set_clock(struct sdhci_host *host, unsigned int clock)
>  
>  	sdhci_set_clock(host, clock);
>  
> -	if (host->mmc->actual_clock && !IS_ERR(sdhci_arasan->phy)) {
> -		sdhci_arasan->phy_on = true;
> -
> +	if (ctrl_phy) {
>  		spin_unlock_irq(&host->lock);
>  		phy_power_on(sdhci_arasan->phy);
>  		spin_lock_irq(&host->lock);
> @@ -549,6 +547,12 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
>  			goto unreg_clk;
>  		}
>  
> +		ret = phy_power_on(sdhci_arasan->phy);
> +		if (ret < 0) {
> +			dev_err(&pdev->dev, "phy_power_on err.\n");
> +			goto err_phy_power;
> +		}
> +
>  		host->mmc_host_ops.hs400_enhanced_strobe =
>  					sdhci_arasan_hs400_enhanced_strobe;
>  	}
> @@ -561,6 +565,9 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
>  
>  err_add_host:
>  	if (!IS_ERR(sdhci_arasan->phy))
> +		phy_power_off(sdhci_arasan->phy);
> +err_phy_power:
> +	if (!IS_ERR(sdhci_arasan->phy))
>  		phy_exit(sdhci_arasan->phy);
>  unreg_clk:
>  	sdhci_arasan_unregister_sdclk(&pdev->dev);
> 



From frank.wang at rock-chips.com  Thu Jul 21 03:17:48 2016
From: frank.wang at rock-chips.com (Frank Wang)
Date: Thu, 21 Jul 2016 18:17:48 +0800
Subject: [PATCH v8 3/3] arm64: dts: rockchip: add usb2-phy support for
 rk3399
In-Reply-To: <3513562.kdo4J8AU27@diego>
References: <1468913311-23977-1-git-send-email-frank.wang@rock-chips.com>
 <CAD=FV=Wc0RdgpfzWhEVsrQhj5Pjf4RF3a81mZKaZfpZS41ce5g@mail.gmail.com>
 <d01e6b5c-8e7a-bcba-477b-cfe3a9088964@rock-chips.com>
 <3513562.kdo4J8AU27@diego>
Message-ID: <1ada0c16-9fd8-75b2-6357-1398073ac462@rock-chips.com>

Hi Heiko,

On 2016/7/21 17:26, Heiko St?bner wrote:
> Hi Frank,
>
> Am Donnerstag, 21. Juli 2016, 10:49:53 schrieb Frank Wang:
>>>> @@ -69,6 +69,15 @@
>>>>
>>>>                   regulator-max-microvolt = <3300000>;
>>>>           
>>>>           };
>>>>
>>>> +       vbus_host: vbus-host-regulator {
>>>> +               compatible = "regulator-fixed";
>>>> +               enable-active-high;
>>>> +               gpio = <&gpio4 25 GPIO_ACTIVE_HIGH>;
>>>> +               pinctrl-names = "default";
>>>> +               pinctrl-0 = <&host_vbus_drv>;
>>>> +               regulator-name = "vbus_host";
>>>> +       };
>>>> +
>>> To match my schematics, this would probably be "vcc5v0_host".
>>> Technically there are two regulators but since they are the same
>>> voltage and enabled by the same GPIO it seems like modeling it as one
>>> regulator is fine.
>> Yep, you are right, I will rename it.
>>
>>> If you really wanted to model things you could also include the input
>>> supply (VCC5V0_SYS).  Not sure how much you care to model in EVB.
>> Actually, from
>> "Documentation/devicetree/bindings/regulator/fixed-regulator.txt" show,
>> input supply name is just optional property, and it seems that only do
>> assign "vin" value for input_supply (the second member of struct
>> fixed_voltage_config) if "vin-supply" is specified.
>>
>> So is input supply name  (VCC5V0_SYS) required here? Would you like to
>> give more comments please?
> While vin-supply is optional, I think that is meant for real top-level
> regulators (our vcc_sys or whatever) that really don't have a parent
> regulator.
>
> It is always nicer to model the whole power-tree [in a sane way], as it makes
> following the schematics a lot easier. If you mount a debugfs these days you
> can even get a nice tree graph of the regulator infrastructure ... where the
> parent-relationship is also needed to create something meaningful.
>

Got it, many thanks for your explanation, I will add vin-supply next 
version.


BR.
Frank

> Heiko
>
>
>




From ykk at rock-chips.com  Thu Jul 21 06:14:34 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Thu, 21 Jul 2016 21:14:34 +0800
Subject: [PATCH v1 1/2] drm/panel: simple-panel: add the delay timing for
 Sharp LQ123P1JX31
Message-ID: <1469106874-4912-1-git-send-email-ykk@rock-chips.com>

According to page 16 of Sharp LQ123P1JX31 datasheet, we need to add the
missing delay timing. Panel prepare time should be t1 (0.5ms~10ms) plus
t3 (0ms~100ms), and panel enable time should equal to t7 (0ms~50ms), and
panel unprepare time should be t11 (1ms~50ms) plus t12 (500ms~).

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
 drivers/gpu/drm/panel/panel-simple.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
index 85143d1..f178998 100644
--- a/drivers/gpu/drm/panel/panel-simple.c
+++ b/drivers/gpu/drm/panel/panel-simple.c
@@ -1384,6 +1384,11 @@ static const struct panel_desc sharp_lq123p1jx31 = {
 		.width = 259,
 		.height = 173,
 	},
+	.delay = {
+		.prepare = 110,
+		.enable = 50,
+		.unprepare = 550,
+	},
 };
 
 static const struct drm_display_mode shelly_sca07010_bfn_lnn_mode = {
-- 
1.9.1




From zyw at rock-chips.com  Thu Jul 21 06:13:22 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Thu, 21 Jul 2016 21:13:22 +0800
Subject: [v6 PATCH 0/6] Rockchip Type-C and DisplayPort driver
Message-ID: <1469106808-28297-1-git-send-email-zyw@rock-chips.com>


Hi all

This series patch is for rockchip Type-C phy and DisplayPort controller
driver.

The USB Type-C PHY is designed to support the USB3 and DP applications.
The PHY basically has two main components: USB3 and DisplyPort. USB3
operates in SuperSpeed mode and the DP can operate at RBR, HBR and HBR2
data rates. The Type-C cable orientation detection and Power Delivery
(PD) is accomplished using a PD PHY or a exernal PD chip.

The DP controller is compliant with DisplayPort Specification,
Version 1.3, This IP is compatible with the rockchip type-c PHY IP.
There is a uCPU in DP controller, it need a firmware to work, please
put the firmware file[0] to /lib/firmware/cdn/dptx.bin. The uCPU in charge
of aux communication and link training, the host use mailbox to
communicate with the ucpu.

The DP contoller has register a notification with extcon API, to get the
alt mode from PD, the PD driver need call the devm_extcon_dev_allocate
to create a extcon device and use extcon_set_state to notify DP
controller. And call extcon_set_cable_property to set orientation.

About the DP audio, cdn-dp registered 2 DAIs: 0 is I2S, 1 is SPDIF.
We can reference them in simple-card.

This series is based on Mark Yao's branch[1] and Chanwoo Choi's
extcon-test branch[2]. The extcon patch is base on a test branch, so
please do not review the "1/6 extcon: Add Type-C and DP support".

I test this patches on the rk3399-evb board, with a fusb302 driver,
this branch has no rk3399.dtsi, so the patch about dts is not included
in this series.

[0]
https://patchwork.kernel.org/patch/9225567/
[1]
https://github.com/markyzq/kernel-drm-rockchip/tree/drm-rockchip-next-2016-05-23
[2]
https://git.kernel.org/cgit/linux/kernel/git/chanwoo/extcon.git/log/?h=extcon-test
- usb: dwc3: omap: Support the changed method to get the state of connector
- usb: chipdata: Support the changed method to get the state of connector
- extcon: Add the support for extcon property according to type of connector
- extcon: Add the extcon_type to group each connector into five category


Changes in v6:
- move the EXTCON_PROP_TYPEC_POLARITY to EXTCON_TYPE_USB in _supported
Series-changes: 5
- support get property
- add assigned-clocks and assigned-clock-rates
- delete the support of PIN_ASSIGN_A/B
- set the default mode to MODE_DFP_USB
- disable DP PLL at USB3 only mode
- add assigned-clocks and assigned-clock-rates
- add power-domains
- add a port struct
- select SND_SOC_HDMI_CODEC
- force reset the phy when hpd detected

Changes in v5:
- support get property from extcon
- remove PIN ASSIGN A/B support
- alphabetical order
- do not use long, use u32 or u64
- return MODE_CLOCK_HIGH when requested > actual
- Optimized Coding Style
- add a formula to get better tu size and symbol value.
- modify according to Sean Paul's comments
- fixed the fw_wait always 0

Changes in v4:
- add a #phy-cells node
- select EXTCON
- use phy framework to control the USB3 and DP function
- rename PIN_MAP_ to PIN_ASSIGN_
- add a reset node
- support 2 phys
- use phy framework to control DP phy
- support 2 phys

Changes in v3:
- use compatible: rockchip,rk3399-typec-phy
- use dashes instead of underscores.
- remove the phy framework(Kishon Vijay Abraham I)
- add parentheses around the macro
- use a single space between type and name
- add spaces after opening and before closing braces.
- use u16 for register value
- remove type-c phy header file
- CodingStyle optimization
- use some cable extcon to get type-c port information
- add a extcon to notify Display Port
- add SoC specific compatible string
- remove reg = <1>;
- use EXTCON_DISP_DP and EXTCON_DISP_DP_ALT cable to get dp port state.
- reset spdif before config it
- modify the firmware clk to 100Mhz
- retry load firmware if fw file is requested too early

Changes in v2:
- add some registers description
- select RESET_CONTROLLER
- alphabetic order
- modify some spelling mistakes
- make mode cleaner
- use bool for enable/disable
- check all of the return value
- return a better err number
- use more readx_poll_timeout()
- clk_disable_unprepare(tcphy->clk_ref);
- remove unuse functions, rockchip_typec_phy_power_on/off
- remove unnecessary typecast from void *
- use dts node to distinguish between phys.
- Alphabetic order
- remove excess error message
- use define clk_rate
- check all return value
- remove dev_set_name(dp->dev, "cdn-dp");
- use schedule_delayed_work
- remove never-called functions
- remove some unnecessary ()

Changes in v1:
- add extcon node description
- move the registers in phy driver
- remove the suffix of reset
- update the licence note
- init core clock to 50MHz
- use extcon API
- remove unused global
- add some comments for magic num
- change usleep_range(1000, 2000) tousleep_range(1000, 1050)
- remove __func__ from dev_err
- return err number when get clk failed
- remove ADDR_ADJ define
- use devm_clk_get(&pdev->dev, "tcpdcore")
- add extcon node description
- add #sound-dai-cells description
- use extcon API
- use hdmi-codec for the DP Asoc
- do not initialize the "ret"
- printk a err log when drm_of_encoder_active_endpoint_id
- modify the dclk pin_pol to a single line

Chris Zhong (6):
  extcon: Add Type-C and DP support
  Documentation: bindings: add dt doc for Rockchip USB Type-C PHY
  phy: Add USB Type-C PHY driver for rk3399
  arm64: dts: rockchip: add Type-C phy for RK3399
  Documentation: bindings: add dt documentation for cdn DP controller
  drm/rockchip: cdn-dp: add cdn DP support for rk3399

 .../bindings/display/rockchip/cdn-dp-rockchip.txt  |  74 ++
 .../devicetree/bindings/phy/phy-rockchip-typec.txt |  84 ++
 arch/arm64/boot/dts/rockchip/rk3399.dtsi           |  44 +
 drivers/extcon/extcon.c                            |  26 +
 drivers/gpu/drm/rockchip/Kconfig                   |  10 +
 drivers/gpu/drm/rockchip/Makefile                  |   1 +
 drivers/gpu/drm/rockchip/cdn-dp-core.c             | 868 +++++++++++++++++++
 drivers/gpu/drm/rockchip/cdn-dp-core.h             | 102 +++
 drivers/gpu/drm/rockchip/cdn-dp-reg.c              | 904 +++++++++++++++++++
 drivers/gpu/drm/rockchip/cdn-dp-reg.h              | 479 ++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c        |  13 +-
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h        |   9 +
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c        |   2 +
 drivers/phy/Kconfig                                |   9 +
 drivers/phy/Makefile                               |   1 +
 drivers/phy/phy-rockchip-typec.c                   | 960 +++++++++++++++++++++
 include/linux/extcon.h                             |  13 +
 17 files changed, 3596 insertions(+), 3 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/display/rockchip/cdn-dp-rockchip.txt
 create mode 100644 Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.c
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.h
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.c
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.h
 create mode 100644 drivers/phy/phy-rockchip-typec.c

-- 
2.6.3



From zyw at rock-chips.com  Thu Jul 21 06:13:23 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Thu, 21 Jul 2016 21:13:23 +0800
Subject: [v6 PATCH 1/6] extcon: Add Type-C and DP support
In-Reply-To: <1469106808-28297-1-git-send-email-zyw@rock-chips.com>
References: <1469106808-28297-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1469106808-28297-2-git-send-email-zyw@rock-chips.com>

Add EXTCON_DISP_DP for the Display external connector. For Type-C
connector the DisplayPort can work as an Alternate Mode(VESA DisplayPort
Alt Mode on USB Type-C Standard). The Type-C support both normal and
flipped orientation, so add a property to extcon.

Signe-off-by: Chris Zhong <zyw at rock-chips.com>

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
---

Changes in v6:
- move the EXTCON_PROP_TYPEC_POLARITY to EXTCON_TYPE_USB in _supported
Series-changes: 5
- support get property

Changes in v5: None
Changes in v4: None
Changes in v3: None
Changes in v2: None
Changes in v1: None

 drivers/extcon/extcon.c | 26 ++++++++++++++++++++++++++
 include/linux/extcon.h  | 13 +++++++++++++
 2 files changed, 39 insertions(+)

diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c
index a1117db..f79b510 100644
--- a/drivers/extcon/extcon.c
+++ b/drivers/extcon/extcon.c
@@ -157,6 +157,11 @@ struct __extcon_info {
 		.id = EXTCON_DISP_VGA,
 		.name = "VGA",
 	},
+	[EXTCON_DISP_DP] = {
+		.type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB,
+		.id = EXTCON_DISP_DP,
+		.name = "DP",
+	},
 
 	/* Miscellaneous external connector */
 	[EXTCON_DOCK] = {
@@ -270,6 +275,7 @@ static bool is_extcon_property_supported(unsigned int id,
 		switch (prop) {
 		case EXTCON_PROP_USB_ID:
 		case EXTCON_PROP_USB_VBUS:
+		case EXTCON_PROP_TYPEC_POLARITY:
 			return true;
 		default:
 			break;
@@ -547,6 +553,26 @@ int extcon_get_cable_property(struct extcon_dev *edev, unsigned int id,
 				enum extcon_property prop,
 				union extcon_property_value *val)
 {
+	struct extcon_cable *cable;
+	int index;
+
+	if (!edev)
+		return -EINVAL;
+
+	/* Check the property whether is supported or not */
+	if (!is_extcon_property_supported(id, prop))
+		return -EINVAL;
+
+	/* Find the cable index of external connector by using id */
+	index = find_cable_index_by_id(edev, id);
+	if (index < 0)
+		return index;
+
+	/* Store the property value */
+	cable = &edev->cables[index];
+
+	val->intval = cable->propval[prop].intval;
+
 	return 0;
 }
 
diff --git a/include/linux/extcon.h b/include/linux/extcon.h
index f6f0a8d..50ef87f 100644
--- a/include/linux/extcon.h
+++ b/include/linux/extcon.h
@@ -77,6 +77,7 @@ enum extcon_type {
 #define EXTCON_DISP_MHL		41	/* Mobile High-Definition Link */
 #define EXTCON_DISP_DVI		42	/* Digital Visual Interface */
 #define EXTCON_DISP_VGA		43	/* Video Graphics Array */
+#define EXTCON_DISP_DP		44	/* DisplayPort */
 
 /* Miscellaneous external connector */
 #define EXTCON_DOCK		60
@@ -108,9 +109,13 @@ enum extcon_property {
 	 * - EXTCON_PROP_USB_USB
 	 * @type:	integer (int value)
 	 * @value:	0 (low) or 1 (high)
+	 * - EXTCON_PROP_TYPEC_POLARITY,
+	 * @type:	integer (int value)
+	 * @value:	0 (normal) or 1 (flip)
 	 */
 	EXTCON_PROP_USB_ID = 0,
 	EXTCON_PROP_USB_VBUS,
+	EXTCON_PROP_TYPEC_POLARITY,
 
 	/* Properties of EXTCON_TYPE_CHG. */
 	/* Properties of EXTCON_TYPE_JACK. */
@@ -225,6 +230,14 @@ extern int extcon_get_cable_state_(struct extcon_dev *edev, unsigned int id);
 extern int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id,
 				   bool cable_state);
 
+extern int extcon_get_cable_property(struct extcon_dev *edev, unsigned int id,
+				     enum extcon_property prop,
+				     union extcon_property_value *val);
+
+extern int extcon_set_cable_property(struct extcon_dev *edev, unsigned int id,
+				     enum extcon_property prop,
+				     union extcon_property_value val);
+
 /*
  * Following APIs are to monitor every action of a notifier.
  * Registrar gets notified for every external port of a connection device.
-- 
2.6.3



From zyw at rock-chips.com  Thu Jul 21 06:13:24 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Thu, 21 Jul 2016 21:13:24 +0800
Subject: [v6 PATCH 2/6] Documentation: bindings: add dt doc for Rockchip USB
 Type-C PHY
In-Reply-To: <1469106808-28297-1-git-send-email-zyw@rock-chips.com>
References: <1469106808-28297-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1469106808-28297-3-git-send-email-zyw@rock-chips.com>

This patch adds a binding that describes the Rockchip USB Type-C PHY
for rk3399

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
Reviewed-by: Tomasz Figa <tfiga at chromium.org>
Reviewed-by: Kever Yang <kever.yang at rock-chips.com>
Acked-by: Rob Herring <robh at kernel.org>

---

Changes in v6:
- add assigned-clocks and assigned-clock-rates

Changes in v5: None
Changes in v4:
- add a #phy-cells node

Changes in v3:
- use compatible: rockchip,rk3399-typec-phy
- use dashes instead of underscores.

Changes in v2:
- add some registers description

Changes in v1:
- add extcon node description
- move the registers in phy driver
- remove the suffix of reset

 .../devicetree/bindings/phy/phy-rockchip-typec.txt | 84 ++++++++++++++++++++++
 1 file changed, 84 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt

diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt b/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt
new file mode 100644
index 0000000..e384056
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt
@@ -0,0 +1,84 @@
+* ROCKCHIP type-c PHY
+---------------------
+
+Required properties:
+ - compatible : must be "rockchip,rk3399-typec-phy"
+ - reg: Address and length of the usb phy control register set
+ - rockchip,grf : phandle to the syscon managing the "general
+   register files"
+ - clocks : phandle + clock specifier for the phy clocks
+ - clock-names : string, clock name, must be "tcpdcore", "tcpdphy-ref";
+ - assigned-clocks: main clock, should be <&cru SCLK_UPHY0_TCPDCORE> or
+		    <&cru SCLK_UPHY1_TCPDCORE>;
+ - assigned-clock-rates : the phy core clk frequency, shall be: 50000000
+ - resets : a list of phandle + reset specifier pairs
+ - reset-names : string reset name, must be:
+		 "uphy", "uphy-pipe", "uphy-tcphy"
+ - extcon : extcon specifier for the Power Delivery
+ - #phy-cells: must be 0. See ./phy-bindings.txt for details.
+
+Note, there are 2 type-c phys for RK3399, and they are almost identical, except
+these registers(description below), every register node contains 3 sections:
+offset, enable bit, write mask bit.
+ - rockchip,typec-conn-dir : the register of type-c connector direction,
+   for type-c phy0, it must be <0xe580 0 16>;
+   for type-c phy1, it must be <0xe58c 0 16>;
+ - rockchip,usb3tousb2-en : the register of type-c force usb3 to usb2 enable
+   control.
+   for type-c phy0, it must be <0xe580 3 19>;
+   for type-c phy1, it must be <0xe58c 3 19>;
+ - rockchip,external-psm : the register of type-c phy external psm clock
+   selection.
+   for type-c phy0, it must be <0xe588 14 30>;
+   for type-c phy1, it must be <0xe594 14 30>;
+ - rockchip,pipe-status : the register of type-c phy pipe status.
+   for type-c phy0, it must be <0xe5c0 0 0>;
+   for type-c phy1, it must be <0xe5c0 16 16>;
+ - rockchip,uphy-dp-sel : the register of type-c phy selection for DP
+   for type-c phy0, it must be <0x6268 19 19>;
+   for type-c phy1, it must be <0x6268 3 19>;
+
+Example:
+	tcphy0: phy at ff7c0000 {
+		compatible = "rockchip,rk3399-typec-phy";
+		reg = <0x0 0xff7c0000 0x0 0x40000>;
+		rockchip,grf = <&grf>;
+		#phy-cells = <0>;
+		extcon = <&fusb0>;
+		clocks = <&cru SCLK_UPHY0_TCPDCORE>,
+			 <&cru SCLK_UPHY0_TCPDPHY_REF>;
+		clock-names = "tcpdcore", "tcpdphy-ref";
+		assigned-clocks = <&cru SCLK_UPHY0_TCPDCORE>;
+		assigned-clock-rates = <50000000>;
+		resets = <&cru SRST_UPHY0>,
+			 <&cru SRST_UPHY0_PIPE_L00>,
+			 <&cru SRST_P_UPHY0_TCPHY>;
+		reset-names = "uphy", "uphy-pipe", "uphy-tcphy";
+		rockchip,typec-conn-dir = <0xe580 0 16>;
+		rockchip,usb3tousb2-en = <0xe580 3 19>;
+		rockchip,external-psm = <0xe588 14 30>;
+		rockchip,pipe-status = <0xe5c0 0 0>;
+		rockchip,uphy-dp-sel = <0x6268 19 19>;
+	};
+
+	tcphy1: phy at ff800000 {
+		compatible = "rockchip,rk3399-typec-phy";
+		reg = <0x0 0xff800000 0x0 0x40000>;
+		rockchip,grf = <&grf>;
+		#phy-cells = <0>;
+		extcon = <&fusb1>;
+		clocks = <&cru SCLK_UPHY1_TCPDCORE>,
+			 <&cru SCLK_UPHY1_TCPDPHY_REF>;
+		clock-names = "tcpdcore", "tcpdphy-ref";
+		assigned-clocks = <&cru SCLK_UPHY1_TCPDCORE>;
+		assigned-clock-rates = <50000000>;
+		resets = <&cru SRST_UPHY1>,
+		         <&cru SRST_UPHY1_PIPE_L00>,
+			 <&cru SRST_P_UPHY1_TCPHY>;
+		reset-names = "uphy", "uphy-pipe", "uphy-tcphy";
+		rockchip,typec-conn-dir = <0xe58c 0 16>;
+		rockchip,usb3tousb2-en = <0xe58c 3 19>;
+		rockchip,external-psm = <0xe594 14 30>;
+		rockchip,pipe-status = <0xe5c0 16 16>;
+		rockchip,uphy-dp-sel = <0x6268 3 19>;
+	};
-- 
2.6.3



From ykk at rock-chips.com  Thu Jul 21 06:14:46 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Thu, 21 Jul 2016 21:14:46 +0800
Subject: [PATCH v1 2/2] drm/bridge: analogix_dp: turn off the panel when eDP
 need to disable
In-Reply-To: <1469106874-4912-1-git-send-email-ykk@rock-chips.com>
References: <1469106874-4912-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1469106886-4979-1-git-send-email-ykk@rock-chips.com>

Some panels (like Sharp LQ123P1JX31) need to be turn off when eDP
controller stop to send valid video signal, otherwhise panel would
go burn in, and keep flicker and flicker.

So it's better to turn off the panel when eDP need to disable, and
we need to turn on the panel in connector->detect() callback, so
that driver would detect panel status rightly.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
 drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index 32715da..ea059b3 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -961,6 +961,14 @@ analogix_dp_detect(struct drm_connector *connector, bool force)
 {
 	struct analogix_dp_device *dp = to_dp(connector);
 
+	/*
+	 * Panle would prepare for several times here, but don't worry it
+	 * would only enable the hardware at the first prepare time.
+	 */
+	if (dp->plat_data->panel)
+		if (drm_panel_prepare(dp->plat_data->panel))
+			DRM_ERROR("failed to setup the panel\n");
+
 	if (analogix_dp_detect_hpd(dp))
 		return connector_status_disconnected;
 
@@ -1063,7 +1071,8 @@ static void analogix_dp_bridge_disable(struct drm_bridge *bridge)
 		return;
 
 	if (dp->plat_data->panel) {
-		if (drm_panel_disable(dp->plat_data->panel)) {
+		if (drm_panel_disable(dp->plat_data->panel) ||
+		    drm_panel_unprepare(dp->plat_data->panel)) {
 			DRM_ERROR("failed to disable the panel\n");
 			return;
 		}
@@ -1333,13 +1342,6 @@ int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev,
 
 	phy_power_on(dp->phy);
 
-	if (dp->plat_data->panel) {
-		if (drm_panel_prepare(dp->plat_data->panel)) {
-			DRM_ERROR("failed to setup the panel\n");
-			return -EBUSY;
-		}
-	}
-
 	analogix_dp_init_dp(dp);
 
 	ret = devm_request_threaded_irq(&pdev->dev, dp->irq,
-- 
1.9.1




From zyw at rock-chips.com  Thu Jul 21 06:13:26 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Thu, 21 Jul 2016 21:13:26 +0800
Subject: [v6 PATCH 4/6] arm64: dts: rockchip: add Type-C phy for RK3399
In-Reply-To: <1469106808-28297-1-git-send-email-zyw@rock-chips.com>
References: <1469106808-28297-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1469106808-28297-5-git-send-email-zyw@rock-chips.com>

There are 2 Type-C phy on RK3399, they are almost same, except the
address of register. They support USB3.0 Type-C and DisplayPort1.3
Alt Mode on USB Type-C. Register a phy, supply it to USB3 controller
and DP controller.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
---

Changes in v6: None
Changes in v5: None
Changes in v4: None
Changes in v3: None
Changes in v2: None
Changes in v1: None

 arch/arm64/boot/dts/rockchip/rk3399.dtsi | 44 ++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index 0af03a3..7095ddf 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -952,6 +952,50 @@
 		};
 	};
 
+	tcphy0: phy at ff7c0000 {
+		compatible = "rockchip,rk3399-typec-phy";
+		reg = <0x0 0xff7c0000 0x0 0x40000>;
+		rockchip,grf = <&grf>;
+		#phy-cells = <0>;
+		clocks = <&cru SCLK_UPHY0_TCPDCORE>,
+			 <&cru SCLK_UPHY0_TCPDPHY_REF>;
+		clock-names = "tcpdcore", "tcpdphy-ref";
+		assigned-clocks = <&cru SCLK_UPHY0_TCPDCORE>;
+		assigned-clock-rates = <50000000>;
+		resets = <&cru SRST_UPHY0>,
+			 <&cru SRST_UPHY0_PIPE_L00>,
+			 <&cru SRST_P_UPHY0_TCPHY>;
+		reset-names = "uphy", "uphy-pipe", "uphy-tcphy";
+		rockchip,typec-conn-dir = <0xe580 0 16>;
+		rockchip,usb3tousb2-en = <0xe580 3 19>;
+		rockchip,external-psm = <0xe588 14 30>;
+		rockchip,pipe-status = <0xe5c0 0 0>;
+		rockchip,uphy-dp-sel = <0x6268 19 19>;
+		status = "disabled";
+	};
+
+	tcphy1: phy at ff800000 {
+		compatible = "rockchip,rk3399-typec-phy";
+		reg = <0x0 0xff800000 0x0 0x40000>;
+		rockchip,grf = <&grf>;
+		#phy-cells = <0>;
+		clocks = <&cru SCLK_UPHY1_TCPDCORE>,
+			 <&cru SCLK_UPHY1_TCPDPHY_REF>;
+		clock-names = "tcpdcore", "tcpdphy-ref";
+		assigned-clocks = <&cru SCLK_UPHY1_TCPDCORE>;
+		assigned-clock-rates = <50000000>;
+		resets = <&cru SRST_UPHY1>,
+			 <&cru SRST_UPHY1_PIPE_L00>,
+			 <&cru SRST_P_UPHY1_TCPHY>;
+		reset-names = "uphy", "uphy-pipe", "uphy-tcphy";
+		rockchip,typec-conn-dir = <0xe58c 0 16>;
+		rockchip,usb3tousb2-en = <0xe58c 3 19>;
+		rockchip,external-psm = <0xe594 14 30>;
+		rockchip,pipe-status = <0xe5c0 16 16>;
+		rockchip,uphy-dp-sel = <0x6268 3 19>;
+		status = "disabled";
+	};
+
 	watchdog at ff840000 {
 		compatible = "snps,dw-wdt";
 		reg = <0x0 0xff840000 0x0 0x100>;
-- 
2.6.3



From zyw at rock-chips.com  Thu Jul 21 06:13:25 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Thu, 21 Jul 2016 21:13:25 +0800
Subject: [v6 PATCH 3/6] phy: Add USB Type-C PHY driver for rk3399
In-Reply-To: <1469106808-28297-1-git-send-email-zyw@rock-chips.com>
References: <1469106808-28297-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1469106808-28297-4-git-send-email-zyw@rock-chips.com>

Add a PHY provider driver for the rk3399 SoC Type-c PHY. The USB
Type-C PHY is designed to support the USB3 and DP applications. The
PHY basically has two main components: USB3 and DisplyPort. USB3
operates in SuperSpeed mode and the DP can operate at RBR, HBR and
HBR2 data rates.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
Signed-off-by: Kever Yang <kever.yang at rock-chips.com>

---

Changes in v6:
- delete the support of PIN_ASSIGN_A/B
- set the default mode to MODE_DFP_USB
- disable DP PLL at USB3 only mode

Changes in v5:
- support get property from extcon
- remove PIN ASSIGN A/B support

Changes in v4:
- select EXTCON
- use phy framework to control the USB3 and DP function
- rename PIN_MAP_ to PIN_ASSIGN_

Changes in v3:
- remove the phy framework(Kishon Vijay Abraham I)
- add parentheses around the macro
- use a single space between type and name
- add spaces after opening and before closing braces.
- use u16 for register value
- remove type-c phy header file
- CodingStyle optimization
- use some cable extcon to get type-c port information
- add a extcon to notify Display Port

Changes in v2:
- select RESET_CONTROLLER
- alphabetic order
- modify some spelling mistakes
- make mode cleaner
- use bool for enable/disable
- check all of the return value
- return a better err number
- use more readx_poll_timeout()
- clk_disable_unprepare(tcphy->clk_ref);
- remove unuse functions, rockchip_typec_phy_power_on/off
- remove unnecessary typecast from void *
- use dts node to distinguish between phys.

Changes in v1:
- update the licence note
- init core clock to 50MHz
- use extcon API
- remove unused global
- add some comments for magic num
- change usleep_range(1000, 2000) tousleep_range(1000, 1050)
- remove __func__ from dev_err
- return err number when get clk failed
- remove ADDR_ADJ define
- use devm_clk_get(&pdev->dev, "tcpdcore")

 drivers/phy/Kconfig              |   9 +
 drivers/phy/Makefile             |   1 +
 drivers/phy/phy-rockchip-typec.c | 960 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 970 insertions(+)
 create mode 100644 drivers/phy/phy-rockchip-typec.c

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 26566db..83706a5 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -351,6 +351,15 @@ config PHY_ROCKCHIP_DP
 	help
 	  Enable this to support the Rockchip Display Port PHY.
 
+config PHY_ROCKCHIP_TYPEC
+	tristate "Rockchip TYPEC PHY Driver"
+	depends on ARCH_ROCKCHIP && OF
+	select EXTCON
+	select GENERIC_PHY
+	select RESET_CONTROLLER
+	help
+	  Enable this to support the Rockchip USB TYPEC PHY.
+
 config PHY_ST_SPEAR1310_MIPHY
 	tristate "ST SPEAR1310-MIPHY driver"
 	select GENERIC_PHY
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 24596a9..91fa413 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_PHY_QCOM_APQ8064_SATA)	+= phy-qcom-apq8064-sata.o
 obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o
 obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o
 obj-$(CONFIG_PHY_ROCKCHIP_DP)		+= phy-rockchip-dp.o
+obj-$(CONFIG_PHY_ROCKCHIP_TYPEC) += phy-rockchip-typec.o
 obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA)	+= phy-qcom-ipq806x-sata.o
 obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY)	+= phy-spear1310-miphy.o
 obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY)	+= phy-spear1340-miphy.o
diff --git a/drivers/phy/phy-rockchip-typec.c b/drivers/phy/phy-rockchip-typec.c
new file mode 100644
index 0000000..6492985
--- /dev/null
+++ b/drivers/phy/phy-rockchip-typec.c
@@ -0,0 +1,960 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Chris Zhong <zyw at rock-chips.com>
+ *         Kever Yang <kever.yang at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/extcon.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/mfd/syscon.h>
+#include <linux/phy/phy.h>
+
+#define CMN_SSM_BANDGAP			(0x21 << 2)
+#define CMN_SSM_BIAS			(0x22 << 2)
+#define CMN_PLLSM0_PLLEN		(0x29 << 2)
+#define CMN_PLLSM0_PLLPRE		(0x2a << 2)
+#define CMN_PLLSM0_PLLVREF		(0x2b << 2)
+#define CMN_PLLSM0_PLLLOCK		(0x2c << 2)
+#define CMN_PLLSM1_PLLEN		(0x31 << 2)
+#define CMN_PLLSM1_PLLPRE		(0x32 << 2)
+#define CMN_PLLSM1_PLLVREF		(0x33 << 2)
+#define CMN_PLLSM1_PLLLOCK		(0x34 << 2)
+#define CMN_PLLSM1_USER_DEF_CTRL	(0x37 << 2)
+#define CMN_ICAL_OVRD			(0xc1 << 2)
+#define CMN_PLL0_VCOCAL_OVRD		(0x83 << 2)
+#define CMN_PLL0_VCOCAL_INIT		(0x84 << 2)
+#define CMN_PLL0_VCOCAL_ITER		(0x85 << 2)
+#define CMN_PLL0_LOCK_REFCNT_START	(0x90 << 2)
+#define CMN_PLL0_LOCK_PLLCNT_START	(0x92 << 2)
+#define CMN_PLL0_LOCK_PLLCNT_THR	(0x93 << 2)
+#define CMN_PLL0_INTDIV			(0x94 << 2)
+#define CMN_PLL0_FRACDIV		(0x95 << 2)
+#define CMN_PLL0_HIGH_THR		(0x96 << 2)
+#define CMN_PLL0_DSM_DIAG		(0x97 << 2)
+#define CMN_PLL0_SS_CTRL1		(0x98 << 2)
+#define CMN_PLL0_SS_CTRL2		(0x99 << 2)
+#define CMN_PLL1_VCOCAL_START		(0xa1 << 2)
+#define CMN_PLL1_VCOCAL_OVRD		(0xa3 << 2)
+#define CMN_PLL1_VCOCAL_INIT		(0xa4 << 2)
+#define CMN_PLL1_VCOCAL_ITER		(0xa5 << 2)
+#define CMN_PLL1_LOCK_REFCNT_START	(0xb0 << 2)
+#define CMN_PLL1_LOCK_PLLCNT_START	(0xb2 << 2)
+#define CMN_PLL1_LOCK_PLLCNT_THR	(0xb3 << 2)
+#define CMN_PLL1_INTDIV			(0xb4 << 2)
+#define CMN_PLL1_FRACDIV		(0xb5 << 2)
+#define CMN_PLL1_HIGH_THR		(0xb6 << 2)
+#define CMN_PLL1_DSM_DIAG		(0xb7 << 2)
+#define CMN_PLL1_SS_CTRL1		(0xb8 << 2)
+#define CMN_PLL1_SS_CTRL2		(0xb9 << 2)
+#define CMN_RXCAL_OVRD			(0xd1 << 2)
+#define CMN_TXPUCAL_CTRL		(0xe0 << 2)
+#define CMN_TXPUCAL_OVRD		(0xe1 << 2)
+#define CMN_TXPDCAL_OVRD		(0xf1 << 2)
+#define CMN_DIAG_PLL0_FBH_OVRD		(0x1c0 << 2)
+#define CMN_DIAG_PLL0_FBL_OVRD		(0x1c1 << 2)
+#define CMN_DIAG_PLL0_OVRD		(0x1c2 << 2)
+#define CMN_DIAG_PLL0_V2I_TUNE		(0x1c5 << 2)
+#define CMN_DIAG_PLL0_CP_TUNE		(0x1c6 << 2)
+#define CMN_DIAG_PLL0_LF_PROG		(0x1c7 << 2)
+#define CMN_DIAG_PLL1_FBH_OVRD		(0x1d0 << 2)
+#define CMN_DIAG_PLL1_FBL_OVRD		(0x1d1 << 2)
+#define CMN_DIAG_PLL1_OVRD		(0x1d2 << 2)
+#define CMN_DIAG_PLL1_V2I_TUNE		(0x1d5 << 2)
+#define CMN_DIAG_PLL1_CP_TUNE		(0x1d6 << 2)
+#define CMN_DIAG_PLL1_LF_PROG		(0x1d7 << 2)
+#define CMN_DIAG_PLL1_PTATIS_TUNE1	(0x1d8 << 2)
+#define CMN_DIAG_PLL1_PTATIS_TUNE2	(0x1d9 << 2)
+#define CMN_DIAG_PLL1_INCLK_CTRL	(0x1da << 2)
+#define CMN_DIAG_HSCLK_SEL		(0x1e0 << 2)
+
+#define XCVR_PSM_RCTRL(n)		((0x4001 | ((n) << 9)) << 2)
+#define XCVR_PSM_CAL_TMR(n)		((0x4002 | ((n) << 9)) << 2)
+#define XCVR_PSM_A0IN_TMR(n)		((0x4003 | ((n) << 9)) << 2)
+#define TX_TXCC_CAL_SCLR_MULT(n)	((0x4047 | ((n) << 9)) << 2)
+#define TX_TXCC_CPOST_MULT_00(n)	((0x404c | ((n) << 9)) << 2)
+#define TX_TXCC_CPOST_MULT_01(n)	((0x404d | ((n) << 9)) << 2)
+#define TX_TXCC_CPOST_MULT_10(n)	((0x404e | ((n) << 9)) << 2)
+#define TX_TXCC_CPOST_MULT_11(n)	((0x404f | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_000(n)	((0x4050 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_001(n)	((0x4051 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_010(n)	((0x4052 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_011(n)	((0x4053 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_100(n)	((0x4054 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_101(n)	((0x4055 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_110(n)	((0x4056 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_111(n)	((0x4057 | ((n) << 9)) << 2)
+#define XCVR_DIAG_PLLDRC_CTRL(n)	((0x40e0 | ((n) << 9)) << 2)
+#define XCVR_DIAG_BIDI_CTRL(n)		((0x40e8 | ((n) << 9)) << 2)
+#define XCVR_DIAG_LANE_FCM_EN_MGN(n)	((0x40f2 | ((n) << 9)) << 2)
+#define TX_PSC_A0(n)			((0x4100 | ((n) << 9)) << 2)
+#define TX_PSC_A1(n)			((0x4101 | ((n) << 9)) << 2)
+#define TX_PSC_A2(n)			((0x4102 | ((n) << 9)) << 2)
+#define TX_PSC_A3(n)			((0x4103 | ((n) << 9)) << 2)
+#define TX_RCVDET_CTRL(n)		((0x4120 | ((n) << 9)) << 2)
+#define TX_RCVDET_EN_TMR(n)		((0x4122 | ((n) << 9)) << 2)
+#define TX_RCVDET_ST_TMR(n)		((0x4123 | ((n) << 9)) << 2)
+#define TX_DIAG_TX_DRV(n)		((0x41e1 | ((n) << 9)) << 2)
+#define TX_DIAG_BGREF_PREDRV_DELAY	(0x41e7 << 2)
+#define TX_ANA_CTRL_REG_1		(0x5020 << 2)
+#define TX_ANA_CTRL_REG_2		(0x5021 << 2)
+#define TXDA_COEFF_CALC_CTRL		(0x5022 << 2)
+#define TX_DIG_CTRL_REG_2		(0x5024 << 2)
+#define TXDA_CYA_AUXDA_CYA		(0x5025 << 2)
+#define TX_ANA_CTRL_REG_3		(0x5026 << 2)
+#define TX_ANA_CTRL_REG_4		(0x5027 << 2)
+#define TX_ANA_CTRL_REG_5		(0x5029 << 2)
+
+#define RX_PSC_A0(n)			((0x8000 | ((n) << 9)) << 2)
+#define RX_PSC_A1(n)			((0x8001 | ((n) << 9)) << 2)
+#define RX_PSC_A2(n)			((0x8002 | ((n) << 9)) << 2)
+#define RX_PSC_A3(n)			((0x8003 | ((n) << 9)) << 2)
+#define RX_PSC_CAL(n)			((0x8006 | ((n) << 9)) << 2)
+#define RX_PSC_RDY(n)			((0x8007 | ((n) << 9)) << 2)
+#define RX_IQPI_ILL_CAL_OVRD		(0x8023 << 2)
+#define RX_EPI_ILL_CAL_OVRD		(0x8033 << 2)
+#define RX_SDCAL0_OVRD			(0x8041 << 2)
+#define RX_SDCAL1_OVRD			(0x8049 << 2)
+#define RX_SLC_INIT			(0x806d << 2)
+#define RX_SLC_RUN			(0x806e << 2)
+#define RX_CDRLF_CNFG2			(0x8081 << 2)
+#define RX_SIGDET_HL_FILT_TMR(n)	((0x8090 | ((n) << 9)) << 2)
+#define RX_SLC_IOP0_OVRD		(0x8101 << 2)
+#define RX_SLC_IOP1_OVRD		(0x8105 << 2)
+#define RX_SLC_QOP0_OVRD		(0x8109 << 2)
+#define RX_SLC_QOP1_OVRD		(0x810d << 2)
+#define RX_SLC_EOP0_OVRD		(0x8111 << 2)
+#define RX_SLC_EOP1_OVRD		(0x8115 << 2)
+#define RX_SLC_ION0_OVRD		(0x8119 << 2)
+#define RX_SLC_ION1_OVRD		(0x811d << 2)
+#define RX_SLC_QON0_OVRD		(0x8121 << 2)
+#define RX_SLC_QON1_OVRD		(0x8125 << 2)
+#define RX_SLC_EON0_OVRD		(0x8129 << 2)
+#define RX_SLC_EON1_OVRD		(0x812d << 2)
+#define RX_SLC_IEP0_OVRD		(0x8131 << 2)
+#define RX_SLC_IEP1_OVRD		(0x8135 << 2)
+#define RX_SLC_QEP0_OVRD		(0x8139 << 2)
+#define RX_SLC_QEP1_OVRD		(0x813d << 2)
+#define RX_SLC_EEP0_OVRD		(0x8141 << 2)
+#define RX_SLC_EEP1_OVRD		(0x8145 << 2)
+#define RX_SLC_IEN0_OVRD		(0x8149 << 2)
+#define RX_SLC_IEN1_OVRD		(0x814d << 2)
+#define RX_SLC_QEN0_OVRD		(0x8151 << 2)
+#define RX_SLC_QEN1_OVRD		(0x8155 << 2)
+#define RX_SLC_EEN0_OVRD		(0x8159 << 2)
+#define RX_SLC_EEN1_OVRD		(0x815d << 2)
+#define RX_DIAG_SIGDET_TUNE(n)		((0x81dc | ((n) << 9)) << 2)
+#define RX_DIAG_SC2C_DELAY		(0x81e1 << 2)
+
+#define PMA_LANE_CFG			(0xc000 << 2)
+#define PIPE_CMN_CTRL1			(0xc001 << 2)
+#define PIPE_CMN_CTRL2			(0xc002 << 2)
+#define PIPE_COM_LOCK_CFG1		(0xc003 << 2)
+#define PIPE_COM_LOCK_CFG2		(0xc004 << 2)
+#define PIPE_RCV_DET_INH		(0xc005 << 2)
+#define DP_MODE_CTL			(0xc008 << 2)
+#define DP_CLK_CTL			(0xc009 << 2)
+#define STS				(0xc00F << 2)
+#define PHY_ISO_CMN_CTRL		(0xc010 << 2)
+#define PHY_DP_TX_CTL			(0xc408 << 2)
+#define PMA_CMN_CTRL1			(0xc800 << 2)
+#define PHY_PMA_ISO_CMN_CTRL		(0xc810 << 2)
+#define PHY_ISOLATION_CTRL		(0xc81f << 2)
+#define PHY_PMA_ISO_XCVR_CTRL(n)	((0xcc11 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_LINK_MODE(n)	((0xcc12 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_PWRST_CTRL(n)	((0xcc13 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_TX_DATA_LO(n)	((0xcc14 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_TX_DATA_HI(n)	((0xcc15 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_RX_DATA_LO(n)	((0xcc16 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_RX_DATA_HI(n)	((0xcc17 | ((n) << 6)) << 2)
+#define TX_BIST_CTRL(n)			((0x4140 | ((n) << 9)) << 2)
+#define TX_BIST_UDDWR(n)		((0x4141 | ((n) << 9)) << 2)
+
+/*
+ * Selects which PLL clock will be driven on the analog high speed
+ * clock 0: PLL 0 div 1
+ * clock 1: PLL 1 div 2
+ */
+#define CLK_PLL_CONFIG			0X30
+#define CLK_PLL_MASK			0x33
+
+#define CMN_READY			BIT(0)
+
+#define DP_PLL_CLOCK_ENABLE		BIT(2)
+#define DP_PLL_ENABLE			BIT(0)
+#define DP_PLL_DATA_RATE_RBR		((2 << 12) | (4 << 8))
+#define DP_PLL_DATA_RATE_HBR		((2 << 12) | (4 << 8))
+#define DP_PLL_DATA_RATE_HBR2		((1 << 12) | (2 << 8))
+
+#define GRF_SOC_CON26			0x6268
+#define UPHY_DP_SEL			BIT(3)
+#define UPHY_DP_SEL_MASK		BIT(19)
+#define DPTX_HPD_SEL			(3 << 12)
+#define DPTX_HPD_DEL			(2 << 12)
+#define DPTX_HPD_SEL_MASK		(3 << 28)
+
+#define PHY_MODE_SET_TIMEOUT		1000000
+
+#define PIN_ASSIGN_A			BIT(0)
+#define PIN_ASSIGN_B			BIT(1)
+#define PIN_ASSIGN_C			BIT(2)
+#define PIN_ASSIGN_D			BIT(3)
+#define PIN_ASSIGN_E			BIT(4)
+#define PIN_ASSIGN_F			BIT(5)
+
+#define MODE_DISCONNECT			0
+#define MODE_UFP_USB			BIT(0)
+#define MODE_DFP_USB			BIT(1)
+#define MODE_DFP_DP			BIT(2)
+
+struct usb3phy_reg {
+	u32 offset;
+	u32 enable_bit;
+	u32 write_enable;
+};
+
+struct rockchip_usb3phy_port_cfg {
+	struct usb3phy_reg typec_conn_dir;
+	struct usb3phy_reg usb3tousb2_en;
+	struct usb3phy_reg external_psm;
+	struct usb3phy_reg pipe_status;
+	struct usb3phy_reg uphy_dp_sel;
+};
+
+struct rockchip_typec_phy {
+	struct device *dev;
+	void __iomem *base;
+	struct extcon_dev *extcon;
+	struct phy *phy;
+	struct regmap *grf_regs;
+	struct clk *clk_core;
+	struct clk *clk_ref;
+	struct reset_control *uphy_rst;
+	struct reset_control *pipe_rst;
+	struct reset_control *tcphy_rst;
+	struct rockchip_usb3phy_port_cfg port_cfgs;
+
+	bool flip;
+	u8 mode;
+	u8 pin_assign;
+};
+
+struct phy_reg {
+	u16 value;
+	u32 addr;
+};
+
+struct phy_reg usb_pll_cfg[] = {
+	{ 0xf0,		CMN_PLL0_VCOCAL_INIT },
+	{ 0x18,		CMN_PLL0_VCOCAL_ITER },
+	{ 0xd0,		CMN_PLL0_INTDIV },
+	{ 0x4a4a,	CMN_PLL0_FRACDIV },
+	{ 0x34,		CMN_PLL0_HIGH_THR },
+	{ 0x1ee,	CMN_PLL0_SS_CTRL1 },
+	{ 0x7f03,	CMN_PLL0_SS_CTRL2 },
+	{ 0x20,		CMN_PLL0_DSM_DIAG },
+	{ 0,		CMN_DIAG_PLL0_OVRD },
+	{ 0,		CMN_DIAG_PLL0_FBH_OVRD },
+	{ 0,		CMN_DIAG_PLL0_FBL_OVRD },
+	{ 0x7,		CMN_DIAG_PLL0_V2I_TUNE },
+	{ 0x45,		CMN_DIAG_PLL0_CP_TUNE },
+	{ 0x8,		CMN_DIAG_PLL0_LF_PROG },
+};
+
+struct phy_reg dp_pll_cfg[] = {
+	{ 0xf0,		CMN_PLL1_VCOCAL_INIT },
+	{ 0x18,		CMN_PLL1_VCOCAL_ITER },
+	{ 0x30b9,	CMN_PLL1_VCOCAL_START },
+	{ 0x21c,	CMN_PLL1_INTDIV },
+	{ 0,		CMN_PLL1_FRACDIV },
+	{ 0x5,		CMN_PLL1_HIGH_THR },
+	{ 0x35,		CMN_PLL1_SS_CTRL1 },
+	{ 0x7f1e,	CMN_PLL1_SS_CTRL2 },
+	{ 0x20,		CMN_PLL1_DSM_DIAG },
+	{ 0,		CMN_PLLSM1_USER_DEF_CTRL },
+	{ 0,		CMN_DIAG_PLL1_OVRD },
+	{ 0,		CMN_DIAG_PLL1_FBH_OVRD },
+	{ 0,		CMN_DIAG_PLL1_FBL_OVRD },
+	{ 0x6,		CMN_DIAG_PLL1_V2I_TUNE },
+	{ 0x45,		CMN_DIAG_PLL1_CP_TUNE },
+	{ 0x8,		CMN_DIAG_PLL1_LF_PROG },
+	{ 0x100,	CMN_DIAG_PLL1_PTATIS_TUNE1 },
+	{ 0x7,		CMN_DIAG_PLL1_PTATIS_TUNE2 },
+	{ 0x4,		CMN_DIAG_PLL1_INCLK_CTRL },
+};
+
+static void tcphy_cfg_24m(struct rockchip_typec_phy *tcphy,
+			  u32 num_lanes)
+{
+	u32 i, rdata;
+
+	/*
+	 * cmn_ref_clk_sel = 3, select the 24Mhz for clk parent
+	 * cmn_psm_clk_dig_div = 2, set the clk division to 2
+	 */
+	writel(0x830, tcphy->base + PMA_CMN_CTRL1);
+	for (i = 0; i < num_lanes; i++) {
+		/*
+		 * The following PHY configuration assumes a 24 MHz reference
+		 * clock.
+		 */
+		writel(0x90, tcphy->base + XCVR_DIAG_LANE_FCM_EN_MGN(i));
+		writel(0x960, tcphy->base + TX_RCVDET_EN_TMR(i));
+		writel(0x30, tcphy->base + TX_RCVDET_ST_TMR(i));
+	}
+
+	rdata = readl(tcphy->base + CMN_DIAG_HSCLK_SEL);
+	rdata &= ~CLK_PLL_MASK;
+	rdata |= CLK_PLL_CONFIG;
+	writel(rdata, tcphy->base + CMN_DIAG_HSCLK_SEL);
+
+}
+
+static void tcphy_cfg_usb_pll(struct rockchip_typec_phy *tcphy)
+{
+	u32 i;
+
+	/* load the configuration of PLL0 */
+	for (i = 0; i < ARRAY_SIZE(usb_pll_cfg); i++)
+		writel(usb_pll_cfg[i].value, tcphy->base + usb_pll_cfg[i].addr);
+}
+
+static void tcphy_cfg_dp_pll(struct rockchip_typec_phy *tcphy)
+{
+	u32 i;
+
+	/* set the default mode to RBR */
+	writel(DP_PLL_CLOCK_ENABLE | DP_PLL_ENABLE | DP_PLL_DATA_RATE_RBR,
+	       tcphy->base + DP_CLK_CTL);
+
+	/* load the configuration of PLL1 */
+	for (i = 0; i < ARRAY_SIZE(dp_pll_cfg); i++)
+		writel(dp_pll_cfg[i].value, tcphy->base + dp_pll_cfg[i].addr);
+}
+
+static void tcphy_tx_usb_cfg_lane(struct rockchip_typec_phy *tcphy,
+				  u32 lane)
+{
+	writel(0x7799, tcphy->base + TX_PSC_A0(lane));
+	writel(0x7798, tcphy->base + TX_PSC_A1(lane));
+	writel(0x5098, tcphy->base + TX_PSC_A2(lane));
+	writel(0x5098, tcphy->base + TX_PSC_A3(lane));
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
+	writel(0xbf, tcphy->base + XCVR_DIAG_BIDI_CTRL(lane));
+}
+
+static void tcphy_rx_usb_cfg_lane(struct rockchip_typec_phy *tcphy,
+				  u32 lane)
+{
+	writel(0xa6fd, tcphy->base + RX_PSC_A0(lane));
+	writel(0xa6fd, tcphy->base + RX_PSC_A1(lane));
+	writel(0xa410, tcphy->base + RX_PSC_A2(lane));
+	writel(0x2410, tcphy->base + RX_PSC_A3(lane));
+	writel(0x23ff, tcphy->base + RX_PSC_CAL(lane));
+	writel(0x13, tcphy->base + RX_SIGDET_HL_FILT_TMR(lane));
+	writel(0x1004, tcphy->base + RX_DIAG_SIGDET_TUNE(lane));
+	writel(0x2010, tcphy->base + RX_PSC_RDY(lane));
+	writel(0xfb, tcphy->base + XCVR_DIAG_BIDI_CTRL(lane));
+}
+
+static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy,
+			      u32 lane)
+{
+	u16 rdata;
+
+	writel(0xbefc, tcphy->base + XCVR_PSM_RCTRL(lane));
+	writel(0x6799, tcphy->base + TX_PSC_A0(lane));
+	writel(0x6798, tcphy->base + TX_PSC_A1(lane));
+	writel(0x98, tcphy->base + TX_PSC_A2(lane));
+	writel(0x98, tcphy->base + TX_PSC_A3(lane));
+
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_001(lane));
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_010(lane));
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_011(lane));
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_100(lane));
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_101(lane));
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_110(lane));
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_111(lane));
+	writel(0, tcphy->base + TX_TXCC_CPOST_MULT_10(lane));
+	writel(0, tcphy->base + TX_TXCC_CPOST_MULT_01(lane));
+	writel(0, tcphy->base + TX_TXCC_CPOST_MULT_00(lane));
+	writel(0, tcphy->base + TX_TXCC_CPOST_MULT_11(lane));
+
+	writel(0x128, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
+	writel(0x400, tcphy->base + TX_DIAG_TX_DRV(lane));
+
+	rdata = readl(tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
+	rdata = (rdata & 0x8fff) | 0x6000;
+	writel(rdata, tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
+}
+
+static void tcphy_cfg_pin_assign(struct rockchip_typec_phy *tcphy)
+{
+	switch (tcphy->pin_assign) {
+	case PIN_ASSIGN_A:
+		writel(0x19d5, tcphy->base + PMA_LANE_CFG);
+		break;
+	case PIN_ASSIGN_B:
+		writel(0x1500, tcphy->base + PMA_LANE_CFG);
+		break;
+	case PIN_ASSIGN_C:
+	case PIN_ASSIGN_E:
+		writel(0x51d9, tcphy->base + PMA_LANE_CFG);
+		break;
+	case PIN_ASSIGN_D:
+	case PIN_ASSIGN_F:
+		writel(0x5100, tcphy->base + PMA_LANE_CFG);
+		break;
+	};
+}
+
+static inline int property_enable(struct rockchip_typec_phy *tcphy,
+				  const struct usb3phy_reg *reg, bool en)
+{
+	u32 mask = 1 << reg->write_enable;
+	u32 val = en << reg->enable_bit;
+
+	return regmap_write(tcphy->grf_regs, reg->offset, val | mask);
+}
+
+static void tcphy_lanes_config(struct rockchip_typec_phy *tcphy)
+{
+	struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+	u32 i;
+
+	property_enable(tcphy, &cfg->typec_conn_dir, tcphy->flip);
+
+	tcphy_cfg_24m(tcphy, 0x4);
+
+	switch (tcphy->mode) {
+	case MODE_UFP_USB:
+	case MODE_DFP_USB:
+		tcphy_cfg_usb_pll(tcphy);
+		if (tcphy->flip) {
+			tcphy_tx_usb_cfg_lane(tcphy, 3);
+			tcphy_rx_usb_cfg_lane(tcphy, 2);
+		} else {
+			tcphy_tx_usb_cfg_lane(tcphy, 0);
+			tcphy_rx_usb_cfg_lane(tcphy, 1);
+		}
+		break;
+	case MODE_DFP_DP:
+		tcphy_cfg_dp_pll(tcphy);
+		for (i = 0; i < 4; i++)
+			tcphy_dp_cfg_lane(tcphy, i);
+		break;
+	case MODE_DFP_USB | MODE_DFP_DP:
+		tcphy_cfg_usb_pll(tcphy);
+		tcphy_cfg_dp_pll(tcphy);
+		if (tcphy->flip) {
+			tcphy_tx_usb_cfg_lane(tcphy, 3);
+			tcphy_rx_usb_cfg_lane(tcphy, 2);
+			tcphy_dp_cfg_lane(tcphy, 0);
+			tcphy_dp_cfg_lane(tcphy, 1);
+		} else {
+			tcphy_tx_usb_cfg_lane(tcphy, 0);
+			tcphy_rx_usb_cfg_lane(tcphy, 1);
+			tcphy_dp_cfg_lane(tcphy, 2);
+			tcphy_dp_cfg_lane(tcphy, 3);
+		}
+		break;
+	}
+}
+
+static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy)
+{
+	u16 rdata, rdata2, val;
+
+	/* disable txda_cal_latch_en for rewrite the calibration values */
+	rdata = readl(tcphy->base + TX_ANA_CTRL_REG_1);
+	val = rdata & 0xdfff;
+	writel(val, tcphy->base + TX_ANA_CTRL_REG_1);
+
+	/*
+	 * read a resistor calibration code from CMN_TXPUCAL_CTRL[6:0] and
+	 * write it to TX_DIG_CTRL_REG_2[6:0], and delay 1ms to make sure it
+	 * works.
+	 */
+	rdata = readl(tcphy->base + TX_DIG_CTRL_REG_2);
+	rdata = rdata & 0xffc0;
+
+	rdata2 = readl(tcphy->base + CMN_TXPUCAL_CTRL);
+	rdata2 = rdata2 & 0x3f;
+
+	val = rdata | rdata2;
+	writel(val, tcphy->base + TX_DIG_CTRL_REG_2);
+	usleep_range(1000, 1050);
+
+	/*
+	 * Enable signal for latch that sample and holds calibration values.
+	 * Activate this signal for 1 clock cycle to sample new calibration
+	 * values.
+	 */
+	rdata = readl(tcphy->base + TX_ANA_CTRL_REG_1);
+	val = rdata | 0x2000;
+	writel(val, tcphy->base + TX_ANA_CTRL_REG_1);
+	usleep_range(150, 200);
+
+	/* set TX Voltage Level and TX Deemphasis to 0 */
+	writel(0, tcphy->base + PHY_DP_TX_CTL);
+	/* re-enable decap */
+	writel(0x100, tcphy->base + TX_ANA_CTRL_REG_2);
+	writel(0x300, tcphy->base + TX_ANA_CTRL_REG_2);
+	writel(0x2008, tcphy->base + TX_ANA_CTRL_REG_1);
+	writel(0x2018, tcphy->base + TX_ANA_CTRL_REG_1);
+
+	writel(0, tcphy->base + TX_ANA_CTRL_REG_5);
+
+	/*
+	 * Programs txda_drv_ldo_prog[15:0], Sets driver LDO
+	 * voltage 16'h1001 for DP-AUX-TX and RX
+	 */
+	writel(0x1001, tcphy->base + TX_ANA_CTRL_REG_4);
+
+	/* re-enables Bandgap reference for LDO */
+	writel(0x2098, tcphy->base + TX_ANA_CTRL_REG_1);
+	writel(0x2198, tcphy->base + TX_ANA_CTRL_REG_1);
+
+	/*
+	 * re-enables the transmitter pre-driver, driver data selection MUX,
+	 * and receiver detect circuits.
+	 */
+	writel(0x301, tcphy->base + TX_ANA_CTRL_REG_2);
+	writel(0x303, tcphy->base + TX_ANA_CTRL_REG_2);
+
+	/*
+	 * BIT 12: Controls auxda_polarity, which selects the polarity of the
+	 * xcvr:
+	 * 1, Reverses the polarity (If TYPEC, Pulls ups aux_p and pull
+	 * down aux_m)
+	 * 0, Normal polarity (if TYPE_C, pulls up aux_m and pulls down
+	 * aux_p)
+	 */
+	val = 0xa078;
+	if (!tcphy->flip)
+		val |= BIT(12);
+	writel(val, tcphy->base + TX_ANA_CTRL_REG_1);
+
+	writel(0, tcphy->base + TX_ANA_CTRL_REG_3);
+	writel(0, tcphy->base + TX_ANA_CTRL_REG_4);
+	writel(0, tcphy->base + TX_ANA_CTRL_REG_5);
+
+	/*
+	 * Controls low_power_swing_en, set the voltage swing of the driver
+	 * to 400mv. The values	below are peak to peak (differential) values.
+	 */
+	writel(4, tcphy->base + TXDA_COEFF_CALC_CTRL);
+	writel(0, tcphy->base + TXDA_CYA_AUXDA_CYA);
+
+	/* Controls tx_high_z_tm_en */
+	val = readl(tcphy->base + TX_DIG_CTRL_REG_2);
+	val |= BIT(15);
+	writel(val, tcphy->base + TX_DIG_CTRL_REG_2);
+}
+
+static void tcphy_usb3_init(struct rockchip_typec_phy *tcphy)
+{
+	struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+	const struct usb3phy_reg *reg = &cfg->pipe_status;
+	int timeout;
+	unsigned int val;
+
+	/* wait TCPHY for pipe ready */
+	for (timeout = 0; timeout < 100; timeout++) {
+		regmap_read(tcphy->grf_regs, reg->offset, &val);
+		if (!(val & BIT(reg->enable_bit)))
+			return;
+		usleep_range(10, 20);
+	}
+
+	dev_err(tcphy->dev, "wait pipe ready timeout!\n");
+}
+
+static int tcphy_dp_init(struct rockchip_typec_phy *tcphy)
+{
+	struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+	u16 val;
+	int ret;
+
+	property_enable(tcphy, &cfg->uphy_dp_sel, 1);
+
+	ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
+				 val, val & BIT(6), 1000, PHY_MODE_SET_TIMEOUT);
+	if (ret < 0) {
+		dev_err(tcphy->dev, "failed to wait TCPHY for DP ready\n");
+		return ret;
+	}
+
+	tcphy_dp_aux_calibration(tcphy);
+
+	if (tcphy->mode & MODE_DFP_USB)
+		writel(0xc101, tcphy->base + DP_MODE_CTL);
+	else
+		writel(0x0101, tcphy->base + DP_MODE_CTL);
+
+	ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
+				 val, val & BIT(4), 1000, PHY_MODE_SET_TIMEOUT);
+	if (ret < 0) {
+		dev_err(tcphy->dev, "failed to wait TCPHY enter A0\n");
+		return -EPROBE_DEFER;
+	}
+
+	return 0;
+}
+
+static int tcphy_phy_init(struct rockchip_typec_phy *tcphy)
+{
+	u32 val;
+	int ret;
+
+	ret = clk_prepare_enable(tcphy->clk_core);
+	if (ret) {
+		dev_err(tcphy->dev, "Failed to prepare_enable core clock\n");
+		return ret;
+	}
+
+	ret = clk_prepare_enable(tcphy->clk_ref);
+	if (ret) {
+		dev_err(tcphy->dev, "Failed to prepare_enable ref clock\n");
+		goto err_clk_core;
+	}
+
+	reset_control_deassert(tcphy->tcphy_rst);
+
+	tcphy_lanes_config(tcphy);
+
+	tcphy_cfg_pin_assign(tcphy);
+
+	if (tcphy->mode & MODE_DFP_DP) {
+		if (tcphy->mode & MODE_DFP_USB)
+			writel(0xc104, tcphy->base + DP_MODE_CTL);
+		else
+			writel(0x0104, tcphy->base + DP_MODE_CTL);
+	}
+
+	reset_control_deassert(tcphy->uphy_rst);
+
+	ret = readx_poll_timeout(readl, tcphy->base + PMA_CMN_CTRL1,
+				 val, val & CMN_READY, 10,
+				 PHY_MODE_SET_TIMEOUT);
+	if (ret < 0) {
+		dev_err(tcphy->dev, "wait pma ready timeout\n");
+		goto timeout_pma_ready;
+	}
+
+	reset_control_deassert(tcphy->pipe_rst);
+
+	return 0;
+
+timeout_pma_ready:
+	reset_control_assert(tcphy->tcphy_rst);
+	reset_control_assert(tcphy->uphy_rst);
+	clk_disable_unprepare(tcphy->clk_ref);
+err_clk_core:
+	clk_disable_unprepare(tcphy->clk_core);
+	return ret;
+}
+
+static void tcphy_phy_deinit(struct rockchip_typec_phy *tcphy)
+{
+	reset_control_assert(tcphy->tcphy_rst);
+	reset_control_assert(tcphy->uphy_rst);
+	reset_control_assert(tcphy->pipe_rst);
+	clk_disable_unprepare(tcphy->clk_core);
+	clk_disable_unprepare(tcphy->clk_ref);
+}
+
+static int rockchip_typec_phy_power_on(struct phy *phy)
+{
+	struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
+	struct extcon_dev *edev = tcphy->extcon;
+	union extcon_property_value property;
+	bool dfp, ufp, dp;
+	u8 mode;
+	int ret, id = EXTCON_USB;
+
+	ufp = extcon_get_cable_state_(edev, EXTCON_USB);
+	dfp = extcon_get_cable_state_(edev, EXTCON_USB_HOST);
+	dp = extcon_get_cable_state_(edev, EXTCON_DISP_DP);
+
+	mode = MODE_DFP_USB;
+	tcphy->pin_assign = PIN_ASSIGN_D;
+
+	if (ufp) {
+		mode = MODE_UFP_USB;
+		id = EXTCON_USB;
+	} else if (dfp && dp) {
+		mode = MODE_DFP_USB | MODE_DFP_DP;
+		tcphy->pin_assign = PIN_ASSIGN_D;
+	} else if (dp) {
+		mode = MODE_DFP_DP;
+		tcphy->pin_assign = PIN_ASSIGN_C;
+		id = EXTCON_DISP_DP;
+	}
+
+	if (tcphy->mode == mode)
+		return 0;
+
+	tcphy->mode = mode;
+	ret = extcon_get_cable_property(edev, id, EXTCON_PROP_TYPEC_POLARITY,
+					&property);
+	if (ret) {
+		dev_err(tcphy->dev, "get property failed\n");
+		return ret;
+	}
+
+	tcphy->flip = property.intval ? 1 : 0;
+
+	regmap_write(tcphy->grf_regs, GRF_SOC_CON26,
+		     DPTX_HPD_SEL_MASK | DPTX_HPD_DEL);
+
+	ret = tcphy_phy_init(tcphy);
+	if (ret) {
+		tcphy->mode = MODE_DISCONNECT;
+		return ret;
+	}
+
+	if (mode & (MODE_UFP_USB | MODE_DFP_USB))
+		tcphy_usb3_init(tcphy);
+
+	if (mode & MODE_DFP_DP) {
+		ret = tcphy_dp_init(tcphy);
+		if (ret) {
+			tcphy_phy_deinit(tcphy);
+			tcphy->mode = MODE_DISCONNECT;
+			return ret;
+		}
+
+		regmap_write(tcphy->grf_regs, GRF_SOC_CON26,
+			     DPTX_HPD_SEL_MASK | DPTX_HPD_SEL);
+	}
+
+	return 0;
+}
+
+static int rockchip_typec_phy_power_off(struct phy *phy)
+{
+	struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
+
+	tcphy_phy_deinit(tcphy);
+	tcphy->mode = MODE_DISCONNECT;
+
+	return 0;
+}
+
+static const struct phy_ops rockchip_tcphy_ops = {
+	.power_on	= rockchip_typec_phy_power_on,
+	.power_off	= rockchip_typec_phy_power_off,
+	.owner		= THIS_MODULE,
+};
+
+static int tcphy_get_param(struct device *dev,
+			   struct usb3phy_reg *reg,
+			   const char *name)
+{
+	u32 buffer[3];
+	int ret;
+
+	ret = of_property_read_u32_array(dev->of_node, name, buffer, 3);
+	if (ret) {
+		dev_err(dev, "Can not parse %s\n", name);
+		return ret;
+	}
+
+	reg->offset = buffer[0];
+	reg->enable_bit = buffer[1];
+	reg->write_enable = buffer[2];
+	return 0;
+}
+
+static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
+			  struct device *dev)
+{
+	struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+	int ret;
+
+	ret = tcphy_get_param(dev, &cfg->typec_conn_dir,
+			      "rockchip,typec-conn-dir");
+	if (ret) {
+		dev_err(dev, "could not find rockchip,typec-conn-dir node\n");
+		return ret;
+	}
+
+	ret = tcphy_get_param(dev, &cfg->usb3tousb2_en,
+			      "rockchip,usb3tousb2-en");
+	if (ret) {
+		dev_err(dev, "could not find rockchip,usb3tousb2-en node\n");
+		return ret;
+	}
+
+	ret = tcphy_get_param(dev, &cfg->external_psm,
+			      "rockchip,external-psm");
+	if (ret) {
+		dev_err(dev, "could not find rockchip,external-psm node\n");
+		return ret;
+	}
+
+	ret = tcphy_get_param(dev, &cfg->pipe_status,
+			      "rockchip,pipe-status");
+	if (ret) {
+		dev_err(dev, "could not find rockchip,pipe-status node\n");
+		return ret;
+	}
+
+	ret = tcphy_get_param(dev, &cfg->uphy_dp_sel,
+			      "rockchip,uphy-dp-sel");
+	if (ret) {
+		dev_err(dev, "could not find rockchip,uphy-dp-sel node\n");
+		return ret;
+	}
+
+	tcphy->grf_regs = syscon_regmap_lookup_by_phandle(dev->of_node,
+							  "rockchip,grf");
+	if (IS_ERR(tcphy->grf_regs)) {
+		dev_err(dev, "could not find grf dt node\n");
+		return PTR_ERR(tcphy->grf_regs);
+	}
+
+	tcphy->clk_core = devm_clk_get(dev, "tcpdcore");
+	if (IS_ERR(tcphy->clk_core)) {
+		dev_err(dev, "could not get uphy core clock\n");
+		return PTR_ERR(tcphy->clk_core);
+	}
+
+	tcphy->clk_ref = devm_clk_get(dev, "tcpdphy-ref");
+	if (IS_ERR(tcphy->clk_ref)) {
+		dev_err(dev, "could not get uphy ref clock\n");
+		return PTR_ERR(tcphy->clk_ref);
+	}
+
+	tcphy->uphy_rst = devm_reset_control_get(dev, "uphy");
+	if (IS_ERR(tcphy->uphy_rst)) {
+		dev_err(dev, "no uphy_rst reset control found\n");
+		return PTR_ERR(tcphy->uphy_rst);
+	}
+
+	tcphy->pipe_rst = devm_reset_control_get(dev, "uphy-pipe");
+	if (IS_ERR(tcphy->pipe_rst)) {
+		dev_err(dev, "no pipe_rst reset control found\n");
+		return PTR_ERR(tcphy->pipe_rst);
+	}
+
+	tcphy->tcphy_rst = devm_reset_control_get(dev, "uphy-tcphy");
+	if (IS_ERR(tcphy->tcphy_rst)) {
+		dev_err(dev, "no tcphy_rst reset control found\n");
+		return PTR_ERR(tcphy->tcphy_rst);
+	}
+
+	return 0;
+}
+
+static void typec_phy_pre_init(struct rockchip_typec_phy *tcphy)
+{
+	struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+
+	reset_control_assert(tcphy->tcphy_rst);
+	reset_control_assert(tcphy->uphy_rst);
+	reset_control_assert(tcphy->pipe_rst);
+
+	/* select external psm clock */
+	property_enable(tcphy, &cfg->external_psm, 1);
+	property_enable(tcphy, &cfg->usb3tousb2_en, 0);
+
+	tcphy->mode = MODE_DISCONNECT;
+}
+
+static int rockchip_typec_phy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct rockchip_typec_phy *tcphy;
+	struct phy_provider *phy_provider;
+	struct resource *res;
+	int ret;
+
+	tcphy = devm_kzalloc(dev, sizeof(*tcphy), GFP_KERNEL);
+	if (!tcphy)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	tcphy->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(tcphy->base)) {
+		dev_err(dev, "failed to remap phy regs\n");
+		return PTR_ERR(tcphy->base);
+	}
+
+	ret = tcphy_parse_dt(tcphy, dev);
+	if (ret)
+		return ret;
+
+	tcphy->dev = dev;
+	platform_set_drvdata(pdev, tcphy);
+
+	typec_phy_pre_init(tcphy);
+
+	tcphy->extcon = extcon_get_edev_by_phandle(dev, 0);
+	if (IS_ERR(tcphy->extcon)) {
+		if (PTR_ERR(tcphy->extcon) != -EPROBE_DEFER)
+			dev_err(dev, "Invalid or missing extcon\n");
+		return PTR_ERR(tcphy->extcon);
+	}
+
+	tcphy->phy = devm_phy_create(dev, NULL, &rockchip_tcphy_ops);
+	if (IS_ERR(tcphy->phy)) {
+		dev_err(dev, "failed to create Tepy-C phy\n");
+		return PTR_ERR(tcphy->phy);
+	}
+
+	phy_set_drvdata(tcphy->phy, tcphy);
+
+	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+	if (IS_ERR(phy_provider)) {
+		dev_err(dev, "Failed to register phy provider\n");
+		return PTR_ERR(phy_provider);
+	}
+
+	return 0;
+}
+
+static const struct of_device_id rockchip_typec_phy_dt_ids[] = {
+	{ .compatible = "rockchip,rk3399-typec-phy" },
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, rockchip_typec_phy_dt_ids);
+
+static struct platform_driver rockchip_typec_phy_driver = {
+	.probe		= rockchip_typec_phy_probe,
+	.driver		= {
+		.name	= "rockchip-typec-phy",
+		.of_match_table = rockchip_typec_phy_dt_ids,
+	},
+};
+
+module_platform_driver(rockchip_typec_phy_driver);
+
+MODULE_AUTHOR("Chris Zhong <zyw at rock-chips.com>");
+MODULE_AUTHOR("Kever Yang <kever.yang at rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip USB TYPE-C PHY driver");
+MODULE_LICENSE("GPL v2");
-- 
2.6.3



From zyw at rock-chips.com  Thu Jul 21 06:13:27 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Thu, 21 Jul 2016 21:13:27 +0800
Subject: [v6 PATCH 5/6] Documentation: bindings: add dt documentation for cdn
 DP controller
In-Reply-To: <1469106808-28297-1-git-send-email-zyw@rock-chips.com>
References: <1469106808-28297-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1469106808-28297-6-git-send-email-zyw@rock-chips.com>

This patch adds a binding that describes the cdn DP controller for
rk3399.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
Acked-by: Rob Herring <robh at kernel.org>

---

Changes in v6:
- add assigned-clocks and assigned-clock-rates
- add power-domains

Changes in v5: None
Changes in v4:
- add a reset node
- support 2 phys

Changes in v3:
- add SoC specific compatible string
- remove reg = <1>;

Changes in v2: None
Changes in v1:
- add extcon node description
- add #sound-dai-cells description

 .../bindings/display/rockchip/cdn-dp-rockchip.txt  | 74 ++++++++++++++++++++++
 1 file changed, 74 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/rockchip/cdn-dp-rockchip.txt

diff --git a/Documentation/devicetree/bindings/display/rockchip/cdn-dp-rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/cdn-dp-rockchip.txt
new file mode 100644
index 0000000..af87dcc
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/rockchip/cdn-dp-rockchip.txt
@@ -0,0 +1,74 @@
+Rockchip RK3399 specific extensions to the cdn Display Port
+================================
+
+Required properties:
+- compatible: must be "rockchip,rk3399-cdn-dp"
+
+- reg: physical base address of the controller and length
+
+- clocks: from common clock binding: handle to dp clock.
+
+- clock-names: from common clock binding:
+	       Required elements: "core-clk" "pclk" "spdif"
+
+- resets : a list of phandle + reset specifier pairs
+- reset-names : string reset name, must be:
+		"spdif"
+- power-domains : power-domain property defined with a phandle
+		  to respective power domain.
+- assigned-clocks: main clock, should be <&cru SCLK_DP_CORE>
+- assigned-clock-rates : the DP core clk frequency, shall be: 100000000
+
+- rockchip,grf: this soc should set GRF regs, so need get grf here.
+
+- ports: contain a port nodes with endpoint definitions as defined in
+	 Documentation/devicetree/bindings/media/video-interfaces.txt.
+	 contained 2 endpoints, connecting to the output of vop.
+
+- phys: from general PHY binding: the phandle for the PHY device.
+
+- extcon: extcon specifier for the Power Delivery
+
+- #sound-dai-cells = it must be 1 if your system is using 2 DAIs: I2S, SPDIF
+
+-------------------------------------------------------------------------------
+
+Example:
+	cdn_dp: dp at fec00000 {
+		compatible = "rockchip,rk3399-cdn-dp";
+		reg = <0x0 0xfec00000 0x0 0x100000>;
+		interrupts = <GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru SCLK_DP_CORE>, <&cru PCLK_DP_CTRL>,
+			 <&cru SCLK_SPDIF_REC_DPTX>;
+		clock-names = "core-clk", "pclk", "spdif";
+		assigned-clocks = <&cru SCLK_DP_CORE>;
+		assigned-clock-rates = <100000000>;
+		power-domains = <&power RK3399_PD_HDCP>;
+		phys = <&tcphy0>, <&tcphy1>;
+		resets = <&cru SRST_DPTX_SPDIF_REC>;
+		reset-names = "spdif";
+		extcon = <&fusb0>, <&fusb1>;
+		rockchip,grf = <&grf>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		#sound-dai-cells = <1>;
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			dp_in: port {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				dp_in_vopb: endpoint at 0 {
+					reg = <0>;
+					remote-endpoint = <&vopb_out_dp>;
+				};
+
+				dp_in_vopl: endpoint at 1 {
+					reg = <1>;
+					remote-endpoint = <&vopl_out_dp>;
+				};
+			};
+		};
+	};
-- 
2.6.3



From zyw at rock-chips.com  Thu Jul 21 06:13:28 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Thu, 21 Jul 2016 21:13:28 +0800
Subject: [v6 PATCH 6/6] drm/rockchip: cdn-dp: add cdn DP support for rk3399
In-Reply-To: <1469106808-28297-1-git-send-email-zyw@rock-chips.com>
References: <1469106808-28297-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1469106808-28297-7-git-send-email-zyw@rock-chips.com>

Add support for cdn DP controller which is embedded in the rk3399
SoCs. The DP is compliant with DisplayPort Specification,
Version 1.3, This IP is compatible with the rockchip type-c PHY IP.
There is a uCPU in DP controller, it need a firmware to work,
please put the firmware file to /lib/firmware/cdn/dptx.bin. The
uCPU in charge of aux communication and link training, the host use
mailbox to communicate with the ucpu.
The dclk pin_pol of vop must not be invert for DP.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
Reviewed-by: Sean Paul <seanpaul at chromium.org>

---

Changes in v6:
- add a port struct
- select SND_SOC_HDMI_CODEC
- force reset the phy when hpd detected

Changes in v5:
- alphabetical order
- do not use long, use u32 or u64
- return MODE_CLOCK_HIGH when requested > actual
- Optimized Coding Style
- add a formula to get better tu size and symbol value.
- modify according to Sean Paul's comments
- fixed the fw_wait always 0

Changes in v4:
- use phy framework to control DP phy
- support 2 phys

Changes in v3:
- use EXTCON_DISP_DP and EXTCON_DISP_DP_ALT cable to get dp port state.
- reset spdif before config it
- modify the firmware clk to 100Mhz
- retry load firmware if fw file is requested too early

Changes in v2:
- Alphabetic order
- remove excess error message
- use define clk_rate
- check all return value
- remove dev_set_name(dp->dev, "cdn-dp");
- use schedule_delayed_work
- remove never-called functions
- remove some unnecessary ()

Changes in v1:
- use extcon API
- use hdmi-codec for the DP Asoc
- do not initialize the "ret"
- printk a err log when drm_of_encoder_active_endpoint_id
- modify the dclk pin_pol to a single line

 drivers/gpu/drm/rockchip/Kconfig            |  10 +
 drivers/gpu/drm/rockchip/Makefile           |   1 +
 drivers/gpu/drm/rockchip/cdn-dp-core.c      | 868 ++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/cdn-dp-core.h      | 102 ++++
 drivers/gpu/drm/rockchip/cdn-dp-reg.c       | 904 ++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/cdn-dp-reg.h       | 479 +++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c |  13 +-
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   9 +
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c |   2 +
 9 files changed, 2385 insertions(+), 3 deletions(-)
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.c
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.h
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.c
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.h

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index d30bdc3..20aaafe 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -25,6 +25,16 @@ config ROCKCHIP_ANALOGIX_DP
 	  for the Analogix Core DP driver. If you want to enable DP
 	  on RK3288 based SoC, you should selet this option.
 
+config ROCKCHIP_CDN_DP
+        tristate "Rockchip cdn DP"
+        depends on DRM_ROCKCHIP
+	select SND_SOC_HDMI_CODEC if SND_SOC
+        help
+	  This selects support for Rockchip SoC specific extensions
+	  for the cdn DP driver. If you want to enable Dp on
+	  RK3399 based SoC, you should select this
+	  option.
+
 config ROCKCHIP_DW_HDMI
         tristate "Rockchip specific extensions for Synopsys DW HDMI"
         depends on DRM_ROCKCHIP
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index 05d0713..abdecd5 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -7,6 +7,7 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
 rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
 
 obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
+obj-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
 obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
 obj-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
 obj-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
new file mode 100644
index 0000000..209a7ef
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
@@ -0,0 +1,868 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Chris Zhong <zyw at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_of.h>
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/extcon.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/mfd/syscon.h>
+#include <linux/phy/phy.h>
+
+#include <sound/hdmi-codec.h>
+
+#include "cdn-dp-core.h"
+#include "cdn-dp-reg.h"
+#include "rockchip_drm_vop.h"
+
+#define connector_to_dp(c) \
+		container_of(c, struct cdn_dp_device, connector)
+
+#define encoder_to_dp(c) \
+		container_of(c, struct cdn_dp_device, encoder)
+
+/* dp grf register offset */
+#define DP_VOP_SEL		0x6224
+#define DP_SEL_VOP_LIT		BIT(12)
+#define MAX_FW_WAIT_SECS	64
+#define CDN_DP_FIRMWARE		"rockchip/dptx.bin"
+
+struct cdn_dp_data {
+	u8 max_phy;
+};
+
+struct cdn_dp_data rk3399_cdn_dp = {
+	.max_phy = 2,
+};
+
+static const struct of_device_id cdn_dp_dt_ids[] = {
+	{ .compatible = "rockchip,rk3399-cdn-dp",
+		.data = (void *)&rk3399_cdn_dp },
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, cdn_dp_dt_ids);
+
+static int cdn_dp_clk_enable(struct cdn_dp_device *dp)
+{
+	int ret;
+	u32 rate;
+
+	ret = clk_prepare_enable(dp->pclk);
+	if (ret < 0) {
+		dev_err(dp->dev, "cannot enable dp pclk %d\n", ret);
+		goto err_pclk;
+	}
+
+	ret = clk_prepare_enable(dp->core_clk);
+	if (ret < 0) {
+		dev_err(dp->dev, "cannot enable core_clk %d\n", ret);
+		goto err_core_clk;
+	}
+
+	rate = clk_get_rate(dp->core_clk);
+	if (rate < 0) {
+		dev_err(dp->dev, "get clk rate failed: %d\n", rate);
+		goto err_set_rate;
+	}
+
+	cdn_dp_set_fw_clk(dp, rate);
+
+	return 0;
+
+err_set_rate:
+	clk_disable_unprepare(dp->core_clk);
+err_core_clk:
+	clk_disable_unprepare(dp->pclk);
+err_pclk:
+	return ret;
+}
+
+static enum drm_connector_status
+cdn_dp_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct cdn_dp_device *dp = connector_to_dp(connector);
+
+	return dp->hpd_status;
+}
+
+static void cdn_dp_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+static struct drm_connector_funcs cdn_dp_atomic_connector_funcs = {
+	.dpms = drm_atomic_helper_connector_dpms,
+	.detect = cdn_dp_connector_detect,
+	.destroy = cdn_dp_connector_destroy,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int cdn_dp_connector_get_modes(struct drm_connector *connector)
+{
+	struct cdn_dp_device *dp = connector_to_dp(connector);
+	struct edid *edid;
+	int ret = 0;
+
+	edid = drm_do_get_edid(connector, cdn_dp_get_edid_block, dp);
+	if (edid) {
+		dev_dbg(dp->dev, "got edid: width[%d] x height[%d]\n",
+			edid->width_cm, edid->height_cm);
+
+		dp->sink_has_audio = drm_detect_monitor_audio(edid);
+		ret = drm_add_edid_modes(connector, edid);
+		if (ret) {
+			drm_mode_connector_update_edid_property(connector,
+								edid);
+			drm_edid_to_eld(connector, edid);
+		}
+		kfree(edid);
+	} else {
+		dev_warn(dp->dev, "failed to get edid\n");
+	}
+
+	return ret;
+}
+
+static struct drm_encoder *
+	cdn_dp_connector_best_encoder(struct drm_connector *connector)
+{
+	struct cdn_dp_device *dp = connector_to_dp(connector);
+
+	return &dp->encoder;
+}
+
+static int cdn_dp_connector_mode_valid(struct drm_connector *connector,
+				       struct drm_display_mode *mode)
+{
+	struct cdn_dp_device *dp = connector_to_dp(connector);
+	struct drm_display_info *display_info = &dp->connector.display_info;
+	u32 requested = mode->clock * display_info->bpc * 3 / 1000;
+	u32 sink_max, source_max, rate;
+	u8 lanes, i;
+	u32 actual;
+
+	/* find the running port */
+	for (i = 0; i < dp->ports; i++) {
+		if (dp->port[i]->phy_status == 1) {
+			source_max = dp->port[i]->cap_lanes;
+			break;
+		}
+	}
+
+	sink_max = drm_dp_max_lane_count(dp->dpcd);
+	lanes = min(source_max, sink_max);
+
+	source_max = drm_dp_bw_code_to_link_rate(CDN_DP_MAX_LINK_RATE);
+	sink_max = drm_dp_max_link_rate(dp->dpcd);
+	rate = min(source_max, sink_max);
+
+	actual = rate * lanes / 100;
+
+	/* efficiency is about 0.8 */
+	actual = actual * 8 / 10;
+
+	if (requested > actual) {
+		dev_dbg(dp->dev, "requested=%d, actual=%d, clock=%d\n",
+			requested, actual, mode->clock);
+		return MODE_CLOCK_HIGH;
+	}
+
+	return MODE_OK;
+}
+
+static struct drm_connector_helper_funcs cdn_dp_connector_helper_funcs = {
+	.get_modes = cdn_dp_connector_get_modes,
+	.best_encoder = cdn_dp_connector_best_encoder,
+	.mode_valid = cdn_dp_connector_mode_valid,
+};
+
+static void cdn_dp_commit(struct drm_encoder *encoder)
+{
+	struct cdn_dp_device *dp = encoder_to_dp(encoder);
+	int ret;
+
+	ret = cdn_dp_training_start(dp);
+	if (ret) {
+		dev_err(dp->dev, "link training failed: %d\n", ret);
+		return;
+	}
+
+	ret = cdn_dp_get_training_status(dp);
+	if (ret) {
+		dev_err(dp->dev, "get link training status failed: %d\n", ret);
+		return;
+	}
+
+	dev_info(dp->dev, "rate:%d, lanes:%d\n",
+		 dp->link.rate, dp->link.num_lanes);
+
+	if (cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE))
+		return;
+
+	if (cdn_dp_config_video(dp)) {
+		dev_err(dp->dev, "unable to config video\n");
+		return;
+	}
+
+	if (cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID))
+		return;
+
+	dp->dpms_mode = DRM_MODE_DPMS_ON;
+}
+
+static void cdn_dp_encoder_mode_set(struct drm_encoder *encoder,
+				    struct drm_display_mode *mode,
+				    struct drm_display_mode *adjusted)
+{
+	struct cdn_dp_device *dp = encoder_to_dp(encoder);
+	struct drm_display_info *display_info = &dp->connector.display_info;
+	struct rockchip_crtc_state *state;
+	struct video_info *video = &dp->video_info;
+	int ret, val;
+
+	switch (display_info->bpc) {
+	case 16:
+	case 12:
+	case 10:
+		video->color_depth = 10;
+		break;
+	case 6:
+		video->color_depth = 6;
+		break;
+	default:
+		video->color_depth = 8;
+		break;
+	}
+
+	video->color_fmt = PXL_RGB;
+
+	video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC);
+	video->h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC);
+
+	ret = drm_of_encoder_active_endpoint_id(dp->dev->of_node, encoder);
+	if (ret < 0) {
+		dev_err(dp->dev, "Could not get vop id, %d", ret);
+		return;
+	}
+
+	dev_dbg(dp->dev, "vop %s output to cdn-dp\n", (ret) ? "LIT" : "BIG");
+	state = to_rockchip_crtc_state(encoder->crtc->state);
+	if (ret) {
+		val = DP_SEL_VOP_LIT | (DP_SEL_VOP_LIT << 16);
+		state->output_mode = ROCKCHIP_OUT_MODE_P888;
+	} else {
+		val = DP_SEL_VOP_LIT << 16;
+		state->output_mode = ROCKCHIP_OUT_MODE_AAAA;
+	}
+	ret = regmap_write(dp->grf, DP_VOP_SEL, val);
+	if (ret != 0)
+		dev_err(dp->dev, "Could not write to GRF: %d\n", ret);
+
+	memcpy(&dp->mode, adjusted, sizeof(*mode));
+}
+
+static void cdn_dp_encoder_enable(struct drm_encoder *encoder)
+{
+	struct cdn_dp_device *dp = encoder_to_dp(encoder);
+
+	if (dp->dpms_mode != DRM_MODE_DPMS_ON)
+		cdn_dp_commit(encoder);
+}
+
+static void cdn_dp_encoder_disable(struct drm_encoder *encoder)
+{
+	struct cdn_dp_device *dp = encoder_to_dp(encoder);
+
+	if (dp->dpms_mode != DRM_MODE_DPMS_OFF) {
+		cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE);
+		dp->dpms_mode = DRM_MODE_DPMS_OFF;
+	}
+}
+
+static int
+cdn_dp_encoder_atomic_check(struct drm_encoder *encoder,
+			    struct drm_crtc_state *crtc_state,
+			    struct drm_connector_state *conn_state)
+{
+	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+
+	s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
+	s->output_type = DRM_MODE_CONNECTOR_DisplayPort;
+
+	return 0;
+}
+
+static struct drm_encoder_helper_funcs cdn_dp_encoder_helper_funcs = {
+	.mode_set = cdn_dp_encoder_mode_set,
+	.enable = cdn_dp_encoder_enable,
+	.disable = cdn_dp_encoder_disable,
+	.atomic_check = cdn_dp_encoder_atomic_check,
+};
+
+static struct drm_encoder_funcs cdn_dp_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+static int cdn_dp_firmware_init(struct cdn_dp_device *dp)
+{
+	int ret;
+	const u32 *iram_data, *dram_data;
+	const struct firmware *fw = dp->fw;
+	const struct cdn_firmware_header *hdr;
+
+	hdr = (struct cdn_firmware_header *)fw->data;
+	if (fw->size != le32_to_cpu(hdr->size_bytes)) {
+		dev_err(dp->dev, "firmware is invalid\n");
+		return -EINVAL;
+	}
+
+	iram_data = (const u32 *)(fw->data + hdr->header_size);
+	dram_data = (const u32 *)(fw->data + hdr->header_size + hdr->iram_size);
+
+	ret = cdn_dp_load_firmware(dp, iram_data, hdr->iram_size,
+				   dram_data, hdr->dram_size);
+	if (ret)
+		return ret;
+
+	dp->fw_loaded = 1;
+
+	ret = cdn_dp_active(dp, true);
+	if (ret) {
+		dev_err(dp->dev, "active ucpu failed: %d\n", ret);
+		return ret;
+	}
+
+	return cdn_dp_event_config(dp);
+}
+
+static int cdn_dp_init(struct cdn_dp_device *dp)
+{
+	struct device *dev = dp->dev;
+	struct device_node *np = dev->of_node;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct resource *res;
+	int ret;
+
+	dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+	if (IS_ERR(dp->grf)) {
+		dev_err(dev, "cdn-dp needs rockchip,grf property\n");
+		return PTR_ERR(dp->grf);
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	dp->regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(dp->regs)) {
+		dev_err(dev, "ioremap reg failed\n");
+		return PTR_ERR(dp->regs);
+	}
+
+	dp->core_clk = devm_clk_get(dev, "core-clk");
+	if (IS_ERR(dp->core_clk)) {
+		dev_err(dev, "cannot get core_clk_dp\n");
+		return PTR_ERR(dp->core_clk);
+	}
+
+	dp->pclk = devm_clk_get(dev, "pclk");
+	if (IS_ERR(dp->pclk)) {
+		dev_err(dev, "cannot get pclk\n");
+		return PTR_ERR(dp->pclk);
+	}
+
+	dp->spdif_clk = devm_clk_get(dev, "spdif");
+	if (IS_ERR(dp->spdif_clk)) {
+		dev_err(dev, "cannot get spdif_clk\n");
+		return PTR_ERR(dp->spdif_clk);
+	}
+
+	dp->spdif_rst = devm_reset_control_get(dev, "spdif");
+	if (IS_ERR(dp->spdif_rst)) {
+		dev_err(dev, "no spdif reset control found\n");
+		return PTR_ERR(dp->spdif_rst);
+	}
+
+	dp->dpms_mode = DRM_MODE_DPMS_OFF;
+
+	ret = cdn_dp_clk_enable(dp);
+	if (ret < 0)
+		return ret;
+
+	cdn_dp_clock_reset(dp);
+
+	return 0;
+}
+
+static int cdn_dp_audio_hw_params(struct device *dev,
+				  struct hdmi_codec_daifmt *daifmt,
+				  struct hdmi_codec_params *params)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	struct audio_info audio = {
+		.sample_width = params->sample_width,
+		.sample_rate = params->sample_rate,
+		.channels = params->channels,
+	};
+	int ret;
+
+	if (!dp->encoder.crtc)
+		return -ENODEV;
+
+	switch (daifmt->fmt) {
+	case HDMI_I2S:
+		audio.format = AFMT_I2S;
+		break;
+	case HDMI_SPDIF:
+		audio.format = AFMT_SPDIF;
+		break;
+	default:
+		dev_err(dev, "%s: Invalid format %d\n", __func__, daifmt->fmt);
+		return -EINVAL;
+	}
+
+	ret = cdn_dp_audio_config_set(dp, &audio);
+	if (!ret)
+		dp->audio_info = audio;
+
+	return ret;
+}
+
+static void cdn_dp_audio_shutdown(struct device *dev)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	int ret = cdn_dp_audio_stop(dp, &dp->audio_info);
+
+	if (!ret)
+		dp->audio_info.format = AFMT_UNUSED;
+}
+
+static int cdn_dp_audio_digital_mute(struct device *dev, bool enable)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+
+	return cdn_dp_audio_mute(dp, enable);
+}
+
+static int cdn_dp_audio_get_eld(struct device *dev, uint8_t *buf, size_t len)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	struct drm_mode_config *config = &dp->encoder.dev->mode_config;
+	struct drm_connector *connector;
+	int ret = -ENODEV;
+
+	mutex_lock(&config->mutex);
+	list_for_each_entry(connector, &config->connector_list, head) {
+		if (&dp->encoder == connector->encoder) {
+			memcpy(buf, connector->eld,
+			       min(sizeof(connector->eld), len));
+			ret = 0;
+		}
+	}
+	mutex_unlock(&config->mutex);
+
+	return ret;
+}
+
+static const struct hdmi_codec_ops audio_codec_ops = {
+	.hw_params = cdn_dp_audio_hw_params,
+	.audio_shutdown = cdn_dp_audio_shutdown,
+	.digital_mute = cdn_dp_audio_digital_mute,
+	.get_eld = cdn_dp_audio_get_eld,
+};
+
+static int cdn_dp_audio_codec_init(struct cdn_dp_device *dp,
+				   struct device *dev)
+{
+	struct hdmi_codec_pdata codec_data = {
+		.i2s = 1,
+		.spdif = 1,
+		.ops = &audio_codec_ops,
+		.max_i2s_channels = 8,
+	};
+
+	dp->audio_pdev = platform_device_register_data(
+			 dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO,
+			 &codec_data, sizeof(codec_data));
+
+	return PTR_ERR_OR_ZERO(dp->audio_pdev);
+}
+
+static int cdn_dp_get_cap_lanes(struct cdn_dp_device *dp,
+				struct extcon_dev *edev)
+{
+	bool dfp, dptx;
+	u8 lanes;
+
+	dfp = extcon_get_cable_state_(edev, EXTCON_USB_HOST);
+	dptx = extcon_get_cable_state_(edev, EXTCON_DISP_DP);
+
+	if (dfp && dptx)
+		lanes = 2;
+	else if (dptx)
+		lanes = 4;
+	else
+		lanes = 0;
+
+	return lanes;
+}
+
+static int cdn_dp_phy_power_ctrl(struct cdn_dp_device *dp,
+				 struct cdn_dp_port *port, bool enable)
+{
+	struct phy *phy = port->phy;
+	int ret;
+	int count = 0;
+
+	while (phy->power_count > 0) {
+		ret = phy_power_off(phy);
+		if (ret)
+			return ret;
+		count++;
+	}
+
+	count = enable ? count + 1 : count - 1;
+	while (count-- > 0) {
+		ret = phy_power_on(phy);
+		if (ret)
+			return ret;
+	}
+
+	port->phy_status = enable;
+	return 0;
+}
+
+static int cdn_dp_pd_event(struct notifier_block *nb,
+			   unsigned long event, void *priv)
+{
+	struct cdn_dp_port *port;
+
+	port = container_of(nb, struct cdn_dp_port, event_nb);
+
+	schedule_delayed_work(&port->event_wq, 0);
+
+	return 0;
+}
+
+static void cdn_dp_pd_event_wq(struct work_struct *work)
+{
+	struct cdn_dp_port *port = container_of(work, struct cdn_dp_port,
+						event_wq.work);
+	struct cdn_dp_device *dp = port->dp;
+	u8 new_cap_lanes;
+	int ret;
+	u8 i;
+
+	new_cap_lanes = cdn_dp_get_cap_lanes(dp, port->extcon);
+
+	if (new_cap_lanes == port->cap_lanes) {
+		dev_err(dp->dev, "lanes count does not change: %d\n",
+			new_cap_lanes);
+		return;
+	}
+
+	if (!new_cap_lanes) {
+		ret = cdn_dp_phy_power_ctrl(dp, port, 0);
+		if (ret) {
+			dev_err(dp->dev, "phy power off failed: %d", ret);
+			return;
+		}
+		port->phy_status = 0;
+		port->cap_lanes = new_cap_lanes;
+		for (i = 0; i < dp->ports; i++) {
+			if (dp->port[i]->phy_status == 1)
+				return;
+		}
+
+		dp->hpd_status = connector_status_disconnected;
+		drm_helper_hpd_irq_event(dp->drm_dev);
+		return;
+	}
+
+	/* if other phy is running, do not touch the hpd_status, and return */
+	for (i = 0; i < dp->ports; i++) {
+		if (dp->port[i]->phy_status == 1) {
+			dev_warn(dp->dev, "busy, phy[%d] is running",
+				 dp->port[i]->id);
+			return;
+		}
+	}
+
+	if (!dp->fw_loaded) {
+		ret = request_firmware(&dp->fw, CDN_DP_FIRMWARE, dp->dev);
+		if (ret == -ENOENT && dp->fw_wait <= MAX_FW_WAIT_SECS) {
+			unsigned long time = msecs_to_jiffies(dp->fw_wait * HZ);
+
+			/*
+			 * If can not find the file, retry to load the firmware
+			 * in 1 second, if still failed after 1 minute, give up.
+			 */
+			schedule_delayed_work(&port->event_wq, time);
+			dev_warn(dp->dev, "retry to request firmware in %dS\n",
+				 dp->fw_wait);
+			dp->fw_wait *= 2;
+			return;
+		} else if (ret) {
+			dev_err(dp->dev, "failed to request firmware: %d\n",
+				ret);
+			return;
+		}
+	}
+
+	ret = cdn_dp_phy_power_ctrl(dp, port, 1);
+	if (ret) {
+		if (!dp->fw_loaded)
+			release_firmware(dp->fw);
+		dev_err(dp->dev, "phy power on failed: %d\n", ret);
+		return;
+	}
+
+	if (!dp->fw_loaded) {
+		ret = cdn_dp_firmware_init(dp);
+		release_firmware(dp->fw);
+		if (ret) {
+			dev_err(dp->dev, "firmware init failed: %d", ret);
+			goto err_firmware;
+		}
+	}
+
+	/* read hpd status failed, or the hpd does not exist. */
+	ret = cdn_dp_get_hpd_status(dp);
+	if (ret <= 0) {
+		dev_err(dp->dev, "get hpd failed: %d", ret);
+		goto err_firmware;
+	}
+
+	ret = cdn_dp_set_host_cap(dp, new_cap_lanes);
+	if (ret) {
+		dev_err(dp->dev, "set host capabilities failed: %d\n", ret);
+		goto err_firmware;
+	}
+
+	/*
+	 * Native read with retry for link status and receiver capability reads
+	 * for cases where the sink may still be asleep.
+	 *
+	 * Sinks are *supposed* to come up within 1ms from an off state, but
+	 * we're also supposed to retry 3 times per the spec.
+	 */
+	for (i = 0; i < 3; i++) {
+		ret = cdn_dp_dpcd_read(dp, 0x000, dp->dpcd,
+				       DP_RECEIVER_CAP_SIZE);
+		if (!ret) {
+			port->cap_lanes = new_cap_lanes;
+			dp->hpd_status = connector_status_connected;
+			drm_helper_hpd_irq_event(dp->drm_dev);
+			return;
+		}
+		usleep_range(1000, 1050);
+	}
+
+	dev_err(dp->dev, "get dpcd failed: %d", ret);
+
+err_firmware:
+	ret = cdn_dp_phy_power_ctrl(dp, port, 0);
+	if (ret)
+		dev_err(dp->dev, "phy power off failed: %d", ret);
+}
+
+static int cdn_dp_bind(struct device *dev, struct device *master,
+		       void *data)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	struct drm_encoder *encoder;
+	struct drm_connector *connector;
+	struct cdn_dp_port *port;
+	struct drm_device *drm_dev = data;
+	int ret, i;
+
+	ret = cdn_dp_init(dp);
+	if (ret < 0)
+		return ret;
+
+	dp->drm_dev = drm_dev;
+	dp->hpd_status = connector_status_disconnected;
+	dp->fw_wait = 1;
+
+	encoder = &dp->encoder;
+
+	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
+							     dev->of_node);
+	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
+
+	ret = drm_encoder_init(drm_dev, encoder, &cdn_dp_encoder_funcs,
+			       DRM_MODE_ENCODER_TMDS, NULL);
+	if (ret) {
+		DRM_ERROR("failed to initialize encoder with drm\n");
+		return ret;
+	}
+
+	drm_encoder_helper_add(encoder, &cdn_dp_encoder_helper_funcs);
+
+	connector = &dp->connector;
+	connector->polled = DRM_CONNECTOR_POLL_HPD;
+	connector->dpms = DRM_MODE_DPMS_OFF;
+
+	ret = drm_connector_init(drm_dev, connector,
+				 &cdn_dp_atomic_connector_funcs,
+				 DRM_MODE_CONNECTOR_DisplayPort);
+	if (ret) {
+		DRM_ERROR("failed to initialize connector with drm\n");
+		goto err_free_encoder;
+	}
+
+	drm_connector_helper_add(connector, &cdn_dp_connector_helper_funcs);
+
+	ret = drm_mode_connector_attach_encoder(connector, encoder);
+	if (ret) {
+		DRM_ERROR("failed to attach connector and encoder\n");
+		goto err_free_connector;
+	}
+
+	cdn_dp_audio_codec_init(dp, dev);
+
+
+	for (i = 0; i < dp->ports; i++) {
+		port = dp->port[i];
+
+		port->event_nb.notifier_call = cdn_dp_pd_event;
+		INIT_DELAYED_WORK(&port->event_wq, cdn_dp_pd_event_wq);
+		ret = extcon_register_notifier(port->extcon, EXTCON_DISP_DP,
+					       &port->event_nb);
+		if (ret) {
+			dev_err(dev, "regitster EXTCON_DISP_DP notifier err\n");
+			return ret;
+		}
+
+		if (extcon_get_cable_state_(port->extcon, EXTCON_DISP_DP))
+			schedule_delayed_work(&port->event_wq, 0);
+	}
+
+	return 0;
+
+err_free_connector:
+	drm_connector_cleanup(connector);
+err_free_encoder:
+	drm_encoder_cleanup(encoder);
+	return ret;
+}
+
+static void cdn_dp_unbind(struct device *dev, struct device *master, void *data)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	struct drm_encoder *encoder = &dp->encoder;
+	struct drm_connector *connector = &dp->connector;
+	struct cdn_dp_port *port;
+	int i;
+
+	platform_device_unregister(dp->audio_pdev);
+	cdn_dp_encoder_disable(encoder);
+	encoder->funcs->destroy(encoder);
+	connector->funcs->destroy(connector);
+
+	for (i = 0; i < dp->ports; i++) {
+		port = dp->port[i];
+		extcon_unregister_notifier(port->extcon, EXTCON_DISP_DP,
+					   &port->event_nb);
+	}
+}
+
+static const struct component_ops cdn_dp_component_ops = {
+	.bind = cdn_dp_bind,
+	.unbind = cdn_dp_unbind,
+};
+
+static int cdn_dp_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	const struct of_device_id *match;
+	struct cdn_dp_data *dp_data;
+	struct cdn_dp_port *port;
+	struct cdn_dp_device *dp;
+	struct extcon_dev *extcon;
+	struct phy *phy;
+	int i;
+
+	dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
+	if (!dp)
+		return -ENOMEM;
+	dp->dev = dev;
+
+	match = of_match_node(cdn_dp_dt_ids, pdev->dev.of_node);
+	dp_data = (struct cdn_dp_data *)match->data;
+
+	for (i = 0; i < dp_data->max_phy; i++) {
+		extcon = extcon_get_edev_by_phandle(dev, i);
+		phy = devm_of_phy_get_by_index(dev, dev->of_node, i);
+
+		if (PTR_ERR(extcon) == -EPROBE_DEFER ||
+		    PTR_ERR(phy) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+
+		if (IS_ERR(extcon) || IS_ERR(phy))
+			continue;
+
+		port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+		if (!dp)
+			return -ENOMEM;
+
+		port->extcon = extcon;
+		port->phy = phy;
+		port->dp = dp;
+		port->id = i;
+		dp->port[dp->ports++] = port;
+	}
+
+	if (!dp->ports) {
+		dev_err(dev, "missing extcon or phy\n");
+		return -EINVAL;
+	}
+
+	dev_set_drvdata(dev, dp);
+
+	return component_add(dev, &cdn_dp_component_ops);
+}
+
+static int cdn_dp_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &cdn_dp_component_ops);
+
+	return 0;
+}
+
+static struct platform_driver cdn_dp_driver = {
+	.probe = cdn_dp_probe,
+	.remove = cdn_dp_remove,
+	.driver = {
+		   .name = "cdn-dp",
+		   .owner = THIS_MODULE,
+		   .of_match_table = of_match_ptr(cdn_dp_dt_ids),
+	},
+};
+
+module_platform_driver(cdn_dp_driver);
+
+MODULE_AUTHOR("Chris Zhong <zyw at rock-chips.com>");
+MODULE_DESCRIPTION("cdn DP Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
new file mode 100644
index 0000000..6eb7b72
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2016 Chris Zhong <zyw at rock-chips.com>
+ * Copyright (C) 2016 ROCKCHIP, Inc.
+ *
+ * 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.
+ */
+
+#ifndef _CDN_DP_CORE_H
+#define _CDN_DP_CORE_H
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_panel.h>
+#include "rockchip_drm_drv.h"
+
+#define MAX_PHY		2
+
+enum audio_format {
+	AFMT_I2S = 0,
+	AFMT_SPDIF = 1,
+	AFMT_UNUSED,
+};
+
+struct audio_info {
+	enum audio_format format;
+	int sample_rate;
+	int channels;
+	int sample_width;
+};
+
+enum vic_pxl_encoding_format {
+	PXL_RGB = 0x1,
+	YCBCR_4_4_4 = 0x2,
+	YCBCR_4_2_2 = 0x4,
+	YCBCR_4_2_0 = 0x8,
+	Y_ONLY = 0x10,
+};
+
+struct video_info {
+	bool h_sync_polarity;
+	bool v_sync_polarity;
+	bool interlaced;
+	int color_depth;
+	enum vic_pxl_encoding_format color_fmt;
+};
+
+struct cdn_firmware_header {
+	u32 size_bytes; /* size of the entire header+image(s) in bytes */
+	u32 header_size; /* size of just the header in bytes */
+	u32 iram_size; /* size of iram */
+	u32 dram_size; /* size of dram */
+};
+
+struct cdn_dp_port {
+	struct cdn_dp_device *dp;
+	struct notifier_block event_nb;
+	struct delayed_work event_wq;
+	struct extcon_dev *extcon;
+	struct phy *phy;
+	u8 cap_lanes;
+	bool phy_status;
+	u8 id;
+};
+
+struct cdn_dp_device {
+	struct device *dev;
+	struct drm_device *drm_dev;
+	struct drm_connector connector;
+	struct drm_encoder encoder;
+	struct drm_display_mode mode;
+	struct platform_device *audio_pdev;
+
+	const struct firmware *fw;	/* cdn dp firmware */
+	unsigned int fw_version;	/* cdn fw version */
+	u32 fw_wait;
+	bool fw_loaded;
+	void __iomem *regs;
+	struct regmap *grf;
+	struct clk *core_clk;
+	struct clk *pclk;
+	struct clk *spdif_clk;
+	struct reset_control *spdif_rst;
+	struct audio_info audio_info;
+	struct video_info video_info;
+	struct drm_dp_link link;
+	struct cdn_dp_port *port[MAX_PHY];
+	u8 ports;
+
+	u8 dpcd[DP_RECEIVER_CAP_SIZE];
+	enum drm_connector_status hpd_status;
+	int dpms_mode;
+	bool sink_has_audio;
+};
+#endif  /* _CDN_DP_CORE_H */
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
new file mode 100644
index 0000000..a224a6b
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
@@ -0,0 +1,904 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Chris Zhong <zyw at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/clk.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/reset.h>
+
+#include "cdn-dp-core.h"
+#include "cdn-dp-reg.h"
+
+#define CDN_DP_SPDIF_CLK		200000000
+#define FW_ALIVE_TIMEOUT_US		1000000
+#define MAILBOX_RETRY_US		1000
+#define MAILBOX_TIMEOUT_US		5000000
+#define LINK_TRAINING_RETRY_MS		20
+#define LINK_TRAINING_TIMEOUT_MS	500
+
+void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, u32 clk)
+{
+	writel(clk / 1000000, dp->regs + SW_CLK_H);
+}
+
+void cdn_dp_clock_reset(struct cdn_dp_device *dp)
+{
+	u32 val;
+
+	val = DPTX_FRMR_DATA_CLK_RSTN_EN |
+	      DPTX_FRMR_DATA_CLK_EN |
+	      DPTX_PHY_DATA_RSTN_EN |
+	      DPTX_PHY_DATA_CLK_EN |
+	      DPTX_PHY_CHAR_RSTN_EN |
+	      DPTX_PHY_CHAR_CLK_EN |
+	      SOURCE_AUX_SYS_CLK_RSTN_EN |
+	      SOURCE_AUX_SYS_CLK_EN |
+	      DPTX_SYS_CLK_RSTN_EN |
+	      DPTX_SYS_CLK_EN |
+	      CFG_DPTX_VIF_CLK_RSTN_EN |
+	      CFG_DPTX_VIF_CLK_EN;
+	writel(val, dp->regs + SOURCE_DPTX_CAR);
+
+	val = SOURCE_PHY_RSTN_EN | SOURCE_PHY_CLK_EN;
+	writel(val, dp->regs + SOURCE_PHY_CAR);
+
+	val = SOURCE_PKT_SYS_RSTN_EN |
+	      SOURCE_PKT_SYS_CLK_EN |
+	      SOURCE_PKT_DATA_RSTN_EN |
+	      SOURCE_PKT_DATA_CLK_EN;
+	writel(val, dp->regs + SOURCE_PKT_CAR);
+
+	val = SPDIF_CDR_CLK_RSTN_EN |
+	      SPDIF_CDR_CLK_EN |
+	      SOURCE_AIF_SYS_RSTN_EN |
+	      SOURCE_AIF_SYS_CLK_EN |
+	      SOURCE_AIF_CLK_RSTN_EN |
+	      SOURCE_AIF_CLK_EN;
+	writel(val, dp->regs + SOURCE_AIF_CAR);
+
+	val = SOURCE_CIPHER_SYSTEM_CLK_RSTN_EN |
+	      SOURCE_CIPHER_SYS_CLK_EN |
+	      SOURCE_CIPHER_CHAR_CLK_RSTN_EN |
+	      SOURCE_CIPHER_CHAR_CLK_EN;
+	writel(val, dp->regs + SOURCE_CIPHER_CAR);
+
+	val = SOURCE_CRYPTO_SYS_CLK_RSTN_EN |
+	      SOURCE_CRYPTO_SYS_CLK_EN;
+	writel(val, dp->regs + SOURCE_CRYPTO_CAR);
+
+	val = ~(MAILBOX_INT_MASK_BIT | PIF_INT_MASK_BIT) & ALL_INT_MASK;
+	writel(val, dp->regs + APB_INT_MASK);
+}
+
+static int cdn_dp_mailbox_read(struct cdn_dp_device *dp)
+{
+	int val, ret;
+
+	if (!dp->fw_loaded)
+		return -EPERM;
+
+	ret = readx_poll_timeout(readl, dp->regs + MAILBOX_EMPTY_ADDR,
+				 val, !val, MAILBOX_RETRY_US,
+				 MAILBOX_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(dp->dev, "failed to read mailbox, keep alive = %x\n",
+			readl(dp->regs + KEEP_ALIVE));
+		return ret;
+	}
+
+	return readl(dp->regs + MAILBOX0_RD_DATA) & 0xff;
+}
+
+static int cdp_dp_mailbox_write(struct cdn_dp_device *dp, u8 val)
+{
+	int ret, full;
+
+	if (!dp->fw_loaded)
+		return -EPERM;
+
+	ret = readx_poll_timeout(readl, dp->regs + MAILBOX_FULL_ADDR,
+				 full, !full, MAILBOX_RETRY_US,
+				 MAILBOX_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(dp->dev, "mailbox is full, keep alive = %x\n",
+			readl(dp->regs + KEEP_ALIVE));
+		return ret;
+	}
+
+	writel(val, dp->regs + MAILBOX0_WR_DATA);
+
+	return 0;
+}
+
+static int cdn_dp_mailbox_validate_receive(struct cdn_dp_device *dp,
+					   u8 module_id, u8 opcode,
+					   u8 req_size)
+{
+	u32 mbox_size, i;
+	u8 header[4];
+	int ret;
+
+	/* read the header of the message */
+	for (i = 0; i < 4; i++)  {
+		ret = cdn_dp_mailbox_read(dp);
+		if (ret < 0)
+			return ret;
+
+		header[i] = ret;
+	}
+
+	mbox_size = (header[2] << 8) | header[3];
+
+	if (opcode != header[0] || module_id != header[1] ||
+	    req_size != mbox_size) {
+		dev_err(dp->dev, "mailbox validate failed");
+
+		/*
+		 * If the message in mailbox is not what we want, we need to
+		 * clear the mailbox by read.
+		 */
+		for (i = 0; i < mbox_size; i++)
+			if (cdn_dp_mailbox_read(dp) < 0)
+				break;
+
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int cdn_dp_mailbox_read_receive(struct cdn_dp_device *dp,
+				       u8 *buff, u8 buff_size)
+{
+	u32 i;
+	int ret;
+
+	for (i = 0; i < buff_size; i++) {
+		ret = cdn_dp_mailbox_read(dp);
+		if (ret < 0)
+			return ret;
+
+		buff[i] = ret;
+	}
+
+	return 0;
+}
+
+static int cdn_dp_mailbox_send(struct cdn_dp_device *dp, u8 module_id,
+			       u8 opcode, u16 size, u8 *message)
+{
+	u8 header[4];
+	int ret, i;
+
+	header[0] = opcode;
+	header[1] = module_id;
+	header[2] = (size >> 8) & 0xff;
+	header[3] = size & 0xff;
+
+	for (i = 0; i < 4; i++) {
+		ret = cdp_dp_mailbox_write(dp, header[i]);
+		if (ret)
+			return ret;
+	}
+
+	for (i = 0; i < size; i++) {
+		ret = cdp_dp_mailbox_write(dp, message[i]);
+		if (ret) {
+			dev_err(dp->dev, "mailbox send failed: %d", ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
+{
+	u8 msg[6];
+
+	msg[0] = (addr >> 8) & 0xff;
+	msg[1] = addr & 0xff;
+	msg[2] = (val >> 24) & 0xff;
+	msg[3] = (val >> 16) & 0xff;
+	msg[4] = (val >> 8) & 0xff;
+	msg[5] = val & 0xff;
+	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_REGISTER,
+				   sizeof(msg), msg);
+}
+
+int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
+{
+	u8 msg[5], reg[5];
+	int ret;
+
+	msg[0] = (len >> 8) & 0xff;
+	msg[1] = len & 0xff;
+	msg[2] = (addr >> 16) & 0xff;
+	msg[3] = (addr >> 8) & 0xff;
+	msg[4] = addr & 0xff;
+	cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_READ_DPCD,
+			    sizeof(msg), msg);
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_READ_DPCD,
+					      sizeof(reg) + len);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg));
+	if (ret)
+		return ret;
+
+	return cdn_dp_mailbox_read_receive(dp, data, len);
+}
+
+int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
+{
+	u8 msg[6], reg[5];
+	int ret;
+
+	msg[0] = 0;
+	msg[1] = 1;
+	msg[2] = (addr >> 16) & 0xff;
+	msg[3] = (addr >> 8) & 0xff;
+	msg[4] = addr & 0xff;
+	msg[5] = value;
+	cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_DPCD,
+			    sizeof(msg), msg);
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_WRITE_DPCD, sizeof(reg));
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg));
+	if (ret)
+		return ret;
+
+	if (addr != (reg[2] << 16 | reg[3] << 8 | reg[4])) {
+		dev_err(dp->dev, "write dpcd failed\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
+			 u32 i_size, const u32 *d_mem, u32 d_size)
+{
+	int i, ret;
+	u32 reg;
+
+	/* reset ucpu before load firmware*/
+	writel(APB_IRAM_PATH | APB_DRAM_PATH | APB_XT_RESET,
+	       dp->regs + APB_CTRL);
+
+	for (i = 0; i < i_size; i += 4)
+		writel(*i_mem++, dp->regs + ADDR_IMEM + i);
+
+	for (i = 0; i < d_size; i += 4)
+		writel(*d_mem++, dp->regs + ADDR_DMEM + i);
+
+	/* un-reset ucpu */
+	writel(0, dp->regs + APB_CTRL);
+
+	/* check the keep alive register to make sure fw working */
+	ret = readx_poll_timeout(readl, dp->regs + KEEP_ALIVE,
+				 reg, reg, 2000, FW_ALIVE_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(dp->dev, "failed to loaded the FW reg = %x\n", reg);
+		return -EINVAL;
+	}
+
+	reg = readl(dp->regs + VER_L) & 0xff;
+	dp->fw_version = reg;
+	reg = readl(dp->regs + VER_H) & 0xff;
+	dp->fw_version |= reg << 8;
+	reg = readl(dp->regs + VER_LIB_L_ADDR) & 0xff;
+	dp->fw_version |= reg << 16;
+	reg = readl(dp->regs + VER_LIB_H_ADDR) & 0xff;
+	dp->fw_version |= reg << 24;
+
+	dev_dbg(dp->dev, "firmware version: %x\n", dp->fw_version);
+
+	return 0;
+}
+
+int cdn_dp_reg_write_bit(struct cdn_dp_device *dp, u16 addr, u8 start_bit,
+			 u8 bits_no, u32 val)
+{
+	u8 field[8];
+
+	field[0] = (addr >> 8) & 0xff;
+	field[1] = addr & 0xff;
+	field[2] = start_bit;
+	field[3] = bits_no;
+	field[4] = (val >> 24) & 0xff;
+	field[5] = (val >> 16) & 0xff;
+	field[6] = (val >> 8) & 0xff;
+	field[7] = val & 0xff;
+
+	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_FIELD,
+				   sizeof(field), field);
+}
+
+int cdn_dp_active(struct cdn_dp_device *dp, bool enable)
+{
+	u8 active = enable ? 1 : 0;
+	u8 resp;
+	int ret;
+
+	/* set firmware status, 1: active; 0: standby */
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_GENERAL,
+				  GENERAL_MAIN_CONTROL, sizeof(active),
+				  &active);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_GENERAL,
+					      GENERAL_MAIN_CONTROL,
+					      sizeof(resp));
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, &resp, sizeof(resp));
+	if (ret)
+		return ret;
+
+	return resp ? 0 : -EINVAL;
+}
+
+int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes)
+{
+	u8 msg[8];
+	int ret;
+
+	msg[0] = CDN_DP_MAX_LINK_RATE;
+	msg[1] = lanes;
+	msg[2] = VOLTAGE_LEVEL_2;
+	msg[3] = PRE_EMPHASIS_LEVEL_3;
+	msg[4] = PTS1 | PTS2 | PTS3 | PTS4;
+	msg[5] = FAST_LT_NOT_SUPPORT;
+	msg[6] = LANE_MAPPING_NORMAL;
+	msg[7] = ENHANCED;
+
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
+				  DPTX_SET_HOST_CAPABILITIES,
+				  sizeof(msg), msg);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_reg_write(dp, DP_AUX_SWAP_INVERSION_CONTROL,
+			       AUX_HOST_INVERT);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+int cdn_dp_event_config(struct cdn_dp_device *dp)
+{
+	u8 msg[5];
+
+	memset(msg, 0, sizeof(msg));
+
+	msg[0] = DPTX_EVENT_ENABLE_HPD | DPTX_EVENT_ENABLE_TRAINING;
+
+	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_ENABLE_EVENT,
+				   sizeof(msg), msg);
+}
+
+u32 cdn_dp_get_event(struct cdn_dp_device *dp)
+{
+	return readl(dp->regs + SW_EVENTS0);
+}
+
+int cdn_dp_get_hpd_status(struct cdn_dp_device *dp)
+{
+	u8 status;
+	int ret;
+
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_HPD_STATE,
+				  0, NULL);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_HPD_STATE, sizeof(status));
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, &status, sizeof(status));
+	if (ret)
+		return ret;
+
+	return status;
+}
+
+int cdn_dp_get_edid_block(void *data, u8 *edid,
+			  unsigned int block, size_t length)
+{
+	struct cdn_dp_device *dp = data;
+	u8 msg[2], reg[2];
+	int ret;
+
+	msg[0] = block / 2;
+	msg[1] = block % 2;
+
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_GET_EDID,
+				  sizeof(msg), msg);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_GET_EDID,
+					      sizeof(reg) + length);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg));
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, edid, length);
+	if (ret)
+		return ret;
+
+	if (reg[0] != length || reg[1] != block / 2) {
+		dev_err(dp->dev, "edid block size err\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int cdn_dp_training_start(struct cdn_dp_device *dp)
+{
+	u8 msg, event[2];
+	unsigned long timeout;
+	int ret;
+
+	msg = LINK_TRAINING_RUN;
+
+	/* start training */
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_TRAINING_CONTROL,
+				  sizeof(msg), &msg);
+	if (ret)
+		return ret;
+
+	timeout = jiffies + msecs_to_jiffies(LINK_TRAINING_TIMEOUT_MS);
+	while (time_before(jiffies, timeout)) {
+		msleep(LINK_TRAINING_RETRY_MS);
+		ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
+					  DPTX_READ_EVENT, 0, NULL);
+		if (ret)
+			return ret;
+
+		ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+						      DPTX_READ_EVENT,
+						      sizeof(event));
+		if (ret)
+			return ret;
+
+		ret = cdn_dp_mailbox_read_receive(dp, event, sizeof(event));
+		if (ret)
+			return ret;
+
+		if (event[1] & EQ_PHASE_FINISHED)
+			return 0;
+	}
+
+	return -ETIMEDOUT;
+}
+
+int cdn_dp_get_training_status(struct cdn_dp_device *dp)
+{
+	u8 status[10];
+	int ret;
+
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_READ_LINK_STAT,
+				  0, NULL);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_READ_LINK_STAT,
+					      sizeof(status));
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, status, sizeof(status));
+	if (ret)
+		return ret;
+
+	dp->link.rate = status[0];
+	dp->link.num_lanes = status[1];
+
+	return 0;
+}
+
+int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active)
+{
+	u8 msg;
+
+	msg = !!active;
+
+	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_SET_VIDEO,
+				   sizeof(msg), &msg);
+}
+
+static int cdn_dp_get_msa_misc(struct video_info *video,
+			       struct drm_display_mode *mode)
+{
+	u8 val[2];
+	u32 msa_misc;
+
+	switch (video->color_fmt) {
+	case PXL_RGB:
+	case Y_ONLY:
+		val[0] = 0;
+		break;
+	/* set YUV default color space conversion to BT601 */
+	case YCBCR_4_4_4:
+		val[0] = 6 + BT_601 * 8;
+		break;
+	case YCBCR_4_2_2:
+		val[0] = 5 + BT_601 * 8;
+		break;
+	case YCBCR_4_2_0:
+		val[0] = 5;
+		break;
+	};
+
+	switch (video->color_depth) {
+	case 6:
+		val[1] = 0;
+		break;
+	case 8:
+		val[1] = 1;
+		break;
+	case 10:
+		val[1] = 2;
+		break;
+	case 12:
+		val[1] = 3;
+		break;
+	case 16:
+		val[1] = 4;
+		break;
+	};
+
+	msa_misc = 2 * val[0] + 32 * val[1] +
+		   ((video->color_fmt == Y_ONLY) ? (1 << 14) : 0);
+
+	return msa_misc;
+}
+
+int cdn_dp_config_video(struct cdn_dp_device *dp)
+{
+	struct video_info *video = &dp->video_info;
+	struct drm_display_mode *mode = &dp->mode;
+	u64 symbol;
+	u32 val, link_rate;
+	u8 bit_per_pix;
+	u8 tu_size_reg = TU_SIZE;
+	int ret;
+
+	bit_per_pix = (video->color_fmt == YCBCR_4_2_2) ?
+		      (video->color_depth * 2) : (video->color_depth * 3);
+
+	link_rate = drm_dp_bw_code_to_link_rate(dp->link.rate);
+
+	val = VIF_BYPASS_INTERLACE;
+	ret = cdn_dp_reg_write(dp, BND_HSYNC2VSYNC, val);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_reg_write(dp, HSYNC2VSYNC_POL_CTRL, 0);
+	if (ret)
+		return ret;
+
+	/* get a best tu_size and symbol */
+	do {
+		tu_size_reg += 2;
+		symbol = tu_size_reg * mode->clock * bit_per_pix;
+		symbol /= dp->link.num_lanes * link_rate * 8;
+	} while ((symbol == 1) || (tu_size_reg - symbol < 4));
+
+	val = symbol + (tu_size_reg << 8);
+	ret = cdn_dp_reg_write(dp, DP_FRAMER_TU, val);
+	if (ret)
+		return ret;
+
+	/* set the FIFO Buffer size */
+	val = ((mode->clock * (symbol + 1) / 1000) + link_rate);
+	val /= (dp->link.num_lanes * link_rate);
+	val = 8 * (symbol + 1) / bit_per_pix - val;
+	val += 2;
+	ret = cdn_dp_reg_write(dp, DP_VC_TABLE(15), val);
+
+	switch (video->color_depth) {
+	case 6:
+		val = BCS_6;
+		break;
+	case 8:
+		val = BCS_8;
+		break;
+	case 10:
+		val = BCS_10;
+		break;
+	case 12:
+		val = BCS_12;
+		break;
+	case 16:
+		val = BCS_16;
+		break;
+	};
+
+	val += video->color_fmt << 8;
+	ret = cdn_dp_reg_write(dp, DP_FRAMER_PXL_REPR, val);
+	if (ret)
+		return ret;
+
+	val = video->h_sync_polarity ? DP_FRAMER_SP_HSP : 0;
+	val |= video->v_sync_polarity ? DP_FRAMER_SP_VSP : 0;
+	ret = cdn_dp_reg_write(dp, DP_FRAMER_SP, val);
+	if (ret)
+		return ret;
+
+	val = (mode->hsync_start - mode->hdisplay) << 16;
+	val |= mode->htotal - mode->hsync_end;
+	ret = cdn_dp_reg_write(dp, DP_FRONT_BACK_PORCH, val);
+	if (ret)
+		return ret;
+
+	val = mode->hdisplay * bit_per_pix / 8;
+	ret = cdn_dp_reg_write(dp, DP_BYTE_COUNT, val);
+	if (ret)
+		return ret;
+
+	val = mode->htotal | ((mode->htotal - mode->hsync_start) << 16);
+	ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_0, val);
+	if (ret)
+		return ret;
+
+	val = mode->hsync_end - mode->hsync_start;
+	val |= (mode->hdisplay << 16) | (video->h_sync_polarity << 15);
+	ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_1, val);
+	if (ret)
+		return ret;
+
+	val = mode->vtotal;
+	val |= ((mode->vtotal - mode->vsync_start) << 16);
+	ret = cdn_dp_reg_write(dp, MSA_VERTICAL_0, val);
+	if (ret)
+		return ret;
+
+	val = mode->vsync_end - mode->vsync_start;
+	val |= mode->vdisplay << 16 | (video->v_sync_polarity << 15);
+	ret = cdn_dp_reg_write(dp, MSA_VERTICAL_1, val);
+	if (ret)
+		return ret;
+
+	val = cdn_dp_get_msa_misc(video, mode);
+	ret = cdn_dp_reg_write(dp, MSA_MISC, val);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_reg_write(dp, STREAM_CONFIG, 1);
+	if (ret)
+		return ret;
+
+	val = mode->hsync_end - mode->hsync_start;
+	val |= (mode->hdisplay << 16);
+	ret = cdn_dp_reg_write(dp, DP_HORIZONTAL, val);
+	if (ret)
+		return ret;
+
+	val = mode->vtotal;
+	val -= (mode->vtotal - mode->vdisplay);
+	val |= (mode->vtotal - mode->vsync_start) << 16;
+	ret = cdn_dp_reg_write(dp, DP_VERTICAL_0, val);
+	if (ret)
+		return ret;
+
+	val = mode->vtotal;
+	ret = cdn_dp_reg_write(dp, DP_VERTICAL_1, val);
+	if (ret)
+		return ret;
+
+	val =  0;
+	return cdn_dp_reg_write_bit(dp, DP_VB_ID, 2, 1, val);
+}
+
+int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio)
+{
+	u32 val;
+	int ret;
+
+	ret = cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, 0);
+	if (ret)
+		return ret;
+
+	val = SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS;
+	val |= SPDIF_FIFO_MID_RANGE(0xe0);
+	val |= SPDIF_JITTER_THRSH(0xe0);
+	val |= SPDIF_JITTER_AVG_WIN(7);
+	writel(val, dp->regs + SPDIF_CTRL_ADDR);
+
+	/* clearn the audio config and reset */
+	writel(0, dp->regs + AUDIO_SRC_CNTL);
+	writel(0, dp->regs + AUDIO_SRC_CNFG);
+	writel(AUDIO_SW_RST, dp->regs + AUDIO_SRC_CNTL);
+	writel(0, dp->regs + AUDIO_SRC_CNTL);
+
+	/* reset smpl2pckt component  */
+	writel(0, dp->regs + SMPL2PKT_CNTL);
+	writel(AUDIO_SW_RST, dp->regs + SMPL2PKT_CNTL);
+	writel(0, dp->regs + SMPL2PKT_CNTL);
+
+	/* reset FIFO */
+	writel(AUDIO_SW_RST, dp->regs + FIFO_CNTL);
+	writel(0, dp->regs + FIFO_CNTL);
+
+	if (audio->format == AFMT_SPDIF)
+		clk_disable_unprepare(dp->spdif_clk);
+
+	return 0;
+}
+
+int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable)
+{
+	return cdn_dp_reg_write_bit(dp, DP_VB_ID, 4, 1, enable);
+}
+
+static void cdn_dp_audio_config_i2s(struct cdn_dp_device *dp,
+				    struct audio_info *audio)
+{
+	int sub_pckt_num = 1, i2s_port_en_val = 0xf, i;
+	u32 val;
+
+	if (audio->channels == 2) {
+		if (dp->link.num_lanes == 1)
+			sub_pckt_num = 2;
+		else
+			sub_pckt_num = 4;
+
+		i2s_port_en_val = 1;
+	} else if (audio->channels == 4) {
+		i2s_port_en_val = 3;
+	}
+
+	writel(0x0, dp->regs + SPDIF_CTRL_ADDR);
+
+	writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL);
+
+	val = MAX_NUM_CH(audio->channels);
+	val |= NUM_OF_I2S_PORTS(audio->channels);
+	val |= AUDIO_TYPE_LPCM;
+	val |= CFG_SUB_PCKT_NUM(sub_pckt_num);
+	writel(val, dp->regs + SMPL2PKT_CNFG);
+
+	if (audio->sample_width == 16)
+		val = 0;
+	else if (audio->sample_width == 24)
+		val = 1 << 9;
+	else
+		val = 2 << 9;
+
+	val |= AUDIO_CH_NUM(audio->channels);
+	val |= I2S_DEC_PORT_EN(i2s_port_en_val);
+	val |= TRANS_SMPL_WIDTH_32;
+	writel(val, dp->regs + AUDIO_SRC_CNFG);
+
+	for (i = 0; i < (audio->channels + 1) / 2; i++) {
+		if (audio->sample_width == 16)
+			val = (0x08 << 8) | (0x08 << 20);
+		else if (audio->sample_width == 24)
+			val = (0x0b << 8) | (0x0b << 20);
+
+		val |= ((2 * i) << 4) | ((2 * i + 1) << 16);
+		writel(val, dp->regs + STTS_BIT_CH(i));
+	}
+
+	switch (audio->sample_rate) {
+	case 32000:
+		val = SAMPLING_FREQ(3) |
+		      ORIGINAL_SAMP_FREQ(0xc);
+		break;
+	case 44100:
+		val = SAMPLING_FREQ(0) |
+		      ORIGINAL_SAMP_FREQ(0xf);
+		break;
+	case 48000:
+		val = SAMPLING_FREQ(2) |
+		      ORIGINAL_SAMP_FREQ(0xd);
+		break;
+	case 88200:
+		val = SAMPLING_FREQ(8) |
+		      ORIGINAL_SAMP_FREQ(0x7);
+		break;
+	case 96000:
+		val = SAMPLING_FREQ(0xa) |
+		      ORIGINAL_SAMP_FREQ(5);
+		break;
+	case 176400:
+		val = SAMPLING_FREQ(0xc) |
+		      ORIGINAL_SAMP_FREQ(3);
+		break;
+	case 192000:
+		val = SAMPLING_FREQ(0xe) |
+		      ORIGINAL_SAMP_FREQ(1);
+		break;
+	}
+	val |= 4;
+	writel(val, dp->regs + COM_CH_STTS_BITS);
+
+	writel(SMPL2PKT_EN, dp->regs + SMPL2PKT_CNTL);
+	writel(I2S_DEC_START, dp->regs + AUDIO_SRC_CNTL);
+}
+
+static void cdn_dp_audio_config_spdif(struct cdn_dp_device *dp)
+{
+	u32 val;
+
+	val = SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS;
+	val |= SPDIF_FIFO_MID_RANGE(0xe0);
+	val |= SPDIF_JITTER_THRSH(0xe0);
+	val |= SPDIF_JITTER_AVG_WIN(7);
+	writel(val, dp->regs + SPDIF_CTRL_ADDR);
+
+	writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL);
+
+	val = MAX_NUM_CH(2) | AUDIO_TYPE_LPCM | CFG_SUB_PCKT_NUM(4);
+	writel(val, dp->regs + SMPL2PKT_CNFG);
+	writel(SMPL2PKT_EN, dp->regs + SMPL2PKT_CNTL);
+
+	val = SPDIF_ENABLE | SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS;
+	val |= SPDIF_FIFO_MID_RANGE(0xe0);
+	val |= SPDIF_JITTER_THRSH(0xe0);
+	val |= SPDIF_JITTER_AVG_WIN(7);
+	writel(val, dp->regs + SPDIF_CTRL_ADDR);
+
+	clk_prepare_enable(dp->spdif_clk);
+	clk_set_rate(dp->spdif_clk, CDN_DP_SPDIF_CLK);
+}
+
+int cdn_dp_audio_config_set(struct cdn_dp_device *dp, struct audio_info *audio)
+{
+	int ret;
+
+	/* reset the spdif clk before config */
+	if (audio->format == AFMT_SPDIF) {
+		reset_control_assert(dp->spdif_rst);
+		reset_control_deassert(dp->spdif_rst);
+	}
+
+	ret = cdn_dp_reg_write(dp, CM_LANE_CTRL, LANE_REF_CYC);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_reg_write(dp, CM_CTRL, 0);
+	if (ret)
+		return ret;
+
+	if (audio->format == AFMT_I2S)
+		cdn_dp_audio_config_i2s(dp, audio);
+	else
+		cdn_dp_audio_config_spdif(dp);
+
+	return cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, AUDIO_PACK_EN);
+}
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
new file mode 100644
index 0000000..1affeed
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
@@ -0,0 +1,479 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Chris Zhong <zyw at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _CDN_DP_REG_H
+#define _CDN_DP_REG_H
+
+#include <linux/bitops.h>
+
+#define ADDR_IMEM		0x10000
+#define ADDR_DMEM		0x20000
+
+/* APB CFG addr */
+#define APB_CTRL			0
+#define XT_INT_CTRL			0x04
+#define MAILBOX_FULL_ADDR		0x08
+#define MAILBOX_EMPTY_ADDR		0x0c
+#define MAILBOX0_WR_DATA		0x10
+#define MAILBOX0_RD_DATA		0x14
+#define KEEP_ALIVE			0x18
+#define VER_L				0x1c
+#define VER_H				0x20
+#define VER_LIB_L_ADDR			0x24
+#define VER_LIB_H_ADDR			0x28
+#define SW_DEBUG_L			0x2c
+#define SW_DEBUG_H			0x30
+#define MAILBOX_INT_MASK		0x34
+#define MAILBOX_INT_STATUS		0x38
+#define SW_CLK_L			0x3c
+#define SW_CLK_H			0x40
+#define SW_EVENTS0			0x44
+#define SW_EVENTS1			0x48
+#define SW_EVENTS2			0x4c
+#define SW_EVENTS3			0x50
+#define XT_OCD_CTRL			0x60
+#define APB_INT_MASK			0x6c
+#define APB_STATUS_MASK			0x70
+
+/* audio decoder addr */
+#define AUDIO_SRC_CNTL			0x30000
+#define AUDIO_SRC_CNFG			0x30004
+#define COM_CH_STTS_BITS		0x30008
+#define STTS_BIT_CH(x)			(0x3000c + ((x) << 2))
+#define SPDIF_CTRL_ADDR			0x3004c
+#define SPDIF_CH1_CS_3100_ADDR		0x30050
+#define SPDIF_CH1_CS_6332_ADDR		0x30054
+#define SPDIF_CH1_CS_9564_ADDR		0x30058
+#define SPDIF_CH1_CS_12796_ADDR		0x3005c
+#define SPDIF_CH1_CS_159128_ADDR	0x30060
+#define SPDIF_CH1_CS_191160_ADDR	0x30064
+#define SPDIF_CH2_CS_3100_ADDR		0x30068
+#define SPDIF_CH2_CS_6332_ADDR		0x3006c
+#define SPDIF_CH2_CS_9564_ADDR		0x30070
+#define SPDIF_CH2_CS_12796_ADDR		0x30074
+#define SPDIF_CH2_CS_159128_ADDR	0x30078
+#define SPDIF_CH2_CS_191160_ADDR	0x3007c
+#define SMPL2PKT_CNTL			0x30080
+#define SMPL2PKT_CNFG			0x30084
+#define FIFO_CNTL			0x30088
+#define FIFO_STTS			0x3008c
+
+/* source pif addr */
+#define SOURCE_PIF_WR_ADDR		0x30800
+#define SOURCE_PIF_WR_REQ		0x30804
+#define SOURCE_PIF_RD_ADDR		0x30808
+#define SOURCE_PIF_RD_REQ		0x3080c
+#define SOURCE_PIF_DATA_WR		0x30810
+#define SOURCE_PIF_DATA_RD		0x30814
+#define SOURCE_PIF_FIFO1_FLUSH		0x30818
+#define SOURCE_PIF_FIFO2_FLUSH		0x3081c
+#define SOURCE_PIF_STATUS		0x30820
+#define SOURCE_PIF_INTERRUPT_SOURCE	0x30824
+#define SOURCE_PIF_INTERRUPT_MASK	0x30828
+#define SOURCE_PIF_PKT_ALLOC_REG	0x3082c
+#define SOURCE_PIF_PKT_ALLOC_WR_EN	0x30830
+#define SOURCE_PIF_SW_RESET		0x30834
+
+/* bellow registers need access by mailbox */
+/* source car addr */
+#define SOURCE_HDTX_CAR			0x0900
+#define SOURCE_DPTX_CAR			0x0904
+#define SOURCE_PHY_CAR			0x0908
+#define SOURCE_CEC_CAR			0x090c
+#define SOURCE_CBUS_CAR			0x0910
+#define SOURCE_PKT_CAR			0x0918
+#define SOURCE_AIF_CAR			0x091c
+#define SOURCE_CIPHER_CAR		0x0920
+#define SOURCE_CRYPTO_CAR		0x0924
+
+/* clock meters addr */
+#define CM_CTRL				0x0a00
+#define CM_I2S_CTRL			0x0a04
+#define CM_SPDIF_CTRL			0x0a08
+#define CM_VID_CTRL			0x0a0c
+#define CM_LANE_CTRL			0x0a10
+#define I2S_NM_STABLE			0x0a14
+#define I2S_NCTS_STABLE			0x0a18
+#define SPDIF_NM_STABLE			0x0a1c
+#define SPDIF_NCTS_STABLE		0x0a20
+#define NMVID_MEAS_STABLE		0x0a24
+#define I2S_MEAS			0x0a40
+#define SPDIF_MEAS			0x0a80
+#define NMVID_MEAS			0x0ac0
+
+/* source vif addr */
+#define BND_HSYNC2VSYNC			0x0b00
+#define HSYNC2VSYNC_F1_L1		0x0b04
+#define HSYNC2VSYNC_F2_L1		0x0b08
+#define HSYNC2VSYNC_STATUS		0x0b0c
+#define HSYNC2VSYNC_POL_CTRL		0x0b10
+
+/* dptx phy addr */
+#define DP_TX_PHY_CONFIG_REG		0x2000
+#define DP_TX_PHY_STATUS_REG		0x2004
+#define DP_TX_PHY_SW_RESET		0x2008
+#define DP_TX_PHY_SCRAMBLER_SEED	0x200c
+#define DP_TX_PHY_TRAINING_01_04	0x2010
+#define DP_TX_PHY_TRAINING_05_08	0x2014
+#define DP_TX_PHY_TRAINING_09_10	0x2018
+#define TEST_COR			0x23fc
+
+/* dptx hpd addr */
+#define HPD_IRQ_DET_MIN_TIMER		0x2100
+#define HPD_IRQ_DET_MAX_TIMER		0x2104
+#define HPD_UNPLGED_DET_MIN_TIMER	0x2108
+#define HPD_STABLE_TIMER		0x210c
+#define HPD_FILTER_TIMER		0x2110
+#define HPD_EVENT_MASK			0x211c
+#define HPD_EVENT_DET			0x2120
+
+/* dpyx framer addr */
+#define DP_FRAMER_GLOBAL_CONFIG		0x2200
+#define DP_SW_RESET			0x2204
+#define DP_FRAMER_TU			0x2208
+#define DP_FRAMER_PXL_REPR		0x220c
+#define DP_FRAMER_SP			0x2210
+#define AUDIO_PACK_CONTROL		0x2214
+#define DP_VC_TABLE(x)			(0x2218 + ((x) << 2))
+#define DP_VB_ID			0x2258
+#define DP_MTPH_LVP_CONTROL		0x225c
+#define DP_MTPH_SYMBOL_VALUES		0x2260
+#define DP_MTPH_ECF_CONTROL		0x2264
+#define DP_MTPH_ACT_CONTROL		0x2268
+#define DP_MTPH_STATUS			0x226c
+#define DP_INTERRUPT_SOURCE		0x2270
+#define DP_INTERRUPT_MASK		0x2274
+#define DP_FRONT_BACK_PORCH		0x2278
+#define DP_BYTE_COUNT			0x227c
+
+/* dptx stream addr */
+#define MSA_HORIZONTAL_0		0x2280
+#define MSA_HORIZONTAL_1		0x2284
+#define MSA_VERTICAL_0			0x2288
+#define MSA_VERTICAL_1			0x228c
+#define MSA_MISC			0x2290
+#define STREAM_CONFIG			0x2294
+#define AUDIO_PACK_STATUS		0x2298
+#define VIF_STATUS			0x229c
+#define PCK_STUFF_STATUS_0		0x22a0
+#define PCK_STUFF_STATUS_1		0x22a4
+#define INFO_PACK_STATUS		0x22a8
+#define RATE_GOVERNOR_STATUS		0x22ac
+#define DP_HORIZONTAL			0x22b0
+#define DP_VERTICAL_0			0x22b4
+#define DP_VERTICAL_1			0x22b8
+#define DP_BLOCK_SDP			0x22bc
+
+/* dptx glbl addr */
+#define DPTX_LANE_EN			0x2300
+#define DPTX_ENHNCD			0x2304
+#define DPTX_INT_MASK			0x2308
+#define DPTX_INT_STATUS			0x230c
+
+/* dp aux addr */
+#define DP_AUX_HOST_CONTROL		0x2800
+#define DP_AUX_INTERRUPT_SOURCE		0x2804
+#define DP_AUX_INTERRUPT_MASK		0x2808
+#define DP_AUX_SWAP_INVERSION_CONTROL	0x280c
+#define DP_AUX_SEND_NACK_TRANSACTION	0x2810
+#define DP_AUX_CLEAR_RX			0x2814
+#define DP_AUX_CLEAR_TX			0x2818
+#define DP_AUX_TIMER_STOP		0x281c
+#define DP_AUX_TIMER_CLEAR		0x2820
+#define DP_AUX_RESET_SW			0x2824
+#define DP_AUX_DIVIDE_2M		0x2828
+#define DP_AUX_TX_PREACHARGE_LENGTH	0x282c
+#define DP_AUX_FREQUENCY_1M_MAX		0x2830
+#define DP_AUX_FREQUENCY_1M_MIN		0x2834
+#define DP_AUX_RX_PRE_MIN		0x2838
+#define DP_AUX_RX_PRE_MAX		0x283c
+#define DP_AUX_TIMER_PRESET		0x2840
+#define DP_AUX_NACK_FORMAT		0x2844
+#define DP_AUX_TX_DATA			0x2848
+#define DP_AUX_RX_DATA			0x284c
+#define DP_AUX_TX_STATUS		0x2850
+#define DP_AUX_RX_STATUS		0x2854
+#define DP_AUX_RX_CYCLE_COUNTER		0x2858
+#define DP_AUX_MAIN_STATES		0x285c
+#define DP_AUX_MAIN_TIMER		0x2860
+#define DP_AUX_AFE_OUT			0x2864
+
+/* crypto addr */
+#define CRYPTO_HDCP_REVISION		0x5800
+#define HDCP_CRYPTO_CONFIG		0x5804
+#define CRYPTO_INTERRUPT_SOURCE		0x5808
+#define CRYPTO_INTERRUPT_MASK		0x580c
+#define CRYPTO22_CONFIG			0x5818
+#define CRYPTO22_STATUS			0x581c
+#define SHA_256_DATA_IN			0x583c
+#define SHA_256_DATA_OUT_(x)		(0x5850 + ((x) << 2))
+#define AES_32_KEY_(x)			(0x5870 + ((x) << 2))
+#define AES_32_DATA_IN			0x5880
+#define AES_32_DATA_OUT_(x)		(0x5884 + ((x) << 2))
+#define CRYPTO14_CONFIG			0x58a0
+#define CRYPTO14_STATUS			0x58a4
+#define CRYPTO14_PRNM_OUT		0x58a8
+#define CRYPTO14_KM_0			0x58ac
+#define CRYPTO14_KM_1			0x58b0
+#define CRYPTO14_AN_0			0x58b4
+#define CRYPTO14_AN_1			0x58b8
+#define CRYPTO14_YOUR_KSV_0		0x58bc
+#define CRYPTO14_YOUR_KSV_1		0x58c0
+#define CRYPTO14_MI_0			0x58c4
+#define CRYPTO14_MI_1			0x58c8
+#define CRYPTO14_TI_0			0x58cc
+#define CRYPTO14_KI_0			0x58d0
+#define CRYPTO14_KI_1			0x58d4
+#define CRYPTO14_BLOCKS_NUM		0x58d8
+#define CRYPTO14_KEY_MEM_DATA_0		0x58dc
+#define CRYPTO14_KEY_MEM_DATA_1		0x58e0
+#define CRYPTO14_SHA1_MSG_DATA		0x58e4
+#define CRYPTO14_SHA1_V_VALUE_(x)	(0x58e8 + ((x) << 2))
+#define TRNG_CTRL			0x58fc
+#define TRNG_DATA_RDY			0x5900
+#define TRNG_DATA			0x5904
+
+/* cipher addr */
+#define HDCP_REVISION			0x60000
+#define INTERRUPT_SOURCE		0x60004
+#define INTERRUPT_MASK			0x60008
+#define HDCP_CIPHER_CONFIG		0x6000c
+#define AES_128_KEY_0			0x60010
+#define AES_128_KEY_1			0x60014
+#define AES_128_KEY_2			0x60018
+#define AES_128_KEY_3			0x6001c
+#define AES_128_RANDOM_0		0x60020
+#define AES_128_RANDOM_1		0x60024
+#define CIPHER14_KM_0			0x60028
+#define CIPHER14_KM_1			0x6002c
+#define CIPHER14_STATUS			0x60030
+#define CIPHER14_RI_PJ_STATUS		0x60034
+#define CIPHER_MODE			0x60038
+#define CIPHER14_AN_0			0x6003c
+#define CIPHER14_AN_1			0x60040
+#define CIPHER22_AUTH			0x60044
+#define CIPHER14_R0_DP_STATUS		0x60048
+#define CIPHER14_BOOTSTRAP		0x6004c
+
+#define DPTX_FRMR_DATA_CLK_RSTN_EN	BIT(11)
+#define DPTX_FRMR_DATA_CLK_EN		BIT(10)
+#define DPTX_PHY_DATA_RSTN_EN		BIT(9)
+#define DPTX_PHY_DATA_CLK_EN		BIT(8)
+#define DPTX_PHY_CHAR_RSTN_EN		BIT(7)
+#define DPTX_PHY_CHAR_CLK_EN		BIT(6)
+#define SOURCE_AUX_SYS_CLK_RSTN_EN	BIT(5)
+#define SOURCE_AUX_SYS_CLK_EN		BIT(4)
+#define DPTX_SYS_CLK_RSTN_EN		BIT(3)
+#define DPTX_SYS_CLK_EN			BIT(2)
+#define CFG_DPTX_VIF_CLK_RSTN_EN	BIT(1)
+#define CFG_DPTX_VIF_CLK_EN		BIT(0)
+
+#define SOURCE_PHY_RSTN_EN		BIT(1)
+#define SOURCE_PHY_CLK_EN		BIT(0)
+
+#define SOURCE_PKT_SYS_RSTN_EN		BIT(3)
+#define SOURCE_PKT_SYS_CLK_EN		BIT(2)
+#define SOURCE_PKT_DATA_RSTN_EN		BIT(1)
+#define SOURCE_PKT_DATA_CLK_EN		BIT(0)
+
+#define SPDIF_CDR_CLK_RSTN_EN		BIT(5)
+#define SPDIF_CDR_CLK_EN		BIT(4)
+#define SOURCE_AIF_SYS_RSTN_EN		BIT(3)
+#define SOURCE_AIF_SYS_CLK_EN		BIT(2)
+#define SOURCE_AIF_CLK_RSTN_EN		BIT(1)
+#define SOURCE_AIF_CLK_EN		BIT(0)
+
+#define SOURCE_CIPHER_SYSTEM_CLK_RSTN_EN	BIT(3)
+#define SOURCE_CIPHER_SYS_CLK_EN		BIT(2)
+#define SOURCE_CIPHER_CHAR_CLK_RSTN_EN		BIT(1)
+#define SOURCE_CIPHER_CHAR_CLK_EN		BIT(0)
+
+#define SOURCE_CRYPTO_SYS_CLK_RSTN_EN	BIT(1)
+#define SOURCE_CRYPTO_SYS_CLK_EN	BIT(0)
+
+#define APB_IRAM_PATH			BIT(2)
+#define APB_DRAM_PATH			BIT(1)
+#define APB_XT_RESET			BIT(0)
+
+#define MAILBOX_INT_MASK_BIT		BIT(1)
+#define PIF_INT_MASK_BIT		BIT(0)
+#define ALL_INT_MASK			3
+
+/* mailbox */
+#define MB_OPCODE_ID			0
+#define MB_MODULE_ID			1
+#define MB_SIZE_MSB_ID			2
+#define MB_SIZE_LSB_ID			3
+#define MB_DATA_ID			4
+
+#define MB_MODULE_ID_DP_TX		0x01
+#define MB_MODULE_ID_HDCP_TX		0x07
+#define MB_MODULE_ID_HDCP_RX		0x08
+#define MB_MODULE_ID_HDCP_GENERAL	0x09
+#define MB_MODULE_ID_GENERAL		0x0a
+
+/* general opcode */
+#define GENERAL_MAIN_CONTROL            0x01
+#define GENERAL_TEST_ECHO               0x02
+#define GENERAL_BUS_SETTINGS            0x03
+#define GENERAL_TEST_ACCESS             0x04
+
+#define DPTX_SET_POWER_MNG			0x00
+#define DPTX_SET_HOST_CAPABILITIES		0x01
+#define DPTX_GET_EDID				0x02
+#define DPTX_READ_DPCD				0x03
+#define DPTX_WRITE_DPCD				0x04
+#define DPTX_ENABLE_EVENT			0x05
+#define DPTX_WRITE_REGISTER			0x06
+#define DPTX_READ_REGISTER			0x07
+#define DPTX_WRITE_FIELD			0x08
+#define DPTX_TRAINING_CONTROL			0x09
+#define DPTX_READ_EVENT				0x0a
+#define DPTX_READ_LINK_STAT			0x0b
+#define DPTX_SET_VIDEO				0x0c
+#define DPTX_SET_AUDIO				0x0d
+#define DPTX_GET_LAST_AUX_STAUS			0x0e
+#define DPTX_SET_LINK_BREAK_POINT		0x0f
+#define DPTX_FORCE_LANES			0x10
+#define DPTX_HPD_STATE				0x11
+
+#define DPTX_EVENT_ENABLE_HPD			BIT(0)
+#define DPTX_EVENT_ENABLE_TRAINING		BIT(1)
+
+#define LINK_TRAINING_NOT_ACTIVE		0
+#define LINK_TRAINING_RUN			1
+#define LINK_TRAINING_RESTART			2
+
+#define CONTROL_VIDEO_IDLE			0
+#define CONTROL_VIDEO_VALID			1
+
+#define VIF_BYPASS_INTERLACE			BIT(13)
+#define INTERLACE_FMT_DET			BIT(12)
+#define INTERLACE_DTCT_WIN			0x20
+
+#define DP_FRAMER_SP_INTERLACE_EN		BIT(2)
+#define DP_FRAMER_SP_HSP			BIT(1)
+#define DP_FRAMER_SP_VSP			BIT(0)
+
+/* capability */
+#define AUX_HOST_INVERT				3
+#define	FAST_LT_SUPPORT				1
+#define FAST_LT_NOT_SUPPORT			0
+#define LANE_MAPPING_NORMAL			0xe4
+#define LANE_MAPPING_FLIPPED			0x1b
+#define ENHANCED				1
+
+#define	FULL_LT_STARTED				BIT(0)
+#define FASE_LT_STARTED				BIT(1)
+#define CLK_RECOVERY_FINISHED			BIT(2)
+#define EQ_PHASE_FINISHED			BIT(3)
+#define FASE_LT_START_FINISHED			BIT(4)
+#define CLK_RECOVERY_FAILED			BIT(5)
+#define EQ_PHASE_FAILED				BIT(6)
+#define FASE_LT_FAILED				BIT(7)
+
+#define DPTX_HPD_EVENT				BIT(0)
+#define DPTX_TRAINING_EVENT			BIT(1)
+#define HDCP_TX_STATUS_EVENT			BIT(4)
+#define HDCP2_TX_IS_KM_STORED_EVENT		BIT(5)
+#define HDCP2_TX_STORE_KM_EVENT			BIT(6)
+#define HDCP_TX_IS_RECEIVER_ID_VALID_EVENT	BIT(7)
+
+#define TU_SIZE					30
+#define CDN_DP_MAX_LINK_RATE			DP_LINK_BW_5_4
+
+/* audio */
+#define AUDIO_PACK_EN				BIT(8)
+#define SAMPLING_FREQ(x)			(((x) & 0xf) << 16)
+#define ORIGINAL_SAMP_FREQ(x)			(((x) & 0xf) << 24)
+#define SYNC_WR_TO_CH_ZERO			BIT(1)
+#define I2S_DEC_START				BIT(1)
+#define AUDIO_SW_RST				BIT(0)
+#define SMPL2PKT_EN				BIT(1)
+#define MAX_NUM_CH(x)				(((x) & 0x1f) - 1)
+#define NUM_OF_I2S_PORTS(x)			((((x) / 2 - 1) & 0x3) << 5)
+#define AUDIO_TYPE_LPCM				(2 << 7)
+#define CFG_SUB_PCKT_NUM(x)			((((x) - 1) & 0x7) << 11)
+#define AUDIO_CH_NUM(x)				((((x) - 1) & 0x1f) << 2)
+#define TRANS_SMPL_WIDTH_16			0
+#define TRANS_SMPL_WIDTH_24			BIT(11)
+#define TRANS_SMPL_WIDTH_32			(2 << 11)
+#define I2S_DEC_PORT_EN(x)			(((x) & 0xf) << 17)
+#define SPDIF_ENABLE				BIT(21)
+#define SPDIF_AVG_SEL				BIT(20)
+#define SPDIF_JITTER_BYPASS			BIT(19)
+#define SPDIF_FIFO_MID_RANGE(x)			(((x) & 0xff) << 11)
+#define SPDIF_JITTER_THRSH(x)			(((x) & 0xff) << 3)
+#define SPDIF_JITTER_AVG_WIN(x)			((x) & 0x7)
+
+/* Refernce cycles when using lane clock as refernce */
+#define LANE_REF_CYC				0x8000
+
+enum voltage_swing_level {
+	VOLTAGE_LEVEL_0,
+	VOLTAGE_LEVEL_1,
+	VOLTAGE_LEVEL_2,
+	VOLTAGE_LEVEL_3,
+};
+
+enum pre_emphasis_level {
+	PRE_EMPHASIS_LEVEL_0,
+	PRE_EMPHASIS_LEVEL_1,
+	PRE_EMPHASIS_LEVEL_2,
+	PRE_EMPHASIS_LEVEL_3,
+};
+
+enum pattern_set {
+	PTS1		= BIT(0),
+	PTS2		= BIT(1),
+	PTS3		= BIT(2),
+	PTS4		= BIT(3),
+	DP_NONE		= BIT(4)
+};
+
+enum vic_color_depth {
+	BCS_6 = 0x1,
+	BCS_8 = 0x2,
+	BCS_10 = 0x4,
+	BCS_12 = 0x8,
+	BCS_16 = 0x10,
+};
+
+enum vic_bt_type {
+	BT_601 = 0x0,
+	BT_709 = 0x1,
+};
+
+void cdn_dp_clock_reset(struct cdn_dp_device *dp);
+
+void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, u32 clk);
+int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
+			 u32 i_size, const u32 *d_mem, u32 d_size);
+int cdn_dp_active(struct cdn_dp_device *dp, bool enable);
+int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes);
+int cdn_dp_event_config(struct cdn_dp_device *dp);
+u32 cdn_dp_get_event(struct cdn_dp_device *dp);
+int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
+int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value);
+int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len);
+int cdn_dp_get_edid_block(void *dp, u8 *edid,
+			  unsigned int block, size_t length);
+int cdn_dp_training_start(struct cdn_dp_device *dp);
+int cdn_dp_get_training_status(struct cdn_dp_device *dp);
+int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active);
+int cdn_dp_config_video(struct cdn_dp_device *dp);
+int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio);
+int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable);
+int cdn_dp_audio_config_set(struct cdn_dp_device *dp, struct audio_info *audio);
+#endif /* _CDN_DP_REG_H */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index edd7ec2..d723d8e 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -969,9 +969,11 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
 		vop_dsp_hold_valid_irq_disable(vop);
 	}
 
-	pin_pol = 0x8;
-	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1;
-	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : (1 << 1);
+	pin_pol = BIT(DCLK_INVERT);
+	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ?
+		   0 : BIT(HSYNC_POSITIVE);
+	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ?
+		   0 : BIT(VSYNC_POSITIVE);
 	VOP_CTRL_SET(vop, pin_pol, pin_pol);
 
 	switch (s->output_type) {
@@ -991,6 +993,11 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
 		VOP_CTRL_SET(vop, mipi_pin_pol, pin_pol);
 		VOP_CTRL_SET(vop, mipi_en, 1);
 		break;
+	case DRM_MODE_CONNECTOR_DisplayPort:
+		pin_pol &= ~BIT(DCLK_INVERT);
+		VOP_CTRL_SET(vop, dp_pin_pol, pin_pol);
+		VOP_CTRL_SET(vop, dp_en, 1);
+		break;
 	default:
 		DRM_ERROR("unsupport connector_type[%d]\n", s->output_type);
 	}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index ff4f52e..4820a8b 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -45,6 +45,7 @@ struct vop_ctrl {
 	struct vop_reg edp_en;
 	struct vop_reg hdmi_en;
 	struct vop_reg mipi_en;
+	struct vop_reg dp_en;
 	struct vop_reg out_mode;
 	struct vop_reg dither_down;
 	struct vop_reg dither_up;
@@ -53,6 +54,7 @@ struct vop_ctrl {
 	struct vop_reg hdmi_pin_pol;
 	struct vop_reg edp_pin_pol;
 	struct vop_reg mipi_pin_pol;
+	struct vop_reg dp_pin_pol;
 
 	struct vop_reg htotal_pw;
 	struct vop_reg hact_st_end;
@@ -242,6 +244,13 @@ enum scale_down_mode {
 	SCALE_DOWN_AVG = 0x1
 };
 
+enum vop_pol {
+	HSYNC_POSITIVE = 0,
+	VSYNC_POSITIVE = 1,
+	DEN_NEGATIVE   = 2,
+	DCLK_INVERT    = 3
+};
+
 #define FRAC_16_16(mult, div)    (((mult) << 16) / (div))
 #define SCL_FT_DEFAULT_FIXPOINT_SHIFT	12
 #define SCL_MAX_VSKIPLINES		4
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 5b1ae1f..dcf172e 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -281,6 +281,7 @@ static const struct vop_data rk3288_vop = {
 static const struct vop_ctrl rk3399_ctrl_data = {
 	.standby = VOP_REG(RK3399_SYS_CTRL, 0x1, 22),
 	.gate_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 23),
+	.dp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 11),
 	.rgb_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 12),
 	.hdmi_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 13),
 	.edp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 14),
@@ -290,6 +291,7 @@ static const struct vop_ctrl rk3399_ctrl_data = {
 	.data_blank = VOP_REG(RK3399_DSP_CTRL0, 0x1, 19),
 	.out_mode = VOP_REG(RK3399_DSP_CTRL0, 0xf, 0),
 	.rgb_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16),
+	.dp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16),
 	.hdmi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 20),
 	.edp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 24),
 	.mipi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 28),
-- 
2.6.3



From ykk at rock-chips.com  Thu Jul 21 07:14:12 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Thu, 21 Jul 2016 22:14:12 +0800
Subject: [PATCH v1.1 2/2] drm/bridge: analogix_dp: turn off the panel when eDP
 need to disable
In-Reply-To: <1469106874-4912-1-git-send-email-ykk@rock-chips.com>
References: <1469106874-4912-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1469110452-8806-1-git-send-email-ykk@rock-chips.com>

Some panels (like Sharp LQ123P1JX31) need to be turn off when eDP
controller stop to send valid video signal, otherwhise panel would
go burn in, and keep flicker and flicker.

So it's better to turn off the panel when eDP need to disable, and
we need to turn on the panel in connector->detect() callback, so
that driver would detect panel status rightly.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---

Changes in v1.1:
- unprepare the panel at the end of bridge->disable() function

 drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 22 +++++++++++++++-------
 1 file changed, 15 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index 32715da..2e519bd 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -961,6 +961,14 @@ analogix_dp_detect(struct drm_connector *connector, bool force)
 {
 	struct analogix_dp_device *dp = to_dp(connector);
 
+	/*
+	 * Panle would prepare for several times here, but don't worry it
+	 * would only enable the hardware at the first prepare time.
+	 */
+	if (dp->plat_data->panel)
+		if (drm_panel_prepare(dp->plat_data->panel))
+			DRM_ERROR("failed to setup the panel\n");
+
 	if (analogix_dp_detect_hpd(dp))
 		return connector_status_disconnected;
 
@@ -1077,6 +1085,13 @@ static void analogix_dp_bridge_disable(struct drm_bridge *bridge)
 
 	pm_runtime_put_sync(dp->dev);
 
+	if (dp->plat_data->panel) {
+		if (drm_panel_unprepare(dp->plat_data->panel)) {
+			DRM_ERROR("failed to turnoff the panel\n");
+			return;
+		}
+	}
+
 	dp->dpms_mode = DRM_MODE_DPMS_OFF;
 }
 
@@ -1333,13 +1348,6 @@ int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev,
 
 	phy_power_on(dp->phy);
 
-	if (dp->plat_data->panel) {
-		if (drm_panel_prepare(dp->plat_data->panel)) {
-			DRM_ERROR("failed to setup the panel\n");
-			return -EBUSY;
-		}
-	}
-
 	analogix_dp_init_dp(dp);
 
 	ret = devm_request_threaded_irq(&pdev->dev, dp->irq,
-- 
1.9.1




From seanpaul at chromium.org  Thu Jul 21 07:28:06 2016
From: seanpaul at chromium.org (Sean Paul)
Date: Thu, 21 Jul 2016 10:28:06 -0400
Subject: [PATCH v1 2/2] drm/bridge: analogix_dp: turn off the panel when
 eDP need to disable
In-Reply-To: <1469106886-4979-1-git-send-email-ykk@rock-chips.com>
References: <1469106874-4912-1-git-send-email-ykk@rock-chips.com>
 <1469106886-4979-1-git-send-email-ykk@rock-chips.com>
Message-ID: <CAOw6vbK7CnFwH_Tyrq6ab4DBxVLa8-JCYEM-Tu+mOYsKmNULtg@mail.gmail.com>

On Thu, Jul 21, 2016 at 9:14 AM, Yakir Yang <ykk at rock-chips.com> wrote:
> Some panels (like Sharp LQ123P1JX31) need to be turn off when eDP
> controller stop to send valid video signal, otherwhise panel would
> go burn in, and keep flicker and flicker.
>
> So it's better to turn off the panel when eDP need to disable, and
> we need to turn on the panel in connector->detect() callback, so
> that driver would detect panel status rightly.
>
> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
> ---
>  drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 18 ++++++++++--------
>  1 file changed, 10 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> index 32715da..ea059b3 100644
> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> @@ -961,6 +961,14 @@ analogix_dp_detect(struct drm_connector *connector, bool force)
>  {
>         struct analogix_dp_device *dp = to_dp(connector);
>
> +       /*
> +        * Panle would prepare for several times here, but don't worry it

s/Panle/Panel/

> +        * would only enable the hardware at the first prepare time.


Errr, this shouldn't go in detect. How about putting this in
bridge_enable instead?

> +        */
> +       if (dp->plat_data->panel)
> +               if (drm_panel_prepare(dp->plat_data->panel))

Personally, I don't like doing work in a conditional since you're
throwing the return code away. Could you break this out into:

ret = drm_panel_prepare(dp->plat_data->panel);
if (ret)
  DRM_ERROR("failed to setup the panel ret=%d\n", ret);

> +                       DRM_ERROR("failed to setup the panel\n");
> +
>         if (analogix_dp_detect_hpd(dp))
>                 return connector_status_disconnected;
>
> @@ -1063,7 +1071,8 @@ static void analogix_dp_bridge_disable(struct drm_bridge *bridge)
>                 return;
>
>         if (dp->plat_data->panel) {
> -               if (drm_panel_disable(dp->plat_data->panel)) {
> +               if (drm_panel_disable(dp->plat_data->panel) ||
> +                   drm_panel_unprepare(dp->plat_data->panel)) {

Same comment here, please break this out into separate statements for
better readability/logging.

>                         DRM_ERROR("failed to disable the panel\n");
>                         return;
>                 }
> @@ -1333,13 +1342,6 @@ int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev,
>
>         phy_power_on(dp->phy);
>
> -       if (dp->plat_data->panel) {
> -               if (drm_panel_prepare(dp->plat_data->panel)) {
> -                       DRM_ERROR("failed to setup the panel\n");
> -                       return -EBUSY;
> -               }
> -       }
> -
>         analogix_dp_init_dp(dp);
>
>         ret = devm_request_threaded_irq(&pdev->dev, dp->irq,
> --
> 1.9.1
>
>


From seanpaul at chromium.org  Thu Jul 21 07:30:13 2016
From: seanpaul at chromium.org (Sean Paul)
Date: Thu, 21 Jul 2016 10:30:13 -0400
Subject: [PATCH v1 1/2] drm/panel: simple-panel: add the delay timing for
 Sharp LQ123P1JX31
In-Reply-To: <1469106874-4912-1-git-send-email-ykk@rock-chips.com>
References: <1469106874-4912-1-git-send-email-ykk@rock-chips.com>
Message-ID: <CAOw6vbLVrJj9So7tJeDtnDnq_+W=ZunPNwRRidfQKmjaj=byUw@mail.gmail.com>

On Thu, Jul 21, 2016 at 9:14 AM, Yakir Yang <ykk at rock-chips.com> wrote:
> According to page 16 of Sharp LQ123P1JX31 datasheet, we need to add the
> missing delay timing. Panel prepare time should be t1 (0.5ms~10ms) plus
> t3 (0ms~100ms), and panel enable time should equal to t7 (0ms~50ms), and
> panel unprepare time should be t11 (1ms~50ms) plus t12 (500ms~).
>
> Signed-off-by: Yakir Yang <ykk at rock-chips.com>

Reviewed-by: Sean Paul <seanpaul at chromium.org>

> ---
>  drivers/gpu/drm/panel/panel-simple.c | 5 +++++
>  1 file changed, 5 insertions(+)
>
> diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
> index 85143d1..f178998 100644
> --- a/drivers/gpu/drm/panel/panel-simple.c
> +++ b/drivers/gpu/drm/panel/panel-simple.c
> @@ -1384,6 +1384,11 @@ static const struct panel_desc sharp_lq123p1jx31 = {
>                 .width = 259,
>                 .height = 173,
>         },
> +       .delay = {
> +               .prepare = 110,
> +               .enable = 50,
> +               .unprepare = 550,
> +       },
>  };
>
>  static const struct drm_display_mode shelly_sca07010_bfn_lnn_mode = {
> --
> 1.9.1
>
>


From mark.yao at rock-chips.com  Thu Jul 21 17:41:19 2016
From: mark.yao at rock-chips.com (Mark yao)
Date: Fri, 22 Jul 2016 08:41:19 +0800
Subject: [v6 PATCH 6/6] drm/rockchip: cdn-dp: add cdn DP support for rk3399
In-Reply-To: <1469106808-28297-7-git-send-email-zyw@rock-chips.com>
References: <1469106808-28297-1-git-send-email-zyw@rock-chips.com>
 <1469106808-28297-7-git-send-email-zyw@rock-chips.com>
Message-ID: <57916BAF.10109@rock-chips.com>

On 2016?07?21? 21:13, Chris Zhong wrote:
> Add support for cdn DP controller which is embedded in the rk3399
> SoCs. The DP is compliant with DisplayPort Specification,
> Version 1.3, This IP is compatible with the rockchip type-c PHY IP.
> There is a uCPU in DP controller, it need a firmware to work,
> please put the firmware file to /lib/firmware/cdn/dptx.bin. The
> uCPU in charge of aux communication and link training, the host use
> mailbox to communicate with the ucpu.
> The dclk pin_pol of vop must not be invert for DP.
>
> Signed-off-by: Chris Zhong <zyw at rock-chips.com>
> Reviewed-by: Sean Paul <seanpaul at chromium.org>

Sean Paul already do a good review, I think it's ready to be merged, so

Acked-by: Mark Yao <mark.yao at rock-chips.com>


> ---
>
> Changes in v6:
> - add a port struct
> - select SND_SOC_HDMI_CODEC
> - force reset the phy when hpd detected
>
> Changes in v5:
> - alphabetical order
> - do not use long, use u32 or u64
> - return MODE_CLOCK_HIGH when requested > actual
> - Optimized Coding Style
> - add a formula to get better tu size and symbol value.
> - modify according to Sean Paul's comments
> - fixed the fw_wait always 0
>
> Changes in v4:
> - use phy framework to control DP phy
> - support 2 phys
>
> Changes in v3:
> - use EXTCON_DISP_DP and EXTCON_DISP_DP_ALT cable to get dp port state.
> - reset spdif before config it
> - modify the firmware clk to 100Mhz
> - retry load firmware if fw file is requested too early
>
> Changes in v2:
> - Alphabetic order
> - remove excess error message
> - use define clk_rate
> - check all return value
> - remove dev_set_name(dp->dev, "cdn-dp");
> - use schedule_delayed_work
> - remove never-called functions
> - remove some unnecessary ()
>
> Changes in v1:
> - use extcon API
> - use hdmi-codec for the DP Asoc
> - do not initialize the "ret"
> - printk a err log when drm_of_encoder_active_endpoint_id
> - modify the dclk pin_pol to a single line
>
>   drivers/gpu/drm/rockchip/Kconfig            |  10 +
>   drivers/gpu/drm/rockchip/Makefile           |   1 +
>   drivers/gpu/drm/rockchip/cdn-dp-core.c      | 868 ++++++++++++++++++++++++++
>   drivers/gpu/drm/rockchip/cdn-dp-core.h      | 102 ++++
>   drivers/gpu/drm/rockchip/cdn-dp-reg.c       | 904 ++++++++++++++++++++++++++++
>   drivers/gpu/drm/rockchip/cdn-dp-reg.h       | 479 +++++++++++++++
>   drivers/gpu/drm/rockchip/rockchip_drm_vop.c |  13 +-
>   drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   9 +
>   drivers/gpu/drm/rockchip/rockchip_vop_reg.c |   2 +
>   9 files changed, 2385 insertions(+), 3 deletions(-)
>   create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.c
>   create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.h
>   create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.c
>   create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.h
>
> diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
> index d30bdc3..20aaafe 100644
> --- a/drivers/gpu/drm/rockchip/Kconfig
> +++ b/drivers/gpu/drm/rockchip/Kconfig
> @@ -25,6 +25,16 @@ config ROCKCHIP_ANALOGIX_DP
>   	  for the Analogix Core DP driver. If you want to enable DP
>   	  on RK3288 based SoC, you should selet this option.
>   
> +config ROCKCHIP_CDN_DP
> +        tristate "Rockchip cdn DP"
> +        depends on DRM_ROCKCHIP
> +	select SND_SOC_HDMI_CODEC if SND_SOC
> +        help
> +	  This selects support for Rockchip SoC specific extensions
> +	  for the cdn DP driver. If you want to enable Dp on
> +	  RK3399 based SoC, you should select this
> +	  option.
> +
>   config ROCKCHIP_DW_HDMI
>           tristate "Rockchip specific extensions for Synopsys DW HDMI"
>           depends on DRM_ROCKCHIP
> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
> index 05d0713..abdecd5 100644
> --- a/drivers/gpu/drm/rockchip/Makefile
> +++ b/drivers/gpu/drm/rockchip/Makefile
> @@ -7,6 +7,7 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
>   rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
>   
>   obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
> +obj-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
>   obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
>   obj-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
>   obj-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
> new file mode 100644
> index 0000000..209a7ef
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
> @@ -0,0 +1,868 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author: Chris Zhong <zyw at rock-chips.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * 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 <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_dp_helper.h>
> +#include <drm/drm_edid.h>
> +#include <drm/drm_of.h>
> +
> +#include <linux/clk.h>
> +#include <linux/component.h>
> +#include <linux/extcon.h>
> +#include <linux/firmware.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/phy/phy.h>
> +
> +#include <sound/hdmi-codec.h>
> +
> +#include "cdn-dp-core.h"
> +#include "cdn-dp-reg.h"
> +#include "rockchip_drm_vop.h"
> +
> +#define connector_to_dp(c) \
> +		container_of(c, struct cdn_dp_device, connector)
> +
> +#define encoder_to_dp(c) \
> +		container_of(c, struct cdn_dp_device, encoder)
> +
> +/* dp grf register offset */
> +#define DP_VOP_SEL		0x6224
> +#define DP_SEL_VOP_LIT		BIT(12)
> +#define MAX_FW_WAIT_SECS	64
> +#define CDN_DP_FIRMWARE		"rockchip/dptx.bin"
> +
> +struct cdn_dp_data {
> +	u8 max_phy;
> +};
> +
> +struct cdn_dp_data rk3399_cdn_dp = {
> +	.max_phy = 2,
> +};
> +
> +static const struct of_device_id cdn_dp_dt_ids[] = {
> +	{ .compatible = "rockchip,rk3399-cdn-dp",
> +		.data = (void *)&rk3399_cdn_dp },

align the .data?


> +	{}
> +};
> +
> +MODULE_DEVICE_TABLE(of, cdn_dp_dt_ids);
> +
> +static int cdn_dp_clk_enable(struct cdn_dp_device *dp)
> +{
> +	int ret;
> +	u32 rate;
> +
> +	ret = clk_prepare_enable(dp->pclk);
> +	if (ret < 0) {
> +		dev_err(dp->dev, "cannot enable dp pclk %d\n", ret);
> +		goto err_pclk;
> +	}
> +
> +	ret = clk_prepare_enable(dp->core_clk);
> +	if (ret < 0) {
> +		dev_err(dp->dev, "cannot enable core_clk %d\n", ret);
> +		goto err_core_clk;
> +	}
> +
> +	rate = clk_get_rate(dp->core_clk);
> +	if (rate < 0) {
> +		dev_err(dp->dev, "get clk rate failed: %d\n", rate);
> +		goto err_set_rate;
> +	}
> +
> +	cdn_dp_set_fw_clk(dp, rate);
> +
> +	return 0;
> +
> +err_set_rate:
> +	clk_disable_unprepare(dp->core_clk);
> +err_core_clk:
> +	clk_disable_unprepare(dp->pclk);
> +err_pclk:
> +	return ret;
> +}
> +
> +static enum drm_connector_status
> +cdn_dp_connector_detect(struct drm_connector *connector, bool force)
> +{
> +	struct cdn_dp_device *dp = connector_to_dp(connector);
> +
> +	return dp->hpd_status;
> +}
> +
> +static void cdn_dp_connector_destroy(struct drm_connector *connector)
> +{
> +	drm_connector_unregister(connector);
> +	drm_connector_cleanup(connector);
> +}
> +
> +static struct drm_connector_funcs cdn_dp_atomic_connector_funcs = {
> +	.dpms = drm_atomic_helper_connector_dpms,
> +	.detect = cdn_dp_connector_detect,
> +	.destroy = cdn_dp_connector_destroy,
> +	.fill_modes = drm_helper_probe_single_connector_modes,
> +	.reset = drm_atomic_helper_connector_reset,
> +	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +static int cdn_dp_connector_get_modes(struct drm_connector *connector)
> +{
> +	struct cdn_dp_device *dp = connector_to_dp(connector);
> +	struct edid *edid;
> +	int ret = 0;
> +
> +	edid = drm_do_get_edid(connector, cdn_dp_get_edid_block, dp);
> +	if (edid) {
> +		dev_dbg(dp->dev, "got edid: width[%d] x height[%d]\n",
> +			edid->width_cm, edid->height_cm);
> +
> +		dp->sink_has_audio = drm_detect_monitor_audio(edid);
> +		ret = drm_add_edid_modes(connector, edid);
> +		if (ret) {
> +			drm_mode_connector_update_edid_property(connector,
> +								edid);
> +			drm_edid_to_eld(connector, edid);
> +		}
> +		kfree(edid);
> +	} else {
> +		dev_warn(dp->dev, "failed to get edid\n");
> +	}
> +
> +	return ret;
> +}
> +
> +static struct drm_encoder *
> +	cdn_dp_connector_best_encoder(struct drm_connector *connector)
> +{
> +	struct cdn_dp_device *dp = connector_to_dp(connector);
> +
> +	return &dp->encoder;
> +}
> +
> +static int cdn_dp_connector_mode_valid(struct drm_connector *connector,
> +				       struct drm_display_mode *mode)
> +{
> +	struct cdn_dp_device *dp = connector_to_dp(connector);
> +	struct drm_display_info *display_info = &dp->connector.display_info;
> +	u32 requested = mode->clock * display_info->bpc * 3 / 1000;
> +	u32 sink_max, source_max, rate;
> +	u8 lanes, i;
> +	u32 actual;
> +
> +	/* find the running port */
> +	for (i = 0; i < dp->ports; i++) {
> +		if (dp->port[i]->phy_status == 1) {
> +			source_max = dp->port[i]->cap_lanes;
> +			break;
> +		}
> +	}
> +
> +	sink_max = drm_dp_max_lane_count(dp->dpcd);
> +	lanes = min(source_max, sink_max);
> +
> +	source_max = drm_dp_bw_code_to_link_rate(CDN_DP_MAX_LINK_RATE);
> +	sink_max = drm_dp_max_link_rate(dp->dpcd);
> +	rate = min(source_max, sink_max);
> +
> +	actual = rate * lanes / 100;
> +
> +	/* efficiency is about 0.8 */
> +	actual = actual * 8 / 10;
> +
> +	if (requested > actual) {
> +		dev_dbg(dp->dev, "requested=%d, actual=%d, clock=%d\n",
> +			requested, actual, mode->clock);
> +		return MODE_CLOCK_HIGH;
> +	}
> +
> +	return MODE_OK;
> +}
> +
> +static struct drm_connector_helper_funcs cdn_dp_connector_helper_funcs = {
> +	.get_modes = cdn_dp_connector_get_modes,
> +	.best_encoder = cdn_dp_connector_best_encoder,
> +	.mode_valid = cdn_dp_connector_mode_valid,
> +};
> +
> +static void cdn_dp_commit(struct drm_encoder *encoder)
> +{
> +	struct cdn_dp_device *dp = encoder_to_dp(encoder);
> +	int ret;
> +
> +	ret = cdn_dp_training_start(dp);
> +	if (ret) {
> +		dev_err(dp->dev, "link training failed: %d\n", ret);
> +		return;
> +	}
> +
> +	ret = cdn_dp_get_training_status(dp);
> +	if (ret) {
> +		dev_err(dp->dev, "get link training status failed: %d\n", ret);
> +		return;
> +	}
> +
> +	dev_info(dp->dev, "rate:%d, lanes:%d\n",
> +		 dp->link.rate, dp->link.num_lanes);
> +
> +	if (cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE))
> +		return;
> +
> +	if (cdn_dp_config_video(dp)) {
> +		dev_err(dp->dev, "unable to config video\n");
> +		return;
> +	}
> +
> +	if (cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID))
> +		return;
> +
> +	dp->dpms_mode = DRM_MODE_DPMS_ON;
> +}
> +
> +static void cdn_dp_encoder_mode_set(struct drm_encoder *encoder,
> +				    struct drm_display_mode *mode,
> +				    struct drm_display_mode *adjusted)
> +{
> +	struct cdn_dp_device *dp = encoder_to_dp(encoder);
> +	struct drm_display_info *display_info = &dp->connector.display_info;
> +	struct rockchip_crtc_state *state;
> +	struct video_info *video = &dp->video_info;
> +	int ret, val;
> +
> +	switch (display_info->bpc) {
> +	case 16:
> +	case 12:
> +	case 10:
> +		video->color_depth = 10;
> +		break;
> +	case 6:
> +		video->color_depth = 6;
> +		break;
> +	default:
> +		video->color_depth = 8;
> +		break;
> +	}
> +
> +	video->color_fmt = PXL_RGB;
> +
> +	video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC);
> +	video->h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC);
> +
> +	ret = drm_of_encoder_active_endpoint_id(dp->dev->of_node, encoder);
> +	if (ret < 0) {
> +		dev_err(dp->dev, "Could not get vop id, %d", ret);
> +		return;
> +	}
> +
> +	dev_dbg(dp->dev, "vop %s output to cdn-dp\n", (ret) ? "LIT" : "BIG");
> +	state = to_rockchip_crtc_state(encoder->crtc->state);
> +	if (ret) {
> +		val = DP_SEL_VOP_LIT | (DP_SEL_VOP_LIT << 16);
> +		state->output_mode = ROCKCHIP_OUT_MODE_P888;
> +	} else {
> +		val = DP_SEL_VOP_LIT << 16;
> +		state->output_mode = ROCKCHIP_OUT_MODE_AAAA;
> +	}
> +	ret = regmap_write(dp->grf, DP_VOP_SEL, val);
> +	if (ret != 0)
> +		dev_err(dp->dev, "Could not write to GRF: %d\n", ret);
> +
> +	memcpy(&dp->mode, adjusted, sizeof(*mode));
> +}
> +
> +static void cdn_dp_encoder_enable(struct drm_encoder *encoder)
> +{
> +	struct cdn_dp_device *dp = encoder_to_dp(encoder);
> +
> +	if (dp->dpms_mode != DRM_MODE_DPMS_ON)
> +		cdn_dp_commit(encoder);
> +}
> +
> +static void cdn_dp_encoder_disable(struct drm_encoder *encoder)
> +{
> +	struct cdn_dp_device *dp = encoder_to_dp(encoder);
> +
> +	if (dp->dpms_mode != DRM_MODE_DPMS_OFF) {
> +		cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE);
> +		dp->dpms_mode = DRM_MODE_DPMS_OFF;
> +	}
> +}
> +
> +static int
> +cdn_dp_encoder_atomic_check(struct drm_encoder *encoder,
> +			    struct drm_crtc_state *crtc_state,
> +			    struct drm_connector_state *conn_state)
> +{
> +	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
> +
> +	s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
> +	s->output_type = DRM_MODE_CONNECTOR_DisplayPort;
> +
> +	return 0;
> +}
> +
> +static struct drm_encoder_helper_funcs cdn_dp_encoder_helper_funcs = {
> +	.mode_set = cdn_dp_encoder_mode_set,
> +	.enable = cdn_dp_encoder_enable,
> +	.disable = cdn_dp_encoder_disable,
> +	.atomic_check = cdn_dp_encoder_atomic_check,
> +};
> +
> +static struct drm_encoder_funcs cdn_dp_encoder_funcs = {
> +	.destroy = drm_encoder_cleanup,
> +};
> +
> +static int cdn_dp_firmware_init(struct cdn_dp_device *dp)
> +{
> +	int ret;
> +	const u32 *iram_data, *dram_data;
> +	const struct firmware *fw = dp->fw;
> +	const struct cdn_firmware_header *hdr;
> +
> +	hdr = (struct cdn_firmware_header *)fw->data;
> +	if (fw->size != le32_to_cpu(hdr->size_bytes)) {
> +		dev_err(dp->dev, "firmware is invalid\n");
> +		return -EINVAL;
> +	}
> +
> +	iram_data = (const u32 *)(fw->data + hdr->header_size);
> +	dram_data = (const u32 *)(fw->data + hdr->header_size + hdr->iram_size);
> +
> +	ret = cdn_dp_load_firmware(dp, iram_data, hdr->iram_size,
> +				   dram_data, hdr->dram_size);
> +	if (ret)
> +		return ret;
> +
> +	dp->fw_loaded = 1;
> +
> +	ret = cdn_dp_active(dp, true);
> +	if (ret) {
> +		dev_err(dp->dev, "active ucpu failed: %d\n", ret);
> +		return ret;
> +	}
> +
> +	return cdn_dp_event_config(dp);
> +}
> +
> +static int cdn_dp_init(struct cdn_dp_device *dp)
> +{
> +	struct device *dev = dp->dev;
> +	struct device_node *np = dev->of_node;
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct resource *res;
> +	int ret;
> +
> +	dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
> +	if (IS_ERR(dp->grf)) {
> +		dev_err(dev, "cdn-dp needs rockchip,grf property\n");
> +		return PTR_ERR(dp->grf);
> +	}
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	dp->regs = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(dp->regs)) {
> +		dev_err(dev, "ioremap reg failed\n");
> +		return PTR_ERR(dp->regs);
> +	}
> +
> +	dp->core_clk = devm_clk_get(dev, "core-clk");
> +	if (IS_ERR(dp->core_clk)) {
> +		dev_err(dev, "cannot get core_clk_dp\n");
> +		return PTR_ERR(dp->core_clk);
> +	}
> +
> +	dp->pclk = devm_clk_get(dev, "pclk");
> +	if (IS_ERR(dp->pclk)) {
> +		dev_err(dev, "cannot get pclk\n");
> +		return PTR_ERR(dp->pclk);
> +	}
> +
> +	dp->spdif_clk = devm_clk_get(dev, "spdif");
> +	if (IS_ERR(dp->spdif_clk)) {
> +		dev_err(dev, "cannot get spdif_clk\n");
> +		return PTR_ERR(dp->spdif_clk);
> +	}
> +
> +	dp->spdif_rst = devm_reset_control_get(dev, "spdif");
> +	if (IS_ERR(dp->spdif_rst)) {
> +		dev_err(dev, "no spdif reset control found\n");
> +		return PTR_ERR(dp->spdif_rst);
> +	}
> +
> +	dp->dpms_mode = DRM_MODE_DPMS_OFF;
> +
> +	ret = cdn_dp_clk_enable(dp);
> +	if (ret < 0)
> +		return ret;
> +
> +	cdn_dp_clock_reset(dp);
> +
> +	return 0;
> +}
> +
> +static int cdn_dp_audio_hw_params(struct device *dev,
> +				  struct hdmi_codec_daifmt *daifmt,
> +				  struct hdmi_codec_params *params)
> +{
> +	struct cdn_dp_device *dp = dev_get_drvdata(dev);
> +	struct audio_info audio = {
> +		.sample_width = params->sample_width,
> +		.sample_rate = params->sample_rate,
> +		.channels = params->channels,
> +	};
> +	int ret;
> +
> +	if (!dp->encoder.crtc)
> +		return -ENODEV;
> +
> +	switch (daifmt->fmt) {
> +	case HDMI_I2S:
> +		audio.format = AFMT_I2S;
> +		break;
> +	case HDMI_SPDIF:
> +		audio.format = AFMT_SPDIF;
> +		break;
> +	default:
> +		dev_err(dev, "%s: Invalid format %d\n", __func__, daifmt->fmt);
> +		return -EINVAL;
> +	}
> +
> +	ret = cdn_dp_audio_config_set(dp, &audio);
> +	if (!ret)
> +		dp->audio_info = audio;
> +
> +	return ret;
> +}
> +
> +static void cdn_dp_audio_shutdown(struct device *dev)
> +{
> +	struct cdn_dp_device *dp = dev_get_drvdata(dev);
> +	int ret = cdn_dp_audio_stop(dp, &dp->audio_info);
> +
> +	if (!ret)
> +		dp->audio_info.format = AFMT_UNUSED;
> +}
> +
> +static int cdn_dp_audio_digital_mute(struct device *dev, bool enable)
> +{
> +	struct cdn_dp_device *dp = dev_get_drvdata(dev);
> +
> +	return cdn_dp_audio_mute(dp, enable);
> +}
> +
> +static int cdn_dp_audio_get_eld(struct device *dev, uint8_t *buf, size_t len)
> +{
> +	struct cdn_dp_device *dp = dev_get_drvdata(dev);
> +	struct drm_mode_config *config = &dp->encoder.dev->mode_config;
> +	struct drm_connector *connector;
> +	int ret = -ENODEV;
> +
> +	mutex_lock(&config->mutex);
> +	list_for_each_entry(connector, &config->connector_list, head) {
> +		if (&dp->encoder == connector->encoder) {
> +			memcpy(buf, connector->eld,
> +			       min(sizeof(connector->eld), len));
> +			ret = 0;
> +		}
> +	}
> +	mutex_unlock(&config->mutex);
> +
> +	return ret;
> +}
> +
> +static const struct hdmi_codec_ops audio_codec_ops = {
> +	.hw_params = cdn_dp_audio_hw_params,
> +	.audio_shutdown = cdn_dp_audio_shutdown,
> +	.digital_mute = cdn_dp_audio_digital_mute,
> +	.get_eld = cdn_dp_audio_get_eld,
> +};
> +
> +static int cdn_dp_audio_codec_init(struct cdn_dp_device *dp,
> +				   struct device *dev)
> +{
> +	struct hdmi_codec_pdata codec_data = {
> +		.i2s = 1,
> +		.spdif = 1,
> +		.ops = &audio_codec_ops,
> +		.max_i2s_channels = 8,
> +	};
> +
> +	dp->audio_pdev = platform_device_register_data(
> +			 dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO,
> +			 &codec_data, sizeof(codec_data));
> +
> +	return PTR_ERR_OR_ZERO(dp->audio_pdev);
> +}
> +
> +static int cdn_dp_get_cap_lanes(struct cdn_dp_device *dp,
> +				struct extcon_dev *edev)
> +{
> +	bool dfp, dptx;
> +	u8 lanes;
> +
> +	dfp = extcon_get_cable_state_(edev, EXTCON_USB_HOST);
> +	dptx = extcon_get_cable_state_(edev, EXTCON_DISP_DP);
> +
> +	if (dfp && dptx)
> +		lanes = 2;
> +	else if (dptx)
> +		lanes = 4;
> +	else
> +		lanes = 0;
> +
> +	return lanes;
> +}
> +
> +static int cdn_dp_phy_power_ctrl(struct cdn_dp_device *dp,
> +				 struct cdn_dp_port *port, bool enable)
> +{
> +	struct phy *phy = port->phy;
> +	int ret;
> +	int count = 0;
> +
> +	while (phy->power_count > 0) {
> +		ret = phy_power_off(phy);
> +		if (ret)
> +			return ret;
> +		count++;
> +	}
> +
> +	count = enable ? count + 1 : count - 1;
> +	while (count-- > 0) {
> +		ret = phy_power_on(phy);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	port->phy_status = enable;
> +	return 0;
> +}
> +
> +static int cdn_dp_pd_event(struct notifier_block *nb,
> +			   unsigned long event, void *priv)
> +{
> +	struct cdn_dp_port *port;
> +
> +	port = container_of(nb, struct cdn_dp_port, event_nb);
> +
> +	schedule_delayed_work(&port->event_wq, 0);
> +
> +	return 0;
> +}
> +
> +static void cdn_dp_pd_event_wq(struct work_struct *work)
> +{
> +	struct cdn_dp_port *port = container_of(work, struct cdn_dp_port,
> +						event_wq.work);
> +	struct cdn_dp_device *dp = port->dp;
> +	u8 new_cap_lanes;
> +	int ret;
> +	u8 i;
> +
> +	new_cap_lanes = cdn_dp_get_cap_lanes(dp, port->extcon);
> +
> +	if (new_cap_lanes == port->cap_lanes) {
> +		dev_err(dp->dev, "lanes count does not change: %d\n",
> +			new_cap_lanes);
> +		return;
> +	}
> +
> +	if (!new_cap_lanes) {
> +		ret = cdn_dp_phy_power_ctrl(dp, port, 0);
> +		if (ret) {
> +			dev_err(dp->dev, "phy power off failed: %d", ret);
> +			return;
> +		}
> +		port->phy_status = 0;
> +		port->cap_lanes = new_cap_lanes;
> +		for (i = 0; i < dp->ports; i++) {
> +			if (dp->port[i]->phy_status == 1)
> +				return;
> +		}
> +
> +		dp->hpd_status = connector_status_disconnected;
> +		drm_helper_hpd_irq_event(dp->drm_dev);
> +		return;
> +	}
> +
> +	/* if other phy is running, do not touch the hpd_status, and return */
> +	for (i = 0; i < dp->ports; i++) {
> +		if (dp->port[i]->phy_status == 1) {
> +			dev_warn(dp->dev, "busy, phy[%d] is running",
> +				 dp->port[i]->id);
> +			return;
> +		}
> +	}
> +
> +	if (!dp->fw_loaded) {
> +		ret = request_firmware(&dp->fw, CDN_DP_FIRMWARE, dp->dev);
> +		if (ret == -ENOENT && dp->fw_wait <= MAX_FW_WAIT_SECS) {
> +			unsigned long time = msecs_to_jiffies(dp->fw_wait * HZ);
> +
> +			/*
> +			 * If can not find the file, retry to load the firmware
> +			 * in 1 second, if still failed after 1 minute, give up.
> +			 */
> +			schedule_delayed_work(&port->event_wq, time);
> +			dev_warn(dp->dev, "retry to request firmware in %dS\n",
> +				 dp->fw_wait);
> +			dp->fw_wait *= 2;
> +			return;
> +		} else if (ret) {
> +			dev_err(dp->dev, "failed to request firmware: %d\n",
> +				ret);
> +			return;
> +		}
> +	}
> +
> +	ret = cdn_dp_phy_power_ctrl(dp, port, 1);
> +	if (ret) {
> +		if (!dp->fw_loaded)
> +			release_firmware(dp->fw);
> +		dev_err(dp->dev, "phy power on failed: %d\n", ret);
> +		return;
> +	}
> +
> +	if (!dp->fw_loaded) {
> +		ret = cdn_dp_firmware_init(dp);
> +		release_firmware(dp->fw);
> +		if (ret) {
> +			dev_err(dp->dev, "firmware init failed: %d", ret);
> +			goto err_firmware;
> +		}
> +	}
> +
> +	/* read hpd status failed, or the hpd does not exist. */
> +	ret = cdn_dp_get_hpd_status(dp);
> +	if (ret <= 0) {
> +		dev_err(dp->dev, "get hpd failed: %d", ret);
> +		goto err_firmware;
> +	}
> +
> +	ret = cdn_dp_set_host_cap(dp, new_cap_lanes);
> +	if (ret) {
> +		dev_err(dp->dev, "set host capabilities failed: %d\n", ret);
> +		goto err_firmware;
> +	}
> +
> +	/*
> +	 * Native read with retry for link status and receiver capability reads
> +	 * for cases where the sink may still be asleep.
> +	 *
> +	 * Sinks are *supposed* to come up within 1ms from an off state, but
> +	 * we're also supposed to retry 3 times per the spec.
> +	 */
> +	for (i = 0; i < 3; i++) {
> +		ret = cdn_dp_dpcd_read(dp, 0x000, dp->dpcd,
> +				       DP_RECEIVER_CAP_SIZE);
> +		if (!ret) {
> +			port->cap_lanes = new_cap_lanes;
> +			dp->hpd_status = connector_status_connected;
> +			drm_helper_hpd_irq_event(dp->drm_dev);
> +			return;
> +		}
> +		usleep_range(1000, 1050);
> +	}
> +
> +	dev_err(dp->dev, "get dpcd failed: %d", ret);
> +
> +err_firmware:
> +	ret = cdn_dp_phy_power_ctrl(dp, port, 0);
> +	if (ret)
> +		dev_err(dp->dev, "phy power off failed: %d", ret);
> +}
> +
> +static int cdn_dp_bind(struct device *dev, struct device *master,
> +		       void *data)
> +{
> +	struct cdn_dp_device *dp = dev_get_drvdata(dev);
> +	struct drm_encoder *encoder;
> +	struct drm_connector *connector;
> +	struct cdn_dp_port *port;
> +	struct drm_device *drm_dev = data;
> +	int ret, i;
> +
> +	ret = cdn_dp_init(dp);
> +	if (ret < 0)
> +		return ret;
> +
> +	dp->drm_dev = drm_dev;
> +	dp->hpd_status = connector_status_disconnected;
> +	dp->fw_wait = 1;
> +
> +	encoder = &dp->encoder;
> +
> +	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
> +							     dev->of_node);
> +	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
> +
> +	ret = drm_encoder_init(drm_dev, encoder, &cdn_dp_encoder_funcs,
> +			       DRM_MODE_ENCODER_TMDS, NULL);
> +	if (ret) {
> +		DRM_ERROR("failed to initialize encoder with drm\n");
> +		return ret;
> +	}
> +
> +	drm_encoder_helper_add(encoder, &cdn_dp_encoder_helper_funcs);
> +
> +	connector = &dp->connector;
> +	connector->polled = DRM_CONNECTOR_POLL_HPD;
> +	connector->dpms = DRM_MODE_DPMS_OFF;
> +
> +	ret = drm_connector_init(drm_dev, connector,
> +				 &cdn_dp_atomic_connector_funcs,
> +				 DRM_MODE_CONNECTOR_DisplayPort);
> +	if (ret) {
> +		DRM_ERROR("failed to initialize connector with drm\n");
> +		goto err_free_encoder;
> +	}
> +
> +	drm_connector_helper_add(connector, &cdn_dp_connector_helper_funcs);
> +
> +	ret = drm_mode_connector_attach_encoder(connector, encoder);
> +	if (ret) {
> +		DRM_ERROR("failed to attach connector and encoder\n");
> +		goto err_free_connector;
> +	}
> +
> +	cdn_dp_audio_codec_init(dp, dev);
> +
> +
> +	for (i = 0; i < dp->ports; i++) {
> +		port = dp->port[i];
> +
> +		port->event_nb.notifier_call = cdn_dp_pd_event;
> +		INIT_DELAYED_WORK(&port->event_wq, cdn_dp_pd_event_wq);
> +		ret = extcon_register_notifier(port->extcon, EXTCON_DISP_DP,
> +					       &port->event_nb);
> +		if (ret) {
> +			dev_err(dev, "regitster EXTCON_DISP_DP notifier err\n");
> +			return ret;
> +		}
> +
> +		if (extcon_get_cable_state_(port->extcon, EXTCON_DISP_DP))
> +			schedule_delayed_work(&port->event_wq, 0);
> +	}
> +
> +	return 0;
> +
> +err_free_connector:
> +	drm_connector_cleanup(connector);
> +err_free_encoder:
> +	drm_encoder_cleanup(encoder);
> +	return ret;
> +}
> +
> +static void cdn_dp_unbind(struct device *dev, struct device *master, void *data)
> +{
> +	struct cdn_dp_device *dp = dev_get_drvdata(dev);
> +	struct drm_encoder *encoder = &dp->encoder;
> +	struct drm_connector *connector = &dp->connector;
> +	struct cdn_dp_port *port;
> +	int i;
> +
> +	platform_device_unregister(dp->audio_pdev);
> +	cdn_dp_encoder_disable(encoder);
> +	encoder->funcs->destroy(encoder);
> +	connector->funcs->destroy(connector);
> +
> +	for (i = 0; i < dp->ports; i++) {
> +		port = dp->port[i];
> +		extcon_unregister_notifier(port->extcon, EXTCON_DISP_DP,
> +					   &port->event_nb);
> +	}
> +}
> +
> +static const struct component_ops cdn_dp_component_ops = {
> +	.bind = cdn_dp_bind,
> +	.unbind = cdn_dp_unbind,
> +};
> +
> +static int cdn_dp_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	const struct of_device_id *match;
> +	struct cdn_dp_data *dp_data;
> +	struct cdn_dp_port *port;
> +	struct cdn_dp_device *dp;
> +	struct extcon_dev *extcon;
> +	struct phy *phy;
> +	int i;
> +
> +	dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
> +	if (!dp)
> +		return -ENOMEM;
> +	dp->dev = dev;
> +
> +	match = of_match_node(cdn_dp_dt_ids, pdev->dev.of_node);
> +	dp_data = (struct cdn_dp_data *)match->data;
> +
> +	for (i = 0; i < dp_data->max_phy; i++) {
> +		extcon = extcon_get_edev_by_phandle(dev, i);
> +		phy = devm_of_phy_get_by_index(dev, dev->of_node, i);
> +
> +		if (PTR_ERR(extcon) == -EPROBE_DEFER ||
> +		    PTR_ERR(phy) == -EPROBE_DEFER)
> +			return -EPROBE_DEFER;
> +
> +		if (IS_ERR(extcon) || IS_ERR(phy))
> +			continue;
> +
> +		port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
> +		if (!dp)
> +			return -ENOMEM;
> +
> +		port->extcon = extcon;
> +		port->phy = phy;
> +		port->dp = dp;
> +		port->id = i;
> +		dp->port[dp->ports++] = port;
> +	}
> +
> +	if (!dp->ports) {
> +		dev_err(dev, "missing extcon or phy\n");
> +		return -EINVAL;
> +	}
> +
> +	dev_set_drvdata(dev, dp);
> +
> +	return component_add(dev, &cdn_dp_component_ops);
> +}
> +
> +static int cdn_dp_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &cdn_dp_component_ops);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver cdn_dp_driver = {
> +	.probe = cdn_dp_probe,
> +	.remove = cdn_dp_remove,
> +	.driver = {
> +		   .name = "cdn-dp",
> +		   .owner = THIS_MODULE,
> +		   .of_match_table = of_match_ptr(cdn_dp_dt_ids),
> +	},
> +};
> +
> +module_platform_driver(cdn_dp_driver);
> +
> +MODULE_AUTHOR("Chris Zhong <zyw at rock-chips.com>");
> +MODULE_DESCRIPTION("cdn DP Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
> new file mode 100644
> index 0000000..6eb7b72
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
> @@ -0,0 +1,102 @@
> +/*
> + * Copyright (C) 2016 Chris Zhong <zyw at rock-chips.com>
> + * Copyright (C) 2016 ROCKCHIP, Inc.
> + *
> + * 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.
> + */
> +
> +#ifndef _CDN_DP_CORE_H
> +#define _CDN_DP_CORE_H
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_dp_helper.h>
> +#include <drm/drm_panel.h>
> +#include "rockchip_drm_drv.h"
> +
> +#define MAX_PHY		2
> +
> +enum audio_format {
> +	AFMT_I2S = 0,
> +	AFMT_SPDIF = 1,
> +	AFMT_UNUSED,
> +};
> +
> +struct audio_info {
> +	enum audio_format format;
> +	int sample_rate;
> +	int channels;
> +	int sample_width;
> +};
> +
> +enum vic_pxl_encoding_format {
> +	PXL_RGB = 0x1,
> +	YCBCR_4_4_4 = 0x2,
> +	YCBCR_4_2_2 = 0x4,
> +	YCBCR_4_2_0 = 0x8,
> +	Y_ONLY = 0x10,
> +};
> +
> +struct video_info {
> +	bool h_sync_polarity;
> +	bool v_sync_polarity;
> +	bool interlaced;
> +	int color_depth;
> +	enum vic_pxl_encoding_format color_fmt;
> +};
> +
> +struct cdn_firmware_header {
> +	u32 size_bytes; /* size of the entire header+image(s) in bytes */
> +	u32 header_size; /* size of just the header in bytes */
> +	u32 iram_size; /* size of iram */
> +	u32 dram_size; /* size of dram */
> +};
> +
> +struct cdn_dp_port {
> +	struct cdn_dp_device *dp;
> +	struct notifier_block event_nb;
> +	struct delayed_work event_wq;
> +	struct extcon_dev *extcon;
> +	struct phy *phy;
> +	u8 cap_lanes;
> +	bool phy_status;
> +	u8 id;
> +};
> +
> +struct cdn_dp_device {
> +	struct device *dev;
> +	struct drm_device *drm_dev;
> +	struct drm_connector connector;
> +	struct drm_encoder encoder;
> +	struct drm_display_mode mode;
> +	struct platform_device *audio_pdev;
> +
> +	const struct firmware *fw;	/* cdn dp firmware */
> +	unsigned int fw_version;	/* cdn fw version */
> +	u32 fw_wait;
> +	bool fw_loaded;
> +	void __iomem *regs;
> +	struct regmap *grf;
> +	struct clk *core_clk;
> +	struct clk *pclk;
> +	struct clk *spdif_clk;
> +	struct reset_control *spdif_rst;
> +	struct audio_info audio_info;
> +	struct video_info video_info;
> +	struct drm_dp_link link;
> +	struct cdn_dp_port *port[MAX_PHY];
> +	u8 ports;
> +
> +	u8 dpcd[DP_RECEIVER_CAP_SIZE];
> +	enum drm_connector_status hpd_status;
> +	int dpms_mode;
> +	bool sink_has_audio;
> +};
> +#endif  /* _CDN_DP_CORE_H */
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
> new file mode 100644
> index 0000000..a224a6b
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
> @@ -0,0 +1,904 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author: Chris Zhong <zyw at rock-chips.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * 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/clk.h>
> +#include <linux/device.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/reset.h>
> +
> +#include "cdn-dp-core.h"
> +#include "cdn-dp-reg.h"
> +
> +#define CDN_DP_SPDIF_CLK		200000000
> +#define FW_ALIVE_TIMEOUT_US		1000000
> +#define MAILBOX_RETRY_US		1000
> +#define MAILBOX_TIMEOUT_US		5000000
> +#define LINK_TRAINING_RETRY_MS		20
> +#define LINK_TRAINING_TIMEOUT_MS	500
> +
> +void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, u32 clk)
> +{
> +	writel(clk / 1000000, dp->regs + SW_CLK_H);
> +}
> +
> +void cdn_dp_clock_reset(struct cdn_dp_device *dp)
> +{
> +	u32 val;
> +
> +	val = DPTX_FRMR_DATA_CLK_RSTN_EN |
> +	      DPTX_FRMR_DATA_CLK_EN |
> +	      DPTX_PHY_DATA_RSTN_EN |
> +	      DPTX_PHY_DATA_CLK_EN |
> +	      DPTX_PHY_CHAR_RSTN_EN |
> +	      DPTX_PHY_CHAR_CLK_EN |
> +	      SOURCE_AUX_SYS_CLK_RSTN_EN |
> +	      SOURCE_AUX_SYS_CLK_EN |
> +	      DPTX_SYS_CLK_RSTN_EN |
> +	      DPTX_SYS_CLK_EN |
> +	      CFG_DPTX_VIF_CLK_RSTN_EN |
> +	      CFG_DPTX_VIF_CLK_EN;
> +	writel(val, dp->regs + SOURCE_DPTX_CAR);
> +
> +	val = SOURCE_PHY_RSTN_EN | SOURCE_PHY_CLK_EN;
> +	writel(val, dp->regs + SOURCE_PHY_CAR);
> +
> +	val = SOURCE_PKT_SYS_RSTN_EN |
> +	      SOURCE_PKT_SYS_CLK_EN |
> +	      SOURCE_PKT_DATA_RSTN_EN |
> +	      SOURCE_PKT_DATA_CLK_EN;
> +	writel(val, dp->regs + SOURCE_PKT_CAR);
> +
> +	val = SPDIF_CDR_CLK_RSTN_EN |
> +	      SPDIF_CDR_CLK_EN |
> +	      SOURCE_AIF_SYS_RSTN_EN |
> +	      SOURCE_AIF_SYS_CLK_EN |
> +	      SOURCE_AIF_CLK_RSTN_EN |
> +	      SOURCE_AIF_CLK_EN;
> +	writel(val, dp->regs + SOURCE_AIF_CAR);
> +
> +	val = SOURCE_CIPHER_SYSTEM_CLK_RSTN_EN |
> +	      SOURCE_CIPHER_SYS_CLK_EN |
> +	      SOURCE_CIPHER_CHAR_CLK_RSTN_EN |
> +	      SOURCE_CIPHER_CHAR_CLK_EN;
> +	writel(val, dp->regs + SOURCE_CIPHER_CAR);
> +
> +	val = SOURCE_CRYPTO_SYS_CLK_RSTN_EN |
> +	      SOURCE_CRYPTO_SYS_CLK_EN;
> +	writel(val, dp->regs + SOURCE_CRYPTO_CAR);
> +
> +	val = ~(MAILBOX_INT_MASK_BIT | PIF_INT_MASK_BIT) & ALL_INT_MASK;
> +	writel(val, dp->regs + APB_INT_MASK);
> +}
> +
> +static int cdn_dp_mailbox_read(struct cdn_dp_device *dp)
> +{
> +	int val, ret;
> +
> +	if (!dp->fw_loaded)
> +		return -EPERM;
> +
> +	ret = readx_poll_timeout(readl, dp->regs + MAILBOX_EMPTY_ADDR,
> +				 val, !val, MAILBOX_RETRY_US,
> +				 MAILBOX_TIMEOUT_US);
> +	if (ret < 0) {
> +		dev_err(dp->dev, "failed to read mailbox, keep alive = %x\n",
> +			readl(dp->regs + KEEP_ALIVE));
> +		return ret;
> +	}
> +
> +	return readl(dp->regs + MAILBOX0_RD_DATA) & 0xff;
> +}
> +
> +static int cdp_dp_mailbox_write(struct cdn_dp_device *dp, u8 val)
> +{
> +	int ret, full;
> +
> +	if (!dp->fw_loaded)
> +		return -EPERM;
> +
> +	ret = readx_poll_timeout(readl, dp->regs + MAILBOX_FULL_ADDR,
> +				 full, !full, MAILBOX_RETRY_US,
> +				 MAILBOX_TIMEOUT_US);
> +	if (ret < 0) {
> +		dev_err(dp->dev, "mailbox is full, keep alive = %x\n",
> +			readl(dp->regs + KEEP_ALIVE));
> +		return ret;
> +	}
> +
> +	writel(val, dp->regs + MAILBOX0_WR_DATA);
> +
> +	return 0;
> +}
> +
> +static int cdn_dp_mailbox_validate_receive(struct cdn_dp_device *dp,
> +					   u8 module_id, u8 opcode,
> +					   u8 req_size)
> +{
> +	u32 mbox_size, i;
> +	u8 header[4];
> +	int ret;
> +
> +	/* read the header of the message */
> +	for (i = 0; i < 4; i++)  {
> +		ret = cdn_dp_mailbox_read(dp);
> +		if (ret < 0)
> +			return ret;
> +
> +		header[i] = ret;
> +	}
> +
> +	mbox_size = (header[2] << 8) | header[3];
> +
> +	if (opcode != header[0] || module_id != header[1] ||
> +	    req_size != mbox_size) {
> +		dev_err(dp->dev, "mailbox validate failed");
> +
> +		/*
> +		 * If the message in mailbox is not what we want, we need to
> +		 * clear the mailbox by read.
> +		 */
> +		for (i = 0; i < mbox_size; i++)
> +			if (cdn_dp_mailbox_read(dp) < 0)
> +				break;
> +
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int cdn_dp_mailbox_read_receive(struct cdn_dp_device *dp,
> +				       u8 *buff, u8 buff_size)
> +{
> +	u32 i;
> +	int ret;
> +
> +	for (i = 0; i < buff_size; i++) {
> +		ret = cdn_dp_mailbox_read(dp);
> +		if (ret < 0)
> +			return ret;
> +
> +		buff[i] = ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int cdn_dp_mailbox_send(struct cdn_dp_device *dp, u8 module_id,
> +			       u8 opcode, u16 size, u8 *message)
> +{
> +	u8 header[4];
> +	int ret, i;
> +
> +	header[0] = opcode;
> +	header[1] = module_id;
> +	header[2] = (size >> 8) & 0xff;
> +	header[3] = size & 0xff;
> +
> +	for (i = 0; i < 4; i++) {
> +		ret = cdp_dp_mailbox_write(dp, header[i]);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	for (i = 0; i < size; i++) {
> +		ret = cdp_dp_mailbox_write(dp, message[i]);
> +		if (ret) {
> +			dev_err(dp->dev, "mailbox send failed: %d", ret);
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
> +{
> +	u8 msg[6];
> +
> +	msg[0] = (addr >> 8) & 0xff;
> +	msg[1] = addr & 0xff;
> +	msg[2] = (val >> 24) & 0xff;
> +	msg[3] = (val >> 16) & 0xff;
> +	msg[4] = (val >> 8) & 0xff;
> +	msg[5] = val & 0xff;
> +	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_REGISTER,
> +				   sizeof(msg), msg);
> +}
> +
> +int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
> +{
> +	u8 msg[5], reg[5];
> +	int ret;
> +
> +	msg[0] = (len >> 8) & 0xff;
> +	msg[1] = len & 0xff;
> +	msg[2] = (addr >> 16) & 0xff;
> +	msg[3] = (addr >> 8) & 0xff;
> +	msg[4] = addr & 0xff;
> +	cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_READ_DPCD,
> +			    sizeof(msg), msg);
> +
> +	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
> +					      DPTX_READ_DPCD,
> +					      sizeof(reg) + len);
> +	if (ret)
> +		return ret;
> +
> +	ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg));
> +	if (ret)
> +		return ret;
> +
> +	return cdn_dp_mailbox_read_receive(dp, data, len);
> +}
> +
> +int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
> +{
> +	u8 msg[6], reg[5];
> +	int ret;
> +
> +	msg[0] = 0;
> +	msg[1] = 1;
> +	msg[2] = (addr >> 16) & 0xff;
> +	msg[3] = (addr >> 8) & 0xff;
> +	msg[4] = addr & 0xff;
> +	msg[5] = value;
> +	cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_DPCD,
> +			    sizeof(msg), msg);
> +
> +	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
> +					      DPTX_WRITE_DPCD, sizeof(reg));
> +	if (ret)
> +		return ret;
> +
> +	ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg));
> +	if (ret)
> +		return ret;
> +
> +	if (addr != (reg[2] << 16 | reg[3] << 8 | reg[4])) {
> +		dev_err(dp->dev, "write dpcd failed\n");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
> +			 u32 i_size, const u32 *d_mem, u32 d_size)
> +{
> +	int i, ret;
> +	u32 reg;
> +
> +	/* reset ucpu before load firmware*/
> +	writel(APB_IRAM_PATH | APB_DRAM_PATH | APB_XT_RESET,
> +	       dp->regs + APB_CTRL);
> +
> +	for (i = 0; i < i_size; i += 4)
> +		writel(*i_mem++, dp->regs + ADDR_IMEM + i);
> +
> +	for (i = 0; i < d_size; i += 4)
> +		writel(*d_mem++, dp->regs + ADDR_DMEM + i);
> +
> +	/* un-reset ucpu */
> +	writel(0, dp->regs + APB_CTRL);
> +
> +	/* check the keep alive register to make sure fw working */
> +	ret = readx_poll_timeout(readl, dp->regs + KEEP_ALIVE,
> +				 reg, reg, 2000, FW_ALIVE_TIMEOUT_US);
> +	if (ret < 0) {
> +		dev_err(dp->dev, "failed to loaded the FW reg = %x\n", reg);
> +		return -EINVAL;
> +	}
> +
> +	reg = readl(dp->regs + VER_L) & 0xff;
> +	dp->fw_version = reg;
> +	reg = readl(dp->regs + VER_H) & 0xff;
> +	dp->fw_version |= reg << 8;
> +	reg = readl(dp->regs + VER_LIB_L_ADDR) & 0xff;
> +	dp->fw_version |= reg << 16;
> +	reg = readl(dp->regs + VER_LIB_H_ADDR) & 0xff;
> +	dp->fw_version |= reg << 24;
> +
> +	dev_dbg(dp->dev, "firmware version: %x\n", dp->fw_version);
> +
> +	return 0;
> +}
> +
> +int cdn_dp_reg_write_bit(struct cdn_dp_device *dp, u16 addr, u8 start_bit,
> +			 u8 bits_no, u32 val)
> +{
> +	u8 field[8];
> +
> +	field[0] = (addr >> 8) & 0xff;
> +	field[1] = addr & 0xff;
> +	field[2] = start_bit;
> +	field[3] = bits_no;
> +	field[4] = (val >> 24) & 0xff;
> +	field[5] = (val >> 16) & 0xff;
> +	field[6] = (val >> 8) & 0xff;
> +	field[7] = val & 0xff;
> +
> +	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_FIELD,
> +				   sizeof(field), field);
> +}
> +
> +int cdn_dp_active(struct cdn_dp_device *dp, bool enable)
> +{
> +	u8 active = enable ? 1 : 0;
> +	u8 resp;
> +	int ret;
> +
> +	/* set firmware status, 1: active; 0: standby */
> +	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_GENERAL,
> +				  GENERAL_MAIN_CONTROL, sizeof(active),
> +				  &active);
> +	if (ret)
> +		return ret;
> +
> +	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_GENERAL,
> +					      GENERAL_MAIN_CONTROL,
> +					      sizeof(resp));
> +	if (ret)
> +		return ret;
> +
> +	ret = cdn_dp_mailbox_read_receive(dp, &resp, sizeof(resp));
> +	if (ret)
> +		return ret;
> +
> +	return resp ? 0 : -EINVAL;
> +}
> +
> +int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes)
> +{
> +	u8 msg[8];
> +	int ret;
> +
> +	msg[0] = CDN_DP_MAX_LINK_RATE;
> +	msg[1] = lanes;
> +	msg[2] = VOLTAGE_LEVEL_2;
> +	msg[3] = PRE_EMPHASIS_LEVEL_3;
> +	msg[4] = PTS1 | PTS2 | PTS3 | PTS4;
> +	msg[5] = FAST_LT_NOT_SUPPORT;
> +	msg[6] = LANE_MAPPING_NORMAL;
> +	msg[7] = ENHANCED;
> +
> +	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
> +				  DPTX_SET_HOST_CAPABILITIES,
> +				  sizeof(msg), msg);
> +	if (ret)
> +		return ret;
> +
> +	ret = cdn_dp_reg_write(dp, DP_AUX_SWAP_INVERSION_CONTROL,
> +			       AUX_HOST_INVERT);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +int cdn_dp_event_config(struct cdn_dp_device *dp)
> +{
> +	u8 msg[5];
> +
> +	memset(msg, 0, sizeof(msg));
> +
> +	msg[0] = DPTX_EVENT_ENABLE_HPD | DPTX_EVENT_ENABLE_TRAINING;
> +
> +	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_ENABLE_EVENT,
> +				   sizeof(msg), msg);
> +}
> +
> +u32 cdn_dp_get_event(struct cdn_dp_device *dp)
> +{
> +	return readl(dp->regs + SW_EVENTS0);
> +}
> +
> +int cdn_dp_get_hpd_status(struct cdn_dp_device *dp)
> +{
> +	u8 status;
> +	int ret;
> +
> +	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_HPD_STATE,
> +				  0, NULL);
> +	if (ret)
> +		return ret;
> +
> +	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
> +					      DPTX_HPD_STATE, sizeof(status));
> +	if (ret)
> +		return ret;
> +
> +	ret = cdn_dp_mailbox_read_receive(dp, &status, sizeof(status));
> +	if (ret)
> +		return ret;
> +
> +	return status;
> +}
> +
> +int cdn_dp_get_edid_block(void *data, u8 *edid,
> +			  unsigned int block, size_t length)
> +{
> +	struct cdn_dp_device *dp = data;
> +	u8 msg[2], reg[2];
> +	int ret;
> +
> +	msg[0] = block / 2;
> +	msg[1] = block % 2;
> +
> +	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_GET_EDID,
> +				  sizeof(msg), msg);
> +	if (ret)
> +		return ret;
> +
> +	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
> +					      DPTX_GET_EDID,
> +					      sizeof(reg) + length);
> +	if (ret)
> +		return ret;
> +
> +	ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg));
> +	if (ret)
> +		return ret;
> +
> +	ret = cdn_dp_mailbox_read_receive(dp, edid, length);
> +	if (ret)
> +		return ret;
> +
> +	if (reg[0] != length || reg[1] != block / 2) {
> +		dev_err(dp->dev, "edid block size err\n");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +int cdn_dp_training_start(struct cdn_dp_device *dp)
> +{
> +	u8 msg, event[2];
> +	unsigned long timeout;
> +	int ret;
> +
> +	msg = LINK_TRAINING_RUN;
> +
> +	/* start training */
> +	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_TRAINING_CONTROL,
> +				  sizeof(msg), &msg);
> +	if (ret)
> +		return ret;
> +
> +	timeout = jiffies + msecs_to_jiffies(LINK_TRAINING_TIMEOUT_MS);
> +	while (time_before(jiffies, timeout)) {
> +		msleep(LINK_TRAINING_RETRY_MS);
> +		ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
> +					  DPTX_READ_EVENT, 0, NULL);
> +		if (ret)
> +			return ret;
> +
> +		ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
> +						      DPTX_READ_EVENT,
> +						      sizeof(event));
> +		if (ret)
> +			return ret;
> +
> +		ret = cdn_dp_mailbox_read_receive(dp, event, sizeof(event));
> +		if (ret)
> +			return ret;
> +
> +		if (event[1] & EQ_PHASE_FINISHED)
> +			return 0;
> +	}
> +
> +	return -ETIMEDOUT;
> +}
> +
> +int cdn_dp_get_training_status(struct cdn_dp_device *dp)
> +{
> +	u8 status[10];
> +	int ret;
> +
> +	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_READ_LINK_STAT,
> +				  0, NULL);
> +	if (ret)
> +		return ret;
> +
> +	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
> +					      DPTX_READ_LINK_STAT,
> +					      sizeof(status));
> +	if (ret)
> +		return ret;
> +
> +	ret = cdn_dp_mailbox_read_receive(dp, status, sizeof(status));
> +	if (ret)
> +		return ret;
> +
> +	dp->link.rate = status[0];
> +	dp->link.num_lanes = status[1];
> +
> +	return 0;
> +}
> +
> +int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active)
> +{
> +	u8 msg;
> +
> +	msg = !!active;
> +
> +	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_SET_VIDEO,
> +				   sizeof(msg), &msg);
> +}
> +
> +static int cdn_dp_get_msa_misc(struct video_info *video,
> +			       struct drm_display_mode *mode)
> +{
> +	u8 val[2];
> +	u32 msa_misc;
> +
> +	switch (video->color_fmt) {
> +	case PXL_RGB:
> +	case Y_ONLY:
> +		val[0] = 0;
> +		break;
> +	/* set YUV default color space conversion to BT601 */
> +	case YCBCR_4_4_4:
> +		val[0] = 6 + BT_601 * 8;
> +		break;
> +	case YCBCR_4_2_2:
> +		val[0] = 5 + BT_601 * 8;
> +		break;
> +	case YCBCR_4_2_0:
> +		val[0] = 5;
> +		break;
> +	};
> +
> +	switch (video->color_depth) {
> +	case 6:
> +		val[1] = 0;
> +		break;
> +	case 8:
> +		val[1] = 1;
> +		break;
> +	case 10:
> +		val[1] = 2;
> +		break;
> +	case 12:
> +		val[1] = 3;
> +		break;
> +	case 16:
> +		val[1] = 4;
> +		break;
> +	};
> +
> +	msa_misc = 2 * val[0] + 32 * val[1] +
> +		   ((video->color_fmt == Y_ONLY) ? (1 << 14) : 0);
> +
> +	return msa_misc;
> +}
> +
> +int cdn_dp_config_video(struct cdn_dp_device *dp)
> +{
> +	struct video_info *video = &dp->video_info;
> +	struct drm_display_mode *mode = &dp->mode;
> +	u64 symbol;
> +	u32 val, link_rate;
> +	u8 bit_per_pix;
> +	u8 tu_size_reg = TU_SIZE;
> +	int ret;
> +
> +	bit_per_pix = (video->color_fmt == YCBCR_4_2_2) ?
> +		      (video->color_depth * 2) : (video->color_depth * 3);
> +
> +	link_rate = drm_dp_bw_code_to_link_rate(dp->link.rate);
> +
> +	val = VIF_BYPASS_INTERLACE;
> +	ret = cdn_dp_reg_write(dp, BND_HSYNC2VSYNC, val);
> +	if (ret)
> +		return ret;
> +
> +	ret = cdn_dp_reg_write(dp, HSYNC2VSYNC_POL_CTRL, 0);
> +	if (ret)
> +		return ret;
> +
> +	/* get a best tu_size and symbol */
> +	do {
> +		tu_size_reg += 2;
> +		symbol = tu_size_reg * mode->clock * bit_per_pix;
> +		symbol /= dp->link.num_lanes * link_rate * 8;
> +	} while ((symbol == 1) || (tu_size_reg - symbol < 4));
> +
> +	val = symbol + (tu_size_reg << 8);
> +	ret = cdn_dp_reg_write(dp, DP_FRAMER_TU, val);
> +	if (ret)
> +		return ret;
> +
> +	/* set the FIFO Buffer size */
> +	val = ((mode->clock * (symbol + 1) / 1000) + link_rate);
> +	val /= (dp->link.num_lanes * link_rate);
> +	val = 8 * (symbol + 1) / bit_per_pix - val;
> +	val += 2;
> +	ret = cdn_dp_reg_write(dp, DP_VC_TABLE(15), val);
> +
> +	switch (video->color_depth) {
> +	case 6:
> +		val = BCS_6;
> +		break;
> +	case 8:
> +		val = BCS_8;
> +		break;
> +	case 10:
> +		val = BCS_10;
> +		break;
> +	case 12:
> +		val = BCS_12;
> +		break;
> +	case 16:
> +		val = BCS_16;
> +		break;
> +	};
> +
> +	val += video->color_fmt << 8;
> +	ret = cdn_dp_reg_write(dp, DP_FRAMER_PXL_REPR, val);
> +	if (ret)
> +		return ret;
> +
> +	val = video->h_sync_polarity ? DP_FRAMER_SP_HSP : 0;
> +	val |= video->v_sync_polarity ? DP_FRAMER_SP_VSP : 0;
> +	ret = cdn_dp_reg_write(dp, DP_FRAMER_SP, val);
> +	if (ret)
> +		return ret;
> +
> +	val = (mode->hsync_start - mode->hdisplay) << 16;
> +	val |= mode->htotal - mode->hsync_end;
> +	ret = cdn_dp_reg_write(dp, DP_FRONT_BACK_PORCH, val);
> +	if (ret)
> +		return ret;
> +
> +	val = mode->hdisplay * bit_per_pix / 8;
> +	ret = cdn_dp_reg_write(dp, DP_BYTE_COUNT, val);
> +	if (ret)
> +		return ret;
> +
> +	val = mode->htotal | ((mode->htotal - mode->hsync_start) << 16);
> +	ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_0, val);
> +	if (ret)
> +		return ret;
> +
> +	val = mode->hsync_end - mode->hsync_start;
> +	val |= (mode->hdisplay << 16) | (video->h_sync_polarity << 15);
> +	ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_1, val);
> +	if (ret)
> +		return ret;
> +
> +	val = mode->vtotal;
> +	val |= ((mode->vtotal - mode->vsync_start) << 16);
> +	ret = cdn_dp_reg_write(dp, MSA_VERTICAL_0, val);
> +	if (ret)
> +		return ret;
> +
> +	val = mode->vsync_end - mode->vsync_start;
> +	val |= mode->vdisplay << 16 | (video->v_sync_polarity << 15);
> +	ret = cdn_dp_reg_write(dp, MSA_VERTICAL_1, val);
> +	if (ret)
> +		return ret;
> +
> +	val = cdn_dp_get_msa_misc(video, mode);
> +	ret = cdn_dp_reg_write(dp, MSA_MISC, val);
> +	if (ret)
> +		return ret;
> +
> +	ret = cdn_dp_reg_write(dp, STREAM_CONFIG, 1);
> +	if (ret)
> +		return ret;
> +
> +	val = mode->hsync_end - mode->hsync_start;
> +	val |= (mode->hdisplay << 16);
> +	ret = cdn_dp_reg_write(dp, DP_HORIZONTAL, val);
> +	if (ret)
> +		return ret;
> +
> +	val = mode->vtotal;
> +	val -= (mode->vtotal - mode->vdisplay);
> +	val |= (mode->vtotal - mode->vsync_start) << 16;
> +	ret = cdn_dp_reg_write(dp, DP_VERTICAL_0, val);
> +	if (ret)
> +		return ret;
> +
> +	val = mode->vtotal;
> +	ret = cdn_dp_reg_write(dp, DP_VERTICAL_1, val);
> +	if (ret)
> +		return ret;
> +
> +	val =  0;
> +	return cdn_dp_reg_write_bit(dp, DP_VB_ID, 2, 1, val);
> +}
> +
> +int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio)
> +{
> +	u32 val;
> +	int ret;
> +
> +	ret = cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, 0);
> +	if (ret)
> +		return ret;
> +
> +	val = SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS;
> +	val |= SPDIF_FIFO_MID_RANGE(0xe0);
> +	val |= SPDIF_JITTER_THRSH(0xe0);
> +	val |= SPDIF_JITTER_AVG_WIN(7);
> +	writel(val, dp->regs + SPDIF_CTRL_ADDR);
> +
> +	/* clearn the audio config and reset */
> +	writel(0, dp->regs + AUDIO_SRC_CNTL);
> +	writel(0, dp->regs + AUDIO_SRC_CNFG);
> +	writel(AUDIO_SW_RST, dp->regs + AUDIO_SRC_CNTL);
> +	writel(0, dp->regs + AUDIO_SRC_CNTL);
> +
> +	/* reset smpl2pckt component  */
> +	writel(0, dp->regs + SMPL2PKT_CNTL);
> +	writel(AUDIO_SW_RST, dp->regs + SMPL2PKT_CNTL);
> +	writel(0, dp->regs + SMPL2PKT_CNTL);
> +
> +	/* reset FIFO */
> +	writel(AUDIO_SW_RST, dp->regs + FIFO_CNTL);
> +	writel(0, dp->regs + FIFO_CNTL);
> +
> +	if (audio->format == AFMT_SPDIF)
> +		clk_disable_unprepare(dp->spdif_clk);
> +
> +	return 0;
> +}
> +
> +int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable)
> +{
> +	return cdn_dp_reg_write_bit(dp, DP_VB_ID, 4, 1, enable);
> +}
> +
> +static void cdn_dp_audio_config_i2s(struct cdn_dp_device *dp,
> +				    struct audio_info *audio)
> +{
> +	int sub_pckt_num = 1, i2s_port_en_val = 0xf, i;
> +	u32 val;
> +
> +	if (audio->channels == 2) {
> +		if (dp->link.num_lanes == 1)
> +			sub_pckt_num = 2;
> +		else
> +			sub_pckt_num = 4;
> +
> +		i2s_port_en_val = 1;
> +	} else if (audio->channels == 4) {
> +		i2s_port_en_val = 3;
> +	}
> +
> +	writel(0x0, dp->regs + SPDIF_CTRL_ADDR);
> +
> +	writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL);
> +
> +	val = MAX_NUM_CH(audio->channels);
> +	val |= NUM_OF_I2S_PORTS(audio->channels);
> +	val |= AUDIO_TYPE_LPCM;
> +	val |= CFG_SUB_PCKT_NUM(sub_pckt_num);
> +	writel(val, dp->regs + SMPL2PKT_CNFG);
> +
> +	if (audio->sample_width == 16)
> +		val = 0;
> +	else if (audio->sample_width == 24)
> +		val = 1 << 9;
> +	else
> +		val = 2 << 9;
> +
> +	val |= AUDIO_CH_NUM(audio->channels);
> +	val |= I2S_DEC_PORT_EN(i2s_port_en_val);
> +	val |= TRANS_SMPL_WIDTH_32;
> +	writel(val, dp->regs + AUDIO_SRC_CNFG);
> +
> +	for (i = 0; i < (audio->channels + 1) / 2; i++) {
> +		if (audio->sample_width == 16)
> +			val = (0x08 << 8) | (0x08 << 20);
> +		else if (audio->sample_width == 24)
> +			val = (0x0b << 8) | (0x0b << 20);
> +
> +		val |= ((2 * i) << 4) | ((2 * i + 1) << 16);
> +		writel(val, dp->regs + STTS_BIT_CH(i));
> +	}
> +
> +	switch (audio->sample_rate) {
> +	case 32000:
> +		val = SAMPLING_FREQ(3) |
> +		      ORIGINAL_SAMP_FREQ(0xc);
> +		break;
> +	case 44100:
> +		val = SAMPLING_FREQ(0) |
> +		      ORIGINAL_SAMP_FREQ(0xf);
> +		break;
> +	case 48000:
> +		val = SAMPLING_FREQ(2) |
> +		      ORIGINAL_SAMP_FREQ(0xd);
> +		break;
> +	case 88200:
> +		val = SAMPLING_FREQ(8) |
> +		      ORIGINAL_SAMP_FREQ(0x7);
> +		break;
> +	case 96000:
> +		val = SAMPLING_FREQ(0xa) |
> +		      ORIGINAL_SAMP_FREQ(5);
> +		break;
> +	case 176400:
> +		val = SAMPLING_FREQ(0xc) |
> +		      ORIGINAL_SAMP_FREQ(3);
> +		break;
> +	case 192000:
> +		val = SAMPLING_FREQ(0xe) |
> +		      ORIGINAL_SAMP_FREQ(1);
> +		break;
> +	}
> +	val |= 4;
> +	writel(val, dp->regs + COM_CH_STTS_BITS);
> +
> +	writel(SMPL2PKT_EN, dp->regs + SMPL2PKT_CNTL);
> +	writel(I2S_DEC_START, dp->regs + AUDIO_SRC_CNTL);
> +}
> +
> +static void cdn_dp_audio_config_spdif(struct cdn_dp_device *dp)
> +{
> +	u32 val;
> +
> +	val = SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS;
> +	val |= SPDIF_FIFO_MID_RANGE(0xe0);
> +	val |= SPDIF_JITTER_THRSH(0xe0);
> +	val |= SPDIF_JITTER_AVG_WIN(7);
> +	writel(val, dp->regs + SPDIF_CTRL_ADDR);
> +
> +	writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL);
> +
> +	val = MAX_NUM_CH(2) | AUDIO_TYPE_LPCM | CFG_SUB_PCKT_NUM(4);
> +	writel(val, dp->regs + SMPL2PKT_CNFG);
> +	writel(SMPL2PKT_EN, dp->regs + SMPL2PKT_CNTL);
> +
> +	val = SPDIF_ENABLE | SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS;
> +	val |= SPDIF_FIFO_MID_RANGE(0xe0);
> +	val |= SPDIF_JITTER_THRSH(0xe0);
> +	val |= SPDIF_JITTER_AVG_WIN(7);
> +	writel(val, dp->regs + SPDIF_CTRL_ADDR);
> +
> +	clk_prepare_enable(dp->spdif_clk);
> +	clk_set_rate(dp->spdif_clk, CDN_DP_SPDIF_CLK);
> +}
> +
> +int cdn_dp_audio_config_set(struct cdn_dp_device *dp, struct audio_info *audio)
> +{
> +	int ret;
> +
> +	/* reset the spdif clk before config */
> +	if (audio->format == AFMT_SPDIF) {
> +		reset_control_assert(dp->spdif_rst);
> +		reset_control_deassert(dp->spdif_rst);
> +	}
> +
> +	ret = cdn_dp_reg_write(dp, CM_LANE_CTRL, LANE_REF_CYC);
> +	if (ret)
> +		return ret;
> +
> +	ret = cdn_dp_reg_write(dp, CM_CTRL, 0);
> +	if (ret)
> +		return ret;
> +
> +	if (audio->format == AFMT_I2S)
> +		cdn_dp_audio_config_i2s(dp, audio);
> +	else
> +		cdn_dp_audio_config_spdif(dp);
> +
> +	return cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, AUDIO_PACK_EN);
> +}
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
> new file mode 100644
> index 0000000..1affeed
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
> @@ -0,0 +1,479 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author: Chris Zhong <zyw at rock-chips.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * 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.
> + */
> +
> +#ifndef _CDN_DP_REG_H
> +#define _CDN_DP_REG_H
> +
> +#include <linux/bitops.h>
> +
> +#define ADDR_IMEM		0x10000
> +#define ADDR_DMEM		0x20000
> +
> +/* APB CFG addr */
> +#define APB_CTRL			0
> +#define XT_INT_CTRL			0x04
> +#define MAILBOX_FULL_ADDR		0x08
> +#define MAILBOX_EMPTY_ADDR		0x0c
> +#define MAILBOX0_WR_DATA		0x10
> +#define MAILBOX0_RD_DATA		0x14
> +#define KEEP_ALIVE			0x18
> +#define VER_L				0x1c
> +#define VER_H				0x20
> +#define VER_LIB_L_ADDR			0x24
> +#define VER_LIB_H_ADDR			0x28
> +#define SW_DEBUG_L			0x2c
> +#define SW_DEBUG_H			0x30
> +#define MAILBOX_INT_MASK		0x34
> +#define MAILBOX_INT_STATUS		0x38
> +#define SW_CLK_L			0x3c
> +#define SW_CLK_H			0x40
> +#define SW_EVENTS0			0x44
> +#define SW_EVENTS1			0x48
> +#define SW_EVENTS2			0x4c
> +#define SW_EVENTS3			0x50
> +#define XT_OCD_CTRL			0x60
> +#define APB_INT_MASK			0x6c
> +#define APB_STATUS_MASK			0x70
> +
> +/* audio decoder addr */
> +#define AUDIO_SRC_CNTL			0x30000
> +#define AUDIO_SRC_CNFG			0x30004
> +#define COM_CH_STTS_BITS		0x30008
> +#define STTS_BIT_CH(x)			(0x3000c + ((x) << 2))
> +#define SPDIF_CTRL_ADDR			0x3004c
> +#define SPDIF_CH1_CS_3100_ADDR		0x30050
> +#define SPDIF_CH1_CS_6332_ADDR		0x30054
> +#define SPDIF_CH1_CS_9564_ADDR		0x30058
> +#define SPDIF_CH1_CS_12796_ADDR		0x3005c
> +#define SPDIF_CH1_CS_159128_ADDR	0x30060
> +#define SPDIF_CH1_CS_191160_ADDR	0x30064
> +#define SPDIF_CH2_CS_3100_ADDR		0x30068
> +#define SPDIF_CH2_CS_6332_ADDR		0x3006c
> +#define SPDIF_CH2_CS_9564_ADDR		0x30070
> +#define SPDIF_CH2_CS_12796_ADDR		0x30074
> +#define SPDIF_CH2_CS_159128_ADDR	0x30078
> +#define SPDIF_CH2_CS_191160_ADDR	0x3007c
> +#define SMPL2PKT_CNTL			0x30080
> +#define SMPL2PKT_CNFG			0x30084
> +#define FIFO_CNTL			0x30088
> +#define FIFO_STTS			0x3008c
> +
> +/* source pif addr */
> +#define SOURCE_PIF_WR_ADDR		0x30800
> +#define SOURCE_PIF_WR_REQ		0x30804
> +#define SOURCE_PIF_RD_ADDR		0x30808
> +#define SOURCE_PIF_RD_REQ		0x3080c
> +#define SOURCE_PIF_DATA_WR		0x30810
> +#define SOURCE_PIF_DATA_RD		0x30814
> +#define SOURCE_PIF_FIFO1_FLUSH		0x30818
> +#define SOURCE_PIF_FIFO2_FLUSH		0x3081c
> +#define SOURCE_PIF_STATUS		0x30820
> +#define SOURCE_PIF_INTERRUPT_SOURCE	0x30824
> +#define SOURCE_PIF_INTERRUPT_MASK	0x30828
> +#define SOURCE_PIF_PKT_ALLOC_REG	0x3082c
> +#define SOURCE_PIF_PKT_ALLOC_WR_EN	0x30830
> +#define SOURCE_PIF_SW_RESET		0x30834
> +
> +/* bellow registers need access by mailbox */
> +/* source car addr */
> +#define SOURCE_HDTX_CAR			0x0900
> +#define SOURCE_DPTX_CAR			0x0904
> +#define SOURCE_PHY_CAR			0x0908
> +#define SOURCE_CEC_CAR			0x090c
> +#define SOURCE_CBUS_CAR			0x0910
> +#define SOURCE_PKT_CAR			0x0918
> +#define SOURCE_AIF_CAR			0x091c
> +#define SOURCE_CIPHER_CAR		0x0920
> +#define SOURCE_CRYPTO_CAR		0x0924
> +
> +/* clock meters addr */
> +#define CM_CTRL				0x0a00
> +#define CM_I2S_CTRL			0x0a04
> +#define CM_SPDIF_CTRL			0x0a08
> +#define CM_VID_CTRL			0x0a0c
> +#define CM_LANE_CTRL			0x0a10
> +#define I2S_NM_STABLE			0x0a14
> +#define I2S_NCTS_STABLE			0x0a18
> +#define SPDIF_NM_STABLE			0x0a1c
> +#define SPDIF_NCTS_STABLE		0x0a20
> +#define NMVID_MEAS_STABLE		0x0a24
> +#define I2S_MEAS			0x0a40
> +#define SPDIF_MEAS			0x0a80
> +#define NMVID_MEAS			0x0ac0
> +
> +/* source vif addr */
> +#define BND_HSYNC2VSYNC			0x0b00
> +#define HSYNC2VSYNC_F1_L1		0x0b04
> +#define HSYNC2VSYNC_F2_L1		0x0b08
> +#define HSYNC2VSYNC_STATUS		0x0b0c
> +#define HSYNC2VSYNC_POL_CTRL		0x0b10
> +
> +/* dptx phy addr */
> +#define DP_TX_PHY_CONFIG_REG		0x2000
> +#define DP_TX_PHY_STATUS_REG		0x2004
> +#define DP_TX_PHY_SW_RESET		0x2008
> +#define DP_TX_PHY_SCRAMBLER_SEED	0x200c
> +#define DP_TX_PHY_TRAINING_01_04	0x2010
> +#define DP_TX_PHY_TRAINING_05_08	0x2014
> +#define DP_TX_PHY_TRAINING_09_10	0x2018
> +#define TEST_COR			0x23fc
> +
> +/* dptx hpd addr */
> +#define HPD_IRQ_DET_MIN_TIMER		0x2100
> +#define HPD_IRQ_DET_MAX_TIMER		0x2104
> +#define HPD_UNPLGED_DET_MIN_TIMER	0x2108
> +#define HPD_STABLE_TIMER		0x210c
> +#define HPD_FILTER_TIMER		0x2110
> +#define HPD_EVENT_MASK			0x211c
> +#define HPD_EVENT_DET			0x2120
> +
> +/* dpyx framer addr */
> +#define DP_FRAMER_GLOBAL_CONFIG		0x2200
> +#define DP_SW_RESET			0x2204
> +#define DP_FRAMER_TU			0x2208
> +#define DP_FRAMER_PXL_REPR		0x220c
> +#define DP_FRAMER_SP			0x2210
> +#define AUDIO_PACK_CONTROL		0x2214
> +#define DP_VC_TABLE(x)			(0x2218 + ((x) << 2))
> +#define DP_VB_ID			0x2258
> +#define DP_MTPH_LVP_CONTROL		0x225c
> +#define DP_MTPH_SYMBOL_VALUES		0x2260
> +#define DP_MTPH_ECF_CONTROL		0x2264
> +#define DP_MTPH_ACT_CONTROL		0x2268
> +#define DP_MTPH_STATUS			0x226c
> +#define DP_INTERRUPT_SOURCE		0x2270
> +#define DP_INTERRUPT_MASK		0x2274
> +#define DP_FRONT_BACK_PORCH		0x2278
> +#define DP_BYTE_COUNT			0x227c
> +
> +/* dptx stream addr */
> +#define MSA_HORIZONTAL_0		0x2280
> +#define MSA_HORIZONTAL_1		0x2284
> +#define MSA_VERTICAL_0			0x2288
> +#define MSA_VERTICAL_1			0x228c
> +#define MSA_MISC			0x2290
> +#define STREAM_CONFIG			0x2294
> +#define AUDIO_PACK_STATUS		0x2298
> +#define VIF_STATUS			0x229c
> +#define PCK_STUFF_STATUS_0		0x22a0
> +#define PCK_STUFF_STATUS_1		0x22a4
> +#define INFO_PACK_STATUS		0x22a8
> +#define RATE_GOVERNOR_STATUS		0x22ac
> +#define DP_HORIZONTAL			0x22b0
> +#define DP_VERTICAL_0			0x22b4
> +#define DP_VERTICAL_1			0x22b8
> +#define DP_BLOCK_SDP			0x22bc
> +
> +/* dptx glbl addr */
> +#define DPTX_LANE_EN			0x2300
> +#define DPTX_ENHNCD			0x2304
> +#define DPTX_INT_MASK			0x2308
> +#define DPTX_INT_STATUS			0x230c
> +
> +/* dp aux addr */
> +#define DP_AUX_HOST_CONTROL		0x2800
> +#define DP_AUX_INTERRUPT_SOURCE		0x2804
> +#define DP_AUX_INTERRUPT_MASK		0x2808
> +#define DP_AUX_SWAP_INVERSION_CONTROL	0x280c
> +#define DP_AUX_SEND_NACK_TRANSACTION	0x2810
> +#define DP_AUX_CLEAR_RX			0x2814
> +#define DP_AUX_CLEAR_TX			0x2818
> +#define DP_AUX_TIMER_STOP		0x281c
> +#define DP_AUX_TIMER_CLEAR		0x2820
> +#define DP_AUX_RESET_SW			0x2824
> +#define DP_AUX_DIVIDE_2M		0x2828
> +#define DP_AUX_TX_PREACHARGE_LENGTH	0x282c
> +#define DP_AUX_FREQUENCY_1M_MAX		0x2830
> +#define DP_AUX_FREQUENCY_1M_MIN		0x2834
> +#define DP_AUX_RX_PRE_MIN		0x2838
> +#define DP_AUX_RX_PRE_MAX		0x283c
> +#define DP_AUX_TIMER_PRESET		0x2840
> +#define DP_AUX_NACK_FORMAT		0x2844
> +#define DP_AUX_TX_DATA			0x2848
> +#define DP_AUX_RX_DATA			0x284c
> +#define DP_AUX_TX_STATUS		0x2850
> +#define DP_AUX_RX_STATUS		0x2854
> +#define DP_AUX_RX_CYCLE_COUNTER		0x2858
> +#define DP_AUX_MAIN_STATES		0x285c
> +#define DP_AUX_MAIN_TIMER		0x2860
> +#define DP_AUX_AFE_OUT			0x2864
> +
> +/* crypto addr */
> +#define CRYPTO_HDCP_REVISION		0x5800
> +#define HDCP_CRYPTO_CONFIG		0x5804
> +#define CRYPTO_INTERRUPT_SOURCE		0x5808
> +#define CRYPTO_INTERRUPT_MASK		0x580c
> +#define CRYPTO22_CONFIG			0x5818
> +#define CRYPTO22_STATUS			0x581c
> +#define SHA_256_DATA_IN			0x583c
> +#define SHA_256_DATA_OUT_(x)		(0x5850 + ((x) << 2))
> +#define AES_32_KEY_(x)			(0x5870 + ((x) << 2))
> +#define AES_32_DATA_IN			0x5880
> +#define AES_32_DATA_OUT_(x)		(0x5884 + ((x) << 2))
> +#define CRYPTO14_CONFIG			0x58a0
> +#define CRYPTO14_STATUS			0x58a4
> +#define CRYPTO14_PRNM_OUT		0x58a8
> +#define CRYPTO14_KM_0			0x58ac
> +#define CRYPTO14_KM_1			0x58b0
> +#define CRYPTO14_AN_0			0x58b4
> +#define CRYPTO14_AN_1			0x58b8
> +#define CRYPTO14_YOUR_KSV_0		0x58bc
> +#define CRYPTO14_YOUR_KSV_1		0x58c0
> +#define CRYPTO14_MI_0			0x58c4
> +#define CRYPTO14_MI_1			0x58c8
> +#define CRYPTO14_TI_0			0x58cc
> +#define CRYPTO14_KI_0			0x58d0
> +#define CRYPTO14_KI_1			0x58d4
> +#define CRYPTO14_BLOCKS_NUM		0x58d8
> +#define CRYPTO14_KEY_MEM_DATA_0		0x58dc
> +#define CRYPTO14_KEY_MEM_DATA_1		0x58e0
> +#define CRYPTO14_SHA1_MSG_DATA		0x58e4
> +#define CRYPTO14_SHA1_V_VALUE_(x)	(0x58e8 + ((x) << 2))
> +#define TRNG_CTRL			0x58fc
> +#define TRNG_DATA_RDY			0x5900
> +#define TRNG_DATA			0x5904
> +
> +/* cipher addr */
> +#define HDCP_REVISION			0x60000
> +#define INTERRUPT_SOURCE		0x60004
> +#define INTERRUPT_MASK			0x60008
> +#define HDCP_CIPHER_CONFIG		0x6000c
> +#define AES_128_KEY_0			0x60010
> +#define AES_128_KEY_1			0x60014
> +#define AES_128_KEY_2			0x60018
> +#define AES_128_KEY_3			0x6001c
> +#define AES_128_RANDOM_0		0x60020
> +#define AES_128_RANDOM_1		0x60024
> +#define CIPHER14_KM_0			0x60028
> +#define CIPHER14_KM_1			0x6002c
> +#define CIPHER14_STATUS			0x60030
> +#define CIPHER14_RI_PJ_STATUS		0x60034
> +#define CIPHER_MODE			0x60038
> +#define CIPHER14_AN_0			0x6003c
> +#define CIPHER14_AN_1			0x60040
> +#define CIPHER22_AUTH			0x60044
> +#define CIPHER14_R0_DP_STATUS		0x60048
> +#define CIPHER14_BOOTSTRAP		0x6004c
> +
> +#define DPTX_FRMR_DATA_CLK_RSTN_EN	BIT(11)
> +#define DPTX_FRMR_DATA_CLK_EN		BIT(10)
> +#define DPTX_PHY_DATA_RSTN_EN		BIT(9)
> +#define DPTX_PHY_DATA_CLK_EN		BIT(8)
> +#define DPTX_PHY_CHAR_RSTN_EN		BIT(7)
> +#define DPTX_PHY_CHAR_CLK_EN		BIT(6)
> +#define SOURCE_AUX_SYS_CLK_RSTN_EN	BIT(5)
> +#define SOURCE_AUX_SYS_CLK_EN		BIT(4)
> +#define DPTX_SYS_CLK_RSTN_EN		BIT(3)
> +#define DPTX_SYS_CLK_EN			BIT(2)
> +#define CFG_DPTX_VIF_CLK_RSTN_EN	BIT(1)
> +#define CFG_DPTX_VIF_CLK_EN		BIT(0)
> +
> +#define SOURCE_PHY_RSTN_EN		BIT(1)
> +#define SOURCE_PHY_CLK_EN		BIT(0)
> +
> +#define SOURCE_PKT_SYS_RSTN_EN		BIT(3)
> +#define SOURCE_PKT_SYS_CLK_EN		BIT(2)
> +#define SOURCE_PKT_DATA_RSTN_EN		BIT(1)
> +#define SOURCE_PKT_DATA_CLK_EN		BIT(0)
> +
> +#define SPDIF_CDR_CLK_RSTN_EN		BIT(5)
> +#define SPDIF_CDR_CLK_EN		BIT(4)
> +#define SOURCE_AIF_SYS_RSTN_EN		BIT(3)
> +#define SOURCE_AIF_SYS_CLK_EN		BIT(2)
> +#define SOURCE_AIF_CLK_RSTN_EN		BIT(1)
> +#define SOURCE_AIF_CLK_EN		BIT(0)
> +
> +#define SOURCE_CIPHER_SYSTEM_CLK_RSTN_EN	BIT(3)
> +#define SOURCE_CIPHER_SYS_CLK_EN		BIT(2)
> +#define SOURCE_CIPHER_CHAR_CLK_RSTN_EN		BIT(1)
> +#define SOURCE_CIPHER_CHAR_CLK_EN		BIT(0)
> +
> +#define SOURCE_CRYPTO_SYS_CLK_RSTN_EN	BIT(1)
> +#define SOURCE_CRYPTO_SYS_CLK_EN	BIT(0)
> +
> +#define APB_IRAM_PATH			BIT(2)
> +#define APB_DRAM_PATH			BIT(1)
> +#define APB_XT_RESET			BIT(0)
> +
> +#define MAILBOX_INT_MASK_BIT		BIT(1)
> +#define PIF_INT_MASK_BIT		BIT(0)
> +#define ALL_INT_MASK			3
> +
> +/* mailbox */
> +#define MB_OPCODE_ID			0
> +#define MB_MODULE_ID			1
> +#define MB_SIZE_MSB_ID			2
> +#define MB_SIZE_LSB_ID			3
> +#define MB_DATA_ID			4
> +
> +#define MB_MODULE_ID_DP_TX		0x01
> +#define MB_MODULE_ID_HDCP_TX		0x07
> +#define MB_MODULE_ID_HDCP_RX		0x08
> +#define MB_MODULE_ID_HDCP_GENERAL	0x09
> +#define MB_MODULE_ID_GENERAL		0x0a
> +
> +/* general opcode */
> +#define GENERAL_MAIN_CONTROL            0x01
> +#define GENERAL_TEST_ECHO               0x02
> +#define GENERAL_BUS_SETTINGS            0x03
> +#define GENERAL_TEST_ACCESS             0x04
> +
> +#define DPTX_SET_POWER_MNG			0x00
> +#define DPTX_SET_HOST_CAPABILITIES		0x01
> +#define DPTX_GET_EDID				0x02
> +#define DPTX_READ_DPCD				0x03
> +#define DPTX_WRITE_DPCD				0x04
> +#define DPTX_ENABLE_EVENT			0x05
> +#define DPTX_WRITE_REGISTER			0x06
> +#define DPTX_READ_REGISTER			0x07
> +#define DPTX_WRITE_FIELD			0x08
> +#define DPTX_TRAINING_CONTROL			0x09
> +#define DPTX_READ_EVENT				0x0a
> +#define DPTX_READ_LINK_STAT			0x0b
> +#define DPTX_SET_VIDEO				0x0c
> +#define DPTX_SET_AUDIO				0x0d
> +#define DPTX_GET_LAST_AUX_STAUS			0x0e
> +#define DPTX_SET_LINK_BREAK_POINT		0x0f
> +#define DPTX_FORCE_LANES			0x10
> +#define DPTX_HPD_STATE				0x11
> +
> +#define DPTX_EVENT_ENABLE_HPD			BIT(0)
> +#define DPTX_EVENT_ENABLE_TRAINING		BIT(1)
> +
> +#define LINK_TRAINING_NOT_ACTIVE		0
> +#define LINK_TRAINING_RUN			1
> +#define LINK_TRAINING_RESTART			2
> +
> +#define CONTROL_VIDEO_IDLE			0
> +#define CONTROL_VIDEO_VALID			1
> +
> +#define VIF_BYPASS_INTERLACE			BIT(13)
> +#define INTERLACE_FMT_DET			BIT(12)
> +#define INTERLACE_DTCT_WIN			0x20
> +
> +#define DP_FRAMER_SP_INTERLACE_EN		BIT(2)
> +#define DP_FRAMER_SP_HSP			BIT(1)
> +#define DP_FRAMER_SP_VSP			BIT(0)
> +
> +/* capability */
> +#define AUX_HOST_INVERT				3
> +#define	FAST_LT_SUPPORT				1
> +#define FAST_LT_NOT_SUPPORT			0
> +#define LANE_MAPPING_NORMAL			0xe4
> +#define LANE_MAPPING_FLIPPED			0x1b
> +#define ENHANCED				1
> +
> +#define	FULL_LT_STARTED				BIT(0)
> +#define FASE_LT_STARTED				BIT(1)
> +#define CLK_RECOVERY_FINISHED			BIT(2)
> +#define EQ_PHASE_FINISHED			BIT(3)
> +#define FASE_LT_START_FINISHED			BIT(4)
> +#define CLK_RECOVERY_FAILED			BIT(5)
> +#define EQ_PHASE_FAILED				BIT(6)
> +#define FASE_LT_FAILED				BIT(7)
> +
> +#define DPTX_HPD_EVENT				BIT(0)
> +#define DPTX_TRAINING_EVENT			BIT(1)
> +#define HDCP_TX_STATUS_EVENT			BIT(4)
> +#define HDCP2_TX_IS_KM_STORED_EVENT		BIT(5)
> +#define HDCP2_TX_STORE_KM_EVENT			BIT(6)
> +#define HDCP_TX_IS_RECEIVER_ID_VALID_EVENT	BIT(7)
> +
> +#define TU_SIZE					30
> +#define CDN_DP_MAX_LINK_RATE			DP_LINK_BW_5_4
> +
> +/* audio */
> +#define AUDIO_PACK_EN				BIT(8)
> +#define SAMPLING_FREQ(x)			(((x) & 0xf) << 16)
> +#define ORIGINAL_SAMP_FREQ(x)			(((x) & 0xf) << 24)
> +#define SYNC_WR_TO_CH_ZERO			BIT(1)
> +#define I2S_DEC_START				BIT(1)
> +#define AUDIO_SW_RST				BIT(0)
> +#define SMPL2PKT_EN				BIT(1)
> +#define MAX_NUM_CH(x)				(((x) & 0x1f) - 1)
> +#define NUM_OF_I2S_PORTS(x)			((((x) / 2 - 1) & 0x3) << 5)
> +#define AUDIO_TYPE_LPCM				(2 << 7)
> +#define CFG_SUB_PCKT_NUM(x)			((((x) - 1) & 0x7) << 11)
> +#define AUDIO_CH_NUM(x)				((((x) - 1) & 0x1f) << 2)
> +#define TRANS_SMPL_WIDTH_16			0
> +#define TRANS_SMPL_WIDTH_24			BIT(11)
> +#define TRANS_SMPL_WIDTH_32			(2 << 11)
> +#define I2S_DEC_PORT_EN(x)			(((x) & 0xf) << 17)
> +#define SPDIF_ENABLE				BIT(21)
> +#define SPDIF_AVG_SEL				BIT(20)
> +#define SPDIF_JITTER_BYPASS			BIT(19)
> +#define SPDIF_FIFO_MID_RANGE(x)			(((x) & 0xff) << 11)
> +#define SPDIF_JITTER_THRSH(x)			(((x) & 0xff) << 3)
> +#define SPDIF_JITTER_AVG_WIN(x)			((x) & 0x7)
> +
> +/* Refernce cycles when using lane clock as refernce */
> +#define LANE_REF_CYC				0x8000
> +
> +enum voltage_swing_level {
> +	VOLTAGE_LEVEL_0,
> +	VOLTAGE_LEVEL_1,
> +	VOLTAGE_LEVEL_2,
> +	VOLTAGE_LEVEL_3,
> +};
> +
> +enum pre_emphasis_level {
> +	PRE_EMPHASIS_LEVEL_0,
> +	PRE_EMPHASIS_LEVEL_1,
> +	PRE_EMPHASIS_LEVEL_2,
> +	PRE_EMPHASIS_LEVEL_3,
> +};
> +
> +enum pattern_set {
> +	PTS1		= BIT(0),
> +	PTS2		= BIT(1),
> +	PTS3		= BIT(2),
> +	PTS4		= BIT(3),
> +	DP_NONE		= BIT(4)
> +};
> +
> +enum vic_color_depth {
> +	BCS_6 = 0x1,
> +	BCS_8 = 0x2,
> +	BCS_10 = 0x4,
> +	BCS_12 = 0x8,
> +	BCS_16 = 0x10,
> +};
> +
> +enum vic_bt_type {
> +	BT_601 = 0x0,
> +	BT_709 = 0x1,
> +};
> +
> +void cdn_dp_clock_reset(struct cdn_dp_device *dp);
> +
> +void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, u32 clk);
> +int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
> +			 u32 i_size, const u32 *d_mem, u32 d_size);
> +int cdn_dp_active(struct cdn_dp_device *dp, bool enable);
> +int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes);
> +int cdn_dp_event_config(struct cdn_dp_device *dp);
> +u32 cdn_dp_get_event(struct cdn_dp_device *dp);
> +int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
> +int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value);
> +int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len);
> +int cdn_dp_get_edid_block(void *dp, u8 *edid,
> +			  unsigned int block, size_t length);
> +int cdn_dp_training_start(struct cdn_dp_device *dp);
> +int cdn_dp_get_training_status(struct cdn_dp_device *dp);
> +int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active);
> +int cdn_dp_config_video(struct cdn_dp_device *dp);
> +int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio);
> +int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable);
> +int cdn_dp_audio_config_set(struct cdn_dp_device *dp, struct audio_info *audio);
> +#endif /* _CDN_DP_REG_H */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> index edd7ec2..d723d8e 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> @@ -969,9 +969,11 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
>   		vop_dsp_hold_valid_irq_disable(vop);
>   	}
>   
> -	pin_pol = 0x8;
> -	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1;
> -	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : (1 << 1);
> +	pin_pol = BIT(DCLK_INVERT);
> +	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ?
> +		   0 : BIT(HSYNC_POSITIVE);
> +	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ?
> +		   0 : BIT(VSYNC_POSITIVE);
>   	VOP_CTRL_SET(vop, pin_pol, pin_pol);
>   
>   	switch (s->output_type) {
> @@ -991,6 +993,11 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
>   		VOP_CTRL_SET(vop, mipi_pin_pol, pin_pol);
>   		VOP_CTRL_SET(vop, mipi_en, 1);
>   		break;
> +	case DRM_MODE_CONNECTOR_DisplayPort:
> +		pin_pol &= ~BIT(DCLK_INVERT);
> +		VOP_CTRL_SET(vop, dp_pin_pol, pin_pol);
> +		VOP_CTRL_SET(vop, dp_en, 1);
> +		break;
>   	default:
>   		DRM_ERROR("unsupport connector_type[%d]\n", s->output_type);
>   	}
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> index ff4f52e..4820a8b 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> @@ -45,6 +45,7 @@ struct vop_ctrl {
>   	struct vop_reg edp_en;
>   	struct vop_reg hdmi_en;
>   	struct vop_reg mipi_en;
> +	struct vop_reg dp_en;
>   	struct vop_reg out_mode;
>   	struct vop_reg dither_down;
>   	struct vop_reg dither_up;
> @@ -53,6 +54,7 @@ struct vop_ctrl {
>   	struct vop_reg hdmi_pin_pol;
>   	struct vop_reg edp_pin_pol;
>   	struct vop_reg mipi_pin_pol;
> +	struct vop_reg dp_pin_pol;
>   
>   	struct vop_reg htotal_pw;
>   	struct vop_reg hact_st_end;
> @@ -242,6 +244,13 @@ enum scale_down_mode {
>   	SCALE_DOWN_AVG = 0x1
>   };
>   
> +enum vop_pol {
> +	HSYNC_POSITIVE = 0,
> +	VSYNC_POSITIVE = 1,
> +	DEN_NEGATIVE   = 2,
> +	DCLK_INVERT    = 3
> +};
> +
>   #define FRAC_16_16(mult, div)    (((mult) << 16) / (div))
>   #define SCL_FT_DEFAULT_FIXPOINT_SHIFT	12
>   #define SCL_MAX_VSKIPLINES		4
> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> index 5b1ae1f..dcf172e 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> @@ -281,6 +281,7 @@ static const struct vop_data rk3288_vop = {
>   static const struct vop_ctrl rk3399_ctrl_data = {
>   	.standby = VOP_REG(RK3399_SYS_CTRL, 0x1, 22),
>   	.gate_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 23),
> +	.dp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 11),
>   	.rgb_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 12),
>   	.hdmi_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 13),
>   	.edp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 14),
> @@ -290,6 +291,7 @@ static const struct vop_ctrl rk3399_ctrl_data = {
>   	.data_blank = VOP_REG(RK3399_DSP_CTRL0, 0x1, 19),
>   	.out_mode = VOP_REG(RK3399_DSP_CTRL0, 0xf, 0),
>   	.rgb_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16),
> +	.dp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16),
>   	.hdmi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 20),
>   	.edp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 24),
>   	.mipi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 28),


-- 
?ark Yao




From ykk at rock-chips.com  Thu Jul 21 18:00:34 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 22 Jul 2016 09:00:34 +0800
Subject: [PATCH v1 1/2] drm/panel: simple-panel: add the delay timing for
 Sharp LQ123P1JX31
In-Reply-To: <CAOw6vbLVrJj9So7tJeDtnDnq_+W=ZunPNwRRidfQKmjaj=byUw@mail.gmail.com>
References: <1469106874-4912-1-git-send-email-ykk@rock-chips.com>
 <CAOw6vbLVrJj9So7tJeDtnDnq_+W=ZunPNwRRidfQKmjaj=byUw@mail.gmail.com>
Message-ID: <57917032.5020101@rock-chips.com>

Sean,

On 07/21/2016 10:30 PM, Sean Paul wrote:
> On Thu, Jul 21, 2016 at 9:14 AM, Yakir Yang <ykk at rock-chips.com> wrote:
>> According to page 16 of Sharp LQ123P1JX31 datasheet, we need to add the
>> missing delay timing. Panel prepare time should be t1 (0.5ms~10ms) plus
>> t3 (0ms~100ms), and panel enable time should equal to t7 (0ms~50ms), and
>> panel unprepare time should be t11 (1ms~50ms) plus t12 (500ms~).
>>
>> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
> Reviewed-by: Sean Paul <seanpaul at chromium.org>

Thanks  :-D

- Yakir

>> ---
>>   drivers/gpu/drm/panel/panel-simple.c | 5 +++++
>>   1 file changed, 5 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
>> index 85143d1..f178998 100644
>> --- a/drivers/gpu/drm/panel/panel-simple.c
>> +++ b/drivers/gpu/drm/panel/panel-simple.c
>> @@ -1384,6 +1384,11 @@ static const struct panel_desc sharp_lq123p1jx31 = {
>>                  .width = 259,
>>                  .height = 173,
>>          },
>> +       .delay = {
>> +               .prepare = 110,
>> +               .enable = 50,
>> +               .unprepare = 550,
>> +       },
>>   };
>>
>>   static const struct drm_display_mode shelly_sca07010_bfn_lnn_mode = {
>> --
>> 1.9.1
>>
>>
>
>




From groeck at google.com  Thu Jul 21 18:40:50 2016
From: groeck at google.com (Guenter Roeck)
Date: Thu, 21 Jul 2016 18:40:50 -0700
Subject: [v6 PATCH 6/6] drm/rockchip: cdn-dp: add cdn DP support for rk3399
In-Reply-To: <1469106808-28297-7-git-send-email-zyw@rock-chips.com>
References: <1469106808-28297-1-git-send-email-zyw@rock-chips.com>
 <1469106808-28297-7-git-send-email-zyw@rock-chips.com>
Message-ID: <CABXOdTcOSbu8aU67MYEKgZ25tV8MmOq1HAfZYnR+6CwT7v6S4Q@mail.gmail.com>

On Thu, Jul 21, 2016 at 6:13 AM, Chris Zhong <zyw at rock-chips.com> wrote:
> Add support for cdn DP controller which is embedded in the rk3399
> SoCs. The DP is compliant with DisplayPort Specification,
> Version 1.3, This IP is compatible with the rockchip type-c PHY IP.
> There is a uCPU in DP controller, it need a firmware to work,
> please put the firmware file to /lib/firmware/cdn/dptx.bin. The

/lib/firmware/rockchip/dptx.bin


From ykk at rock-chips.com  Thu Jul 21 18:42:30 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 22 Jul 2016 09:42:30 +0800
Subject: [PATCH v2 1/3] drm/panel: simple-panel: add the delay timing for
 Sharp LQ123P1JX31
Message-ID: <1469151750-7130-1-git-send-email-ykk@rock-chips.com>

According to page 16 of Sharp LQ123P1JX31 datasheet, we need to add the
missing delay timing. Panel prepare time should be t1 (0.5ms~10ms) plus
t3 (0ms~100ms), and panel enable time should equal to t7 (0ms~50ms), and
panel unprepare time should be t11 (1ms~50ms) plus t12 (500ms~).

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
Reviewed-by: Sean Paul <seanpaul at chromium.org>
---
Changes in v2:
- Add the reviewed tag from Sean.

 drivers/gpu/drm/panel/panel-simple.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
index 85143d1..f178998 100644
--- a/drivers/gpu/drm/panel/panel-simple.c
+++ b/drivers/gpu/drm/panel/panel-simple.c
@@ -1384,6 +1384,11 @@ static const struct panel_desc sharp_lq123p1jx31 = {
 		.width = 259,
 		.height = 173,
 	},
+	.delay = {
+		.prepare = 110,
+		.enable = 50,
+		.unprepare = 550,
+	},
 };
 
 static const struct drm_display_mode shelly_sca07010_bfn_lnn_mode = {
-- 
1.9.1




From ykk at rock-chips.com  Thu Jul 21 18:50:40 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 22 Jul 2016 09:50:40 +0800
Subject: [PATCH v2 2/3] drm/bridge: analogix_dp: turn off the panel when eDP
 need to disable
In-Reply-To: <1469151750-7130-1-git-send-email-ykk@rock-chips.com>
References: <1469151750-7130-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1469152240-7316-1-git-send-email-ykk@rock-chips.com>

Some panels (like Sharp LQ123P1JX31) need to be turn off when eDP
controller stop to send valid video signal, otherwhise panel would
go burn in, and keep flicker and flicker.

So it's better to turn off the panel when eDP need to disable, and
we need to turn on the panel in connector->detect() callback, so
that driver would detect panel status rightly.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
Changes in v2:
- s/Panle/Panel/ (Sean)
- Move the drm_panel_xxxx out of a conditional, and throw the return
  code away (Sean)
- Add comments about why we need unprepare the panel in bridge_disable (Sean)
- Unprepare the panel at the end of bridge->disable() function.

 drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 37 ++++++++++++++++------
 1 file changed, 28 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index 32715da..0ddaf93 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -960,6 +960,17 @@ enum drm_connector_status
 analogix_dp_detect(struct drm_connector *connector, bool force)
 {
 	struct analogix_dp_device *dp = to_dp(connector);
+	int ret;
+
+	/*
+	 * Panel would prepare for several times here, but don't worry, it
+	 * would only enable the hardware at the first prepare time.
+	 */
+	if (dp->plat_data->panel) {
+		ret = drm_panel_prepare(dp->plat_data->panel);
+		if (ret)
+			DRM_ERROR("failed to setup the panel ret = %d\n", ret);
+	}
 
 	if (analogix_dp_detect_hpd(dp))
 		return connector_status_disconnected;
@@ -1058,13 +1069,15 @@ static void analogix_dp_bridge_enable(struct drm_bridge *bridge)
 static void analogix_dp_bridge_disable(struct drm_bridge *bridge)
 {
 	struct analogix_dp_device *dp = bridge->driver_private;
+	int ret;
 
 	if (dp->dpms_mode != DRM_MODE_DPMS_ON)
 		return;
 
 	if (dp->plat_data->panel) {
-		if (drm_panel_disable(dp->plat_data->panel)) {
-			DRM_ERROR("failed to disable the panel\n");
+		ret = drm_panel_disable(dp->plat_data->panel);
+		if (ret) {
+			DRM_ERROR("failed to disable the panel [%d]\n", ret);
 			return;
 		}
 	}
@@ -1077,6 +1090,19 @@ static void analogix_dp_bridge_disable(struct drm_bridge *bridge)
 
 	pm_runtime_put_sync(dp->dev);
 
+	/*
+	 * Some panels need to be turn off when eDP controller stop to send
+	 * valid video signal, otherwhise panel would go burn in, and keep
+	 * flicker and flicker.
+	 */
+	if (dp->plat_data->panel) {
+		 ret = drm_panel_unprepare(dp->plat_data->panel);
+		 if (ret) {
+			DRM_ERROR("failed to turnoff the panel [%d]\n", ret);
+			return;
+		}
+	}
+
 	dp->dpms_mode = DRM_MODE_DPMS_OFF;
 }
 
@@ -1333,13 +1359,6 @@ int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev,
 
 	phy_power_on(dp->phy);
 
-	if (dp->plat_data->panel) {
-		if (drm_panel_prepare(dp->plat_data->panel)) {
-			DRM_ERROR("failed to setup the panel\n");
-			return -EBUSY;
-		}
-	}
-
 	analogix_dp_init_dp(dp);
 
 	ret = devm_request_threaded_irq(&pdev->dev, dp->irq,
-- 
1.9.1




From ykk at rock-chips.com  Thu Jul 21 18:50:53 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Fri, 22 Jul 2016 09:50:53 +0800
Subject: [PATCH v2 3/3] drm/bridge: analogix_dp: remove the panel
 prepare/unprepare in suspend/resume
In-Reply-To: <1469151750-7130-1-git-send-email-ykk@rock-chips.com>
References: <1469151750-7130-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1469152253-7383-1-git-send-email-ykk@rock-chips.com>

We already manager the panel power status in bridge_disable and
connector->detect functions, then we don't need to manager the
panel power status at suspend/resume in particular.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
Changes in v2: None

 drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 12 ------------
 1 file changed, 12 deletions(-)

diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index 0ddaf93..3dadad2 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -1413,11 +1413,6 @@ int analogix_dp_suspend(struct device *dev)
 
 	clk_disable_unprepare(dp->clock);
 
-	if (dp->plat_data->panel) {
-		if (drm_panel_unprepare(dp->plat_data->panel))
-			DRM_ERROR("failed to turnoff the panel\n");
-	}
-
 	return 0;
 }
 EXPORT_SYMBOL_GPL(analogix_dp_suspend);
@@ -1433,13 +1428,6 @@ int analogix_dp_resume(struct device *dev)
 		return ret;
 	}
 
-	if (dp->plat_data->panel) {
-		if (drm_panel_prepare(dp->plat_data->panel)) {
-			DRM_ERROR("failed to setup the panel\n");
-			return -EBUSY;
-		}
-	}
-
 	return 0;
 }
 EXPORT_SYMBOL_GPL(analogix_dp_resume);
-- 
1.9.1




From zyw at rock-chips.com  Thu Jul 21 19:14:12 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Fri, 22 Jul 2016 10:14:12 +0800
Subject: [v6.1 PATCH 6/6] drm/rockchip: cdn-dp: add cdn DP support for rk3399
In-Reply-To: <CABXOdTcOSbu8aU67MYEKgZ25tV8MmOq1HAfZYnR+6CwT7v6S4Q@mail.gmail.com>
References: <CABXOdTcOSbu8aU67MYEKgZ25tV8MmOq1HAfZYnR+6CwT7v6S4Q@mail.gmail.com>
Message-ID: <1469153652-23080-1-git-send-email-zyw@rock-chips.com>

Add support for cdn DP controller which is embedded in the rk3399
SoCs. The DP is compliant with DisplayPort Specification,
Version 1.3, This IP is compatible with the rockchip type-c PHY IP.
There is a uCPU in DP controller, it need a firmware to work,
please put the firmware file to /lib/firmware/rockchip/dptx.bin. The
uCPU in charge of aux communication and link training, the host use
mailbox to communicate with the ucpu.
The dclk pin_pol of vop must not be invert for DP.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
Reviewed-by: Sean Paul <seanpaul at chromium.org>
Acked-by: Mark Yao <mark.yao at rock-chips.com>

---

Changes in v6.1:
- correct the path in commit

Changes in v6:
- add a port struct
- select SND_SOC_HDMI_CODEC
- force reset the phy when hpd detected

Changes in v5:
- alphabetical order
- do not use long, use u32 or u64
- return MODE_CLOCK_HIGH when requested > actual
- Optimized Coding Style
- add a formula to get better tu size and symbol value.
- modify according to Sean Paul's comments
- fixed the fw_wait always 0

Changes in v4:
- use phy framework to control DP phy
- support 2 phys

Changes in v3:
- use EXTCON_DISP_DP and EXTCON_DISP_DP_ALT cable to get dp port state.
- reset spdif before config it
- modify the firmware clk to 100Mhz
- retry load firmware if fw file is requested too early

Changes in v2:
- Alphabetic order
- remove excess error message
- use define clk_rate
- check all return value
- remove dev_set_name(dp->dev, "cdn-dp");
- use schedule_delayed_work
- remove never-called functions
- remove some unnecessary ()

Changes in v1:
- use extcon API
- use hdmi-codec for the DP Asoc
- do not initialize the "ret"
- printk a err log when drm_of_encoder_active_endpoint_id
- modify the dclk pin_pol to a single line

 drivers/gpu/drm/rockchip/Kconfig            |  10 +
 drivers/gpu/drm/rockchip/Makefile           |   1 +
 drivers/gpu/drm/rockchip/cdn-dp-core.c      | 868 ++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/cdn-dp-core.h      | 102 ++++
 drivers/gpu/drm/rockchip/cdn-dp-reg.c       | 904 ++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/cdn-dp-reg.h       | 479 +++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c |  13 +-
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   9 +
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c |   2 +
 9 files changed, 2385 insertions(+), 3 deletions(-)
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.c
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.h
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.c
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.h

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index d30bdc3..20aaafe 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -25,6 +25,16 @@ config ROCKCHIP_ANALOGIX_DP
 	  for the Analogix Core DP driver. If you want to enable DP
 	  on RK3288 based SoC, you should selet this option.
 
+config ROCKCHIP_CDN_DP
+        tristate "Rockchip cdn DP"
+        depends on DRM_ROCKCHIP
+	select SND_SOC_HDMI_CODEC if SND_SOC
+        help
+	  This selects support for Rockchip SoC specific extensions
+	  for the cdn DP driver. If you want to enable Dp on
+	  RK3399 based SoC, you should select this
+	  option.
+
 config ROCKCHIP_DW_HDMI
         tristate "Rockchip specific extensions for Synopsys DW HDMI"
         depends on DRM_ROCKCHIP
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index 05d0713..abdecd5 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -7,6 +7,7 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
 rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
 
 obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
+obj-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
 obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
 obj-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
 obj-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
new file mode 100644
index 0000000..209a7ef
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
@@ -0,0 +1,868 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Chris Zhong <zyw at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_of.h>
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/extcon.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/mfd/syscon.h>
+#include <linux/phy/phy.h>
+
+#include <sound/hdmi-codec.h>
+
+#include "cdn-dp-core.h"
+#include "cdn-dp-reg.h"
+#include "rockchip_drm_vop.h"
+
+#define connector_to_dp(c) \
+		container_of(c, struct cdn_dp_device, connector)
+
+#define encoder_to_dp(c) \
+		container_of(c, struct cdn_dp_device, encoder)
+
+/* dp grf register offset */
+#define DP_VOP_SEL		0x6224
+#define DP_SEL_VOP_LIT		BIT(12)
+#define MAX_FW_WAIT_SECS	64
+#define CDN_DP_FIRMWARE		"rockchip/dptx.bin"
+
+struct cdn_dp_data {
+	u8 max_phy;
+};
+
+struct cdn_dp_data rk3399_cdn_dp = {
+	.max_phy = 2,
+};
+
+static const struct of_device_id cdn_dp_dt_ids[] = {
+	{ .compatible = "rockchip,rk3399-cdn-dp",
+		.data = (void *)&rk3399_cdn_dp },
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, cdn_dp_dt_ids);
+
+static int cdn_dp_clk_enable(struct cdn_dp_device *dp)
+{
+	int ret;
+	u32 rate;
+
+	ret = clk_prepare_enable(dp->pclk);
+	if (ret < 0) {
+		dev_err(dp->dev, "cannot enable dp pclk %d\n", ret);
+		goto err_pclk;
+	}
+
+	ret = clk_prepare_enable(dp->core_clk);
+	if (ret < 0) {
+		dev_err(dp->dev, "cannot enable core_clk %d\n", ret);
+		goto err_core_clk;
+	}
+
+	rate = clk_get_rate(dp->core_clk);
+	if (rate < 0) {
+		dev_err(dp->dev, "get clk rate failed: %d\n", rate);
+		goto err_set_rate;
+	}
+
+	cdn_dp_set_fw_clk(dp, rate);
+
+	return 0;
+
+err_set_rate:
+	clk_disable_unprepare(dp->core_clk);
+err_core_clk:
+	clk_disable_unprepare(dp->pclk);
+err_pclk:
+	return ret;
+}
+
+static enum drm_connector_status
+cdn_dp_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct cdn_dp_device *dp = connector_to_dp(connector);
+
+	return dp->hpd_status;
+}
+
+static void cdn_dp_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+static struct drm_connector_funcs cdn_dp_atomic_connector_funcs = {
+	.dpms = drm_atomic_helper_connector_dpms,
+	.detect = cdn_dp_connector_detect,
+	.destroy = cdn_dp_connector_destroy,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int cdn_dp_connector_get_modes(struct drm_connector *connector)
+{
+	struct cdn_dp_device *dp = connector_to_dp(connector);
+	struct edid *edid;
+	int ret = 0;
+
+	edid = drm_do_get_edid(connector, cdn_dp_get_edid_block, dp);
+	if (edid) {
+		dev_dbg(dp->dev, "got edid: width[%d] x height[%d]\n",
+			edid->width_cm, edid->height_cm);
+
+		dp->sink_has_audio = drm_detect_monitor_audio(edid);
+		ret = drm_add_edid_modes(connector, edid);
+		if (ret) {
+			drm_mode_connector_update_edid_property(connector,
+								edid);
+			drm_edid_to_eld(connector, edid);
+		}
+		kfree(edid);
+	} else {
+		dev_warn(dp->dev, "failed to get edid\n");
+	}
+
+	return ret;
+}
+
+static struct drm_encoder *
+	cdn_dp_connector_best_encoder(struct drm_connector *connector)
+{
+	struct cdn_dp_device *dp = connector_to_dp(connector);
+
+	return &dp->encoder;
+}
+
+static int cdn_dp_connector_mode_valid(struct drm_connector *connector,
+				       struct drm_display_mode *mode)
+{
+	struct cdn_dp_device *dp = connector_to_dp(connector);
+	struct drm_display_info *display_info = &dp->connector.display_info;
+	u32 requested = mode->clock * display_info->bpc * 3 / 1000;
+	u32 sink_max, source_max, rate;
+	u8 lanes, i;
+	u32 actual;
+
+	/* find the running port */
+	for (i = 0; i < dp->ports; i++) {
+		if (dp->port[i]->phy_status == 1) {
+			source_max = dp->port[i]->cap_lanes;
+			break;
+		}
+	}
+
+	sink_max = drm_dp_max_lane_count(dp->dpcd);
+	lanes = min(source_max, sink_max);
+
+	source_max = drm_dp_bw_code_to_link_rate(CDN_DP_MAX_LINK_RATE);
+	sink_max = drm_dp_max_link_rate(dp->dpcd);
+	rate = min(source_max, sink_max);
+
+	actual = rate * lanes / 100;
+
+	/* efficiency is about 0.8 */
+	actual = actual * 8 / 10;
+
+	if (requested > actual) {
+		dev_dbg(dp->dev, "requested=%d, actual=%d, clock=%d\n",
+			requested, actual, mode->clock);
+		return MODE_CLOCK_HIGH;
+	}
+
+	return MODE_OK;
+}
+
+static struct drm_connector_helper_funcs cdn_dp_connector_helper_funcs = {
+	.get_modes = cdn_dp_connector_get_modes,
+	.best_encoder = cdn_dp_connector_best_encoder,
+	.mode_valid = cdn_dp_connector_mode_valid,
+};
+
+static void cdn_dp_commit(struct drm_encoder *encoder)
+{
+	struct cdn_dp_device *dp = encoder_to_dp(encoder);
+	int ret;
+
+	ret = cdn_dp_training_start(dp);
+	if (ret) {
+		dev_err(dp->dev, "link training failed: %d\n", ret);
+		return;
+	}
+
+	ret = cdn_dp_get_training_status(dp);
+	if (ret) {
+		dev_err(dp->dev, "get link training status failed: %d\n", ret);
+		return;
+	}
+
+	dev_info(dp->dev, "rate:%d, lanes:%d\n",
+		 dp->link.rate, dp->link.num_lanes);
+
+	if (cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE))
+		return;
+
+	if (cdn_dp_config_video(dp)) {
+		dev_err(dp->dev, "unable to config video\n");
+		return;
+	}
+
+	if (cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID))
+		return;
+
+	dp->dpms_mode = DRM_MODE_DPMS_ON;
+}
+
+static void cdn_dp_encoder_mode_set(struct drm_encoder *encoder,
+				    struct drm_display_mode *mode,
+				    struct drm_display_mode *adjusted)
+{
+	struct cdn_dp_device *dp = encoder_to_dp(encoder);
+	struct drm_display_info *display_info = &dp->connector.display_info;
+	struct rockchip_crtc_state *state;
+	struct video_info *video = &dp->video_info;
+	int ret, val;
+
+	switch (display_info->bpc) {
+	case 16:
+	case 12:
+	case 10:
+		video->color_depth = 10;
+		break;
+	case 6:
+		video->color_depth = 6;
+		break;
+	default:
+		video->color_depth = 8;
+		break;
+	}
+
+	video->color_fmt = PXL_RGB;
+
+	video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC);
+	video->h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC);
+
+	ret = drm_of_encoder_active_endpoint_id(dp->dev->of_node, encoder);
+	if (ret < 0) {
+		dev_err(dp->dev, "Could not get vop id, %d", ret);
+		return;
+	}
+
+	dev_dbg(dp->dev, "vop %s output to cdn-dp\n", (ret) ? "LIT" : "BIG");
+	state = to_rockchip_crtc_state(encoder->crtc->state);
+	if (ret) {
+		val = DP_SEL_VOP_LIT | (DP_SEL_VOP_LIT << 16);
+		state->output_mode = ROCKCHIP_OUT_MODE_P888;
+	} else {
+		val = DP_SEL_VOP_LIT << 16;
+		state->output_mode = ROCKCHIP_OUT_MODE_AAAA;
+	}
+	ret = regmap_write(dp->grf, DP_VOP_SEL, val);
+	if (ret != 0)
+		dev_err(dp->dev, "Could not write to GRF: %d\n", ret);
+
+	memcpy(&dp->mode, adjusted, sizeof(*mode));
+}
+
+static void cdn_dp_encoder_enable(struct drm_encoder *encoder)
+{
+	struct cdn_dp_device *dp = encoder_to_dp(encoder);
+
+	if (dp->dpms_mode != DRM_MODE_DPMS_ON)
+		cdn_dp_commit(encoder);
+}
+
+static void cdn_dp_encoder_disable(struct drm_encoder *encoder)
+{
+	struct cdn_dp_device *dp = encoder_to_dp(encoder);
+
+	if (dp->dpms_mode != DRM_MODE_DPMS_OFF) {
+		cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE);
+		dp->dpms_mode = DRM_MODE_DPMS_OFF;
+	}
+}
+
+static int
+cdn_dp_encoder_atomic_check(struct drm_encoder *encoder,
+			    struct drm_crtc_state *crtc_state,
+			    struct drm_connector_state *conn_state)
+{
+	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+
+	s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
+	s->output_type = DRM_MODE_CONNECTOR_DisplayPort;
+
+	return 0;
+}
+
+static struct drm_encoder_helper_funcs cdn_dp_encoder_helper_funcs = {
+	.mode_set = cdn_dp_encoder_mode_set,
+	.enable = cdn_dp_encoder_enable,
+	.disable = cdn_dp_encoder_disable,
+	.atomic_check = cdn_dp_encoder_atomic_check,
+};
+
+static struct drm_encoder_funcs cdn_dp_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+static int cdn_dp_firmware_init(struct cdn_dp_device *dp)
+{
+	int ret;
+	const u32 *iram_data, *dram_data;
+	const struct firmware *fw = dp->fw;
+	const struct cdn_firmware_header *hdr;
+
+	hdr = (struct cdn_firmware_header *)fw->data;
+	if (fw->size != le32_to_cpu(hdr->size_bytes)) {
+		dev_err(dp->dev, "firmware is invalid\n");
+		return -EINVAL;
+	}
+
+	iram_data = (const u32 *)(fw->data + hdr->header_size);
+	dram_data = (const u32 *)(fw->data + hdr->header_size + hdr->iram_size);
+
+	ret = cdn_dp_load_firmware(dp, iram_data, hdr->iram_size,
+				   dram_data, hdr->dram_size);
+	if (ret)
+		return ret;
+
+	dp->fw_loaded = 1;
+
+	ret = cdn_dp_active(dp, true);
+	if (ret) {
+		dev_err(dp->dev, "active ucpu failed: %d\n", ret);
+		return ret;
+	}
+
+	return cdn_dp_event_config(dp);
+}
+
+static int cdn_dp_init(struct cdn_dp_device *dp)
+{
+	struct device *dev = dp->dev;
+	struct device_node *np = dev->of_node;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct resource *res;
+	int ret;
+
+	dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+	if (IS_ERR(dp->grf)) {
+		dev_err(dev, "cdn-dp needs rockchip,grf property\n");
+		return PTR_ERR(dp->grf);
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	dp->regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(dp->regs)) {
+		dev_err(dev, "ioremap reg failed\n");
+		return PTR_ERR(dp->regs);
+	}
+
+	dp->core_clk = devm_clk_get(dev, "core-clk");
+	if (IS_ERR(dp->core_clk)) {
+		dev_err(dev, "cannot get core_clk_dp\n");
+		return PTR_ERR(dp->core_clk);
+	}
+
+	dp->pclk = devm_clk_get(dev, "pclk");
+	if (IS_ERR(dp->pclk)) {
+		dev_err(dev, "cannot get pclk\n");
+		return PTR_ERR(dp->pclk);
+	}
+
+	dp->spdif_clk = devm_clk_get(dev, "spdif");
+	if (IS_ERR(dp->spdif_clk)) {
+		dev_err(dev, "cannot get spdif_clk\n");
+		return PTR_ERR(dp->spdif_clk);
+	}
+
+	dp->spdif_rst = devm_reset_control_get(dev, "spdif");
+	if (IS_ERR(dp->spdif_rst)) {
+		dev_err(dev, "no spdif reset control found\n");
+		return PTR_ERR(dp->spdif_rst);
+	}
+
+	dp->dpms_mode = DRM_MODE_DPMS_OFF;
+
+	ret = cdn_dp_clk_enable(dp);
+	if (ret < 0)
+		return ret;
+
+	cdn_dp_clock_reset(dp);
+
+	return 0;
+}
+
+static int cdn_dp_audio_hw_params(struct device *dev,
+				  struct hdmi_codec_daifmt *daifmt,
+				  struct hdmi_codec_params *params)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	struct audio_info audio = {
+		.sample_width = params->sample_width,
+		.sample_rate = params->sample_rate,
+		.channels = params->channels,
+	};
+	int ret;
+
+	if (!dp->encoder.crtc)
+		return -ENODEV;
+
+	switch (daifmt->fmt) {
+	case HDMI_I2S:
+		audio.format = AFMT_I2S;
+		break;
+	case HDMI_SPDIF:
+		audio.format = AFMT_SPDIF;
+		break;
+	default:
+		dev_err(dev, "%s: Invalid format %d\n", __func__, daifmt->fmt);
+		return -EINVAL;
+	}
+
+	ret = cdn_dp_audio_config_set(dp, &audio);
+	if (!ret)
+		dp->audio_info = audio;
+
+	return ret;
+}
+
+static void cdn_dp_audio_shutdown(struct device *dev)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	int ret = cdn_dp_audio_stop(dp, &dp->audio_info);
+
+	if (!ret)
+		dp->audio_info.format = AFMT_UNUSED;
+}
+
+static int cdn_dp_audio_digital_mute(struct device *dev, bool enable)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+
+	return cdn_dp_audio_mute(dp, enable);
+}
+
+static int cdn_dp_audio_get_eld(struct device *dev, uint8_t *buf, size_t len)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	struct drm_mode_config *config = &dp->encoder.dev->mode_config;
+	struct drm_connector *connector;
+	int ret = -ENODEV;
+
+	mutex_lock(&config->mutex);
+	list_for_each_entry(connector, &config->connector_list, head) {
+		if (&dp->encoder == connector->encoder) {
+			memcpy(buf, connector->eld,
+			       min(sizeof(connector->eld), len));
+			ret = 0;
+		}
+	}
+	mutex_unlock(&config->mutex);
+
+	return ret;
+}
+
+static const struct hdmi_codec_ops audio_codec_ops = {
+	.hw_params = cdn_dp_audio_hw_params,
+	.audio_shutdown = cdn_dp_audio_shutdown,
+	.digital_mute = cdn_dp_audio_digital_mute,
+	.get_eld = cdn_dp_audio_get_eld,
+};
+
+static int cdn_dp_audio_codec_init(struct cdn_dp_device *dp,
+				   struct device *dev)
+{
+	struct hdmi_codec_pdata codec_data = {
+		.i2s = 1,
+		.spdif = 1,
+		.ops = &audio_codec_ops,
+		.max_i2s_channels = 8,
+	};
+
+	dp->audio_pdev = platform_device_register_data(
+			 dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO,
+			 &codec_data, sizeof(codec_data));
+
+	return PTR_ERR_OR_ZERO(dp->audio_pdev);
+}
+
+static int cdn_dp_get_cap_lanes(struct cdn_dp_device *dp,
+				struct extcon_dev *edev)
+{
+	bool dfp, dptx;
+	u8 lanes;
+
+	dfp = extcon_get_cable_state_(edev, EXTCON_USB_HOST);
+	dptx = extcon_get_cable_state_(edev, EXTCON_DISP_DP);
+
+	if (dfp && dptx)
+		lanes = 2;
+	else if (dptx)
+		lanes = 4;
+	else
+		lanes = 0;
+
+	return lanes;
+}
+
+static int cdn_dp_phy_power_ctrl(struct cdn_dp_device *dp,
+				 struct cdn_dp_port *port, bool enable)
+{
+	struct phy *phy = port->phy;
+	int ret;
+	int count = 0;
+
+	while (phy->power_count > 0) {
+		ret = phy_power_off(phy);
+		if (ret)
+			return ret;
+		count++;
+	}
+
+	count = enable ? count + 1 : count - 1;
+	while (count-- > 0) {
+		ret = phy_power_on(phy);
+		if (ret)
+			return ret;
+	}
+
+	port->phy_status = enable;
+	return 0;
+}
+
+static int cdn_dp_pd_event(struct notifier_block *nb,
+			   unsigned long event, void *priv)
+{
+	struct cdn_dp_port *port;
+
+	port = container_of(nb, struct cdn_dp_port, event_nb);
+
+	schedule_delayed_work(&port->event_wq, 0);
+
+	return 0;
+}
+
+static void cdn_dp_pd_event_wq(struct work_struct *work)
+{
+	struct cdn_dp_port *port = container_of(work, struct cdn_dp_port,
+						event_wq.work);
+	struct cdn_dp_device *dp = port->dp;
+	u8 new_cap_lanes;
+	int ret;
+	u8 i;
+
+	new_cap_lanes = cdn_dp_get_cap_lanes(dp, port->extcon);
+
+	if (new_cap_lanes == port->cap_lanes) {
+		dev_err(dp->dev, "lanes count does not change: %d\n",
+			new_cap_lanes);
+		return;
+	}
+
+	if (!new_cap_lanes) {
+		ret = cdn_dp_phy_power_ctrl(dp, port, 0);
+		if (ret) {
+			dev_err(dp->dev, "phy power off failed: %d", ret);
+			return;
+		}
+		port->phy_status = 0;
+		port->cap_lanes = new_cap_lanes;
+		for (i = 0; i < dp->ports; i++) {
+			if (dp->port[i]->phy_status == 1)
+				return;
+		}
+
+		dp->hpd_status = connector_status_disconnected;
+		drm_helper_hpd_irq_event(dp->drm_dev);
+		return;
+	}
+
+	/* if other phy is running, do not touch the hpd_status, and return */
+	for (i = 0; i < dp->ports; i++) {
+		if (dp->port[i]->phy_status == 1) {
+			dev_warn(dp->dev, "busy, phy[%d] is running",
+				 dp->port[i]->id);
+			return;
+		}
+	}
+
+	if (!dp->fw_loaded) {
+		ret = request_firmware(&dp->fw, CDN_DP_FIRMWARE, dp->dev);
+		if (ret == -ENOENT && dp->fw_wait <= MAX_FW_WAIT_SECS) {
+			unsigned long time = msecs_to_jiffies(dp->fw_wait * HZ);
+
+			/*
+			 * If can not find the file, retry to load the firmware
+			 * in 1 second, if still failed after 1 minute, give up.
+			 */
+			schedule_delayed_work(&port->event_wq, time);
+			dev_warn(dp->dev, "retry to request firmware in %dS\n",
+				 dp->fw_wait);
+			dp->fw_wait *= 2;
+			return;
+		} else if (ret) {
+			dev_err(dp->dev, "failed to request firmware: %d\n",
+				ret);
+			return;
+		}
+	}
+
+	ret = cdn_dp_phy_power_ctrl(dp, port, 1);
+	if (ret) {
+		if (!dp->fw_loaded)
+			release_firmware(dp->fw);
+		dev_err(dp->dev, "phy power on failed: %d\n", ret);
+		return;
+	}
+
+	if (!dp->fw_loaded) {
+		ret = cdn_dp_firmware_init(dp);
+		release_firmware(dp->fw);
+		if (ret) {
+			dev_err(dp->dev, "firmware init failed: %d", ret);
+			goto err_firmware;
+		}
+	}
+
+	/* read hpd status failed, or the hpd does not exist. */
+	ret = cdn_dp_get_hpd_status(dp);
+	if (ret <= 0) {
+		dev_err(dp->dev, "get hpd failed: %d", ret);
+		goto err_firmware;
+	}
+
+	ret = cdn_dp_set_host_cap(dp, new_cap_lanes);
+	if (ret) {
+		dev_err(dp->dev, "set host capabilities failed: %d\n", ret);
+		goto err_firmware;
+	}
+
+	/*
+	 * Native read with retry for link status and receiver capability reads
+	 * for cases where the sink may still be asleep.
+	 *
+	 * Sinks are *supposed* to come up within 1ms from an off state, but
+	 * we're also supposed to retry 3 times per the spec.
+	 */
+	for (i = 0; i < 3; i++) {
+		ret = cdn_dp_dpcd_read(dp, 0x000, dp->dpcd,
+				       DP_RECEIVER_CAP_SIZE);
+		if (!ret) {
+			port->cap_lanes = new_cap_lanes;
+			dp->hpd_status = connector_status_connected;
+			drm_helper_hpd_irq_event(dp->drm_dev);
+			return;
+		}
+		usleep_range(1000, 1050);
+	}
+
+	dev_err(dp->dev, "get dpcd failed: %d", ret);
+
+err_firmware:
+	ret = cdn_dp_phy_power_ctrl(dp, port, 0);
+	if (ret)
+		dev_err(dp->dev, "phy power off failed: %d", ret);
+}
+
+static int cdn_dp_bind(struct device *dev, struct device *master,
+		       void *data)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	struct drm_encoder *encoder;
+	struct drm_connector *connector;
+	struct cdn_dp_port *port;
+	struct drm_device *drm_dev = data;
+	int ret, i;
+
+	ret = cdn_dp_init(dp);
+	if (ret < 0)
+		return ret;
+
+	dp->drm_dev = drm_dev;
+	dp->hpd_status = connector_status_disconnected;
+	dp->fw_wait = 1;
+
+	encoder = &dp->encoder;
+
+	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
+							     dev->of_node);
+	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
+
+	ret = drm_encoder_init(drm_dev, encoder, &cdn_dp_encoder_funcs,
+			       DRM_MODE_ENCODER_TMDS, NULL);
+	if (ret) {
+		DRM_ERROR("failed to initialize encoder with drm\n");
+		return ret;
+	}
+
+	drm_encoder_helper_add(encoder, &cdn_dp_encoder_helper_funcs);
+
+	connector = &dp->connector;
+	connector->polled = DRM_CONNECTOR_POLL_HPD;
+	connector->dpms = DRM_MODE_DPMS_OFF;
+
+	ret = drm_connector_init(drm_dev, connector,
+				 &cdn_dp_atomic_connector_funcs,
+				 DRM_MODE_CONNECTOR_DisplayPort);
+	if (ret) {
+		DRM_ERROR("failed to initialize connector with drm\n");
+		goto err_free_encoder;
+	}
+
+	drm_connector_helper_add(connector, &cdn_dp_connector_helper_funcs);
+
+	ret = drm_mode_connector_attach_encoder(connector, encoder);
+	if (ret) {
+		DRM_ERROR("failed to attach connector and encoder\n");
+		goto err_free_connector;
+	}
+
+	cdn_dp_audio_codec_init(dp, dev);
+
+
+	for (i = 0; i < dp->ports; i++) {
+		port = dp->port[i];
+
+		port->event_nb.notifier_call = cdn_dp_pd_event;
+		INIT_DELAYED_WORK(&port->event_wq, cdn_dp_pd_event_wq);
+		ret = extcon_register_notifier(port->extcon, EXTCON_DISP_DP,
+					       &port->event_nb);
+		if (ret) {
+			dev_err(dev, "regitster EXTCON_DISP_DP notifier err\n");
+			return ret;
+		}
+
+		if (extcon_get_cable_state_(port->extcon, EXTCON_DISP_DP))
+			schedule_delayed_work(&port->event_wq, 0);
+	}
+
+	return 0;
+
+err_free_connector:
+	drm_connector_cleanup(connector);
+err_free_encoder:
+	drm_encoder_cleanup(encoder);
+	return ret;
+}
+
+static void cdn_dp_unbind(struct device *dev, struct device *master, void *data)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	struct drm_encoder *encoder = &dp->encoder;
+	struct drm_connector *connector = &dp->connector;
+	struct cdn_dp_port *port;
+	int i;
+
+	platform_device_unregister(dp->audio_pdev);
+	cdn_dp_encoder_disable(encoder);
+	encoder->funcs->destroy(encoder);
+	connector->funcs->destroy(connector);
+
+	for (i = 0; i < dp->ports; i++) {
+		port = dp->port[i];
+		extcon_unregister_notifier(port->extcon, EXTCON_DISP_DP,
+					   &port->event_nb);
+	}
+}
+
+static const struct component_ops cdn_dp_component_ops = {
+	.bind = cdn_dp_bind,
+	.unbind = cdn_dp_unbind,
+};
+
+static int cdn_dp_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	const struct of_device_id *match;
+	struct cdn_dp_data *dp_data;
+	struct cdn_dp_port *port;
+	struct cdn_dp_device *dp;
+	struct extcon_dev *extcon;
+	struct phy *phy;
+	int i;
+
+	dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
+	if (!dp)
+		return -ENOMEM;
+	dp->dev = dev;
+
+	match = of_match_node(cdn_dp_dt_ids, pdev->dev.of_node);
+	dp_data = (struct cdn_dp_data *)match->data;
+
+	for (i = 0; i < dp_data->max_phy; i++) {
+		extcon = extcon_get_edev_by_phandle(dev, i);
+		phy = devm_of_phy_get_by_index(dev, dev->of_node, i);
+
+		if (PTR_ERR(extcon) == -EPROBE_DEFER ||
+		    PTR_ERR(phy) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+
+		if (IS_ERR(extcon) || IS_ERR(phy))
+			continue;
+
+		port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+		if (!dp)
+			return -ENOMEM;
+
+		port->extcon = extcon;
+		port->phy = phy;
+		port->dp = dp;
+		port->id = i;
+		dp->port[dp->ports++] = port;
+	}
+
+	if (!dp->ports) {
+		dev_err(dev, "missing extcon or phy\n");
+		return -EINVAL;
+	}
+
+	dev_set_drvdata(dev, dp);
+
+	return component_add(dev, &cdn_dp_component_ops);
+}
+
+static int cdn_dp_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &cdn_dp_component_ops);
+
+	return 0;
+}
+
+static struct platform_driver cdn_dp_driver = {
+	.probe = cdn_dp_probe,
+	.remove = cdn_dp_remove,
+	.driver = {
+		   .name = "cdn-dp",
+		   .owner = THIS_MODULE,
+		   .of_match_table = of_match_ptr(cdn_dp_dt_ids),
+	},
+};
+
+module_platform_driver(cdn_dp_driver);
+
+MODULE_AUTHOR("Chris Zhong <zyw at rock-chips.com>");
+MODULE_DESCRIPTION("cdn DP Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
new file mode 100644
index 0000000..6eb7b72
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2016 Chris Zhong <zyw at rock-chips.com>
+ * Copyright (C) 2016 ROCKCHIP, Inc.
+ *
+ * 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.
+ */
+
+#ifndef _CDN_DP_CORE_H
+#define _CDN_DP_CORE_H
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_panel.h>
+#include "rockchip_drm_drv.h"
+
+#define MAX_PHY		2
+
+enum audio_format {
+	AFMT_I2S = 0,
+	AFMT_SPDIF = 1,
+	AFMT_UNUSED,
+};
+
+struct audio_info {
+	enum audio_format format;
+	int sample_rate;
+	int channels;
+	int sample_width;
+};
+
+enum vic_pxl_encoding_format {
+	PXL_RGB = 0x1,
+	YCBCR_4_4_4 = 0x2,
+	YCBCR_4_2_2 = 0x4,
+	YCBCR_4_2_0 = 0x8,
+	Y_ONLY = 0x10,
+};
+
+struct video_info {
+	bool h_sync_polarity;
+	bool v_sync_polarity;
+	bool interlaced;
+	int color_depth;
+	enum vic_pxl_encoding_format color_fmt;
+};
+
+struct cdn_firmware_header {
+	u32 size_bytes; /* size of the entire header+image(s) in bytes */
+	u32 header_size; /* size of just the header in bytes */
+	u32 iram_size; /* size of iram */
+	u32 dram_size; /* size of dram */
+};
+
+struct cdn_dp_port {
+	struct cdn_dp_device *dp;
+	struct notifier_block event_nb;
+	struct delayed_work event_wq;
+	struct extcon_dev *extcon;
+	struct phy *phy;
+	u8 cap_lanes;
+	bool phy_status;
+	u8 id;
+};
+
+struct cdn_dp_device {
+	struct device *dev;
+	struct drm_device *drm_dev;
+	struct drm_connector connector;
+	struct drm_encoder encoder;
+	struct drm_display_mode mode;
+	struct platform_device *audio_pdev;
+
+	const struct firmware *fw;	/* cdn dp firmware */
+	unsigned int fw_version;	/* cdn fw version */
+	u32 fw_wait;
+	bool fw_loaded;
+	void __iomem *regs;
+	struct regmap *grf;
+	struct clk *core_clk;
+	struct clk *pclk;
+	struct clk *spdif_clk;
+	struct reset_control *spdif_rst;
+	struct audio_info audio_info;
+	struct video_info video_info;
+	struct drm_dp_link link;
+	struct cdn_dp_port *port[MAX_PHY];
+	u8 ports;
+
+	u8 dpcd[DP_RECEIVER_CAP_SIZE];
+	enum drm_connector_status hpd_status;
+	int dpms_mode;
+	bool sink_has_audio;
+};
+#endif  /* _CDN_DP_CORE_H */
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
new file mode 100644
index 0000000..a224a6b
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
@@ -0,0 +1,904 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Chris Zhong <zyw at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/clk.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/reset.h>
+
+#include "cdn-dp-core.h"
+#include "cdn-dp-reg.h"
+
+#define CDN_DP_SPDIF_CLK		200000000
+#define FW_ALIVE_TIMEOUT_US		1000000
+#define MAILBOX_RETRY_US		1000
+#define MAILBOX_TIMEOUT_US		5000000
+#define LINK_TRAINING_RETRY_MS		20
+#define LINK_TRAINING_TIMEOUT_MS	500
+
+void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, u32 clk)
+{
+	writel(clk / 1000000, dp->regs + SW_CLK_H);
+}
+
+void cdn_dp_clock_reset(struct cdn_dp_device *dp)
+{
+	u32 val;
+
+	val = DPTX_FRMR_DATA_CLK_RSTN_EN |
+	      DPTX_FRMR_DATA_CLK_EN |
+	      DPTX_PHY_DATA_RSTN_EN |
+	      DPTX_PHY_DATA_CLK_EN |
+	      DPTX_PHY_CHAR_RSTN_EN |
+	      DPTX_PHY_CHAR_CLK_EN |
+	      SOURCE_AUX_SYS_CLK_RSTN_EN |
+	      SOURCE_AUX_SYS_CLK_EN |
+	      DPTX_SYS_CLK_RSTN_EN |
+	      DPTX_SYS_CLK_EN |
+	      CFG_DPTX_VIF_CLK_RSTN_EN |
+	      CFG_DPTX_VIF_CLK_EN;
+	writel(val, dp->regs + SOURCE_DPTX_CAR);
+
+	val = SOURCE_PHY_RSTN_EN | SOURCE_PHY_CLK_EN;
+	writel(val, dp->regs + SOURCE_PHY_CAR);
+
+	val = SOURCE_PKT_SYS_RSTN_EN |
+	      SOURCE_PKT_SYS_CLK_EN |
+	      SOURCE_PKT_DATA_RSTN_EN |
+	      SOURCE_PKT_DATA_CLK_EN;
+	writel(val, dp->regs + SOURCE_PKT_CAR);
+
+	val = SPDIF_CDR_CLK_RSTN_EN |
+	      SPDIF_CDR_CLK_EN |
+	      SOURCE_AIF_SYS_RSTN_EN |
+	      SOURCE_AIF_SYS_CLK_EN |
+	      SOURCE_AIF_CLK_RSTN_EN |
+	      SOURCE_AIF_CLK_EN;
+	writel(val, dp->regs + SOURCE_AIF_CAR);
+
+	val = SOURCE_CIPHER_SYSTEM_CLK_RSTN_EN |
+	      SOURCE_CIPHER_SYS_CLK_EN |
+	      SOURCE_CIPHER_CHAR_CLK_RSTN_EN |
+	      SOURCE_CIPHER_CHAR_CLK_EN;
+	writel(val, dp->regs + SOURCE_CIPHER_CAR);
+
+	val = SOURCE_CRYPTO_SYS_CLK_RSTN_EN |
+	      SOURCE_CRYPTO_SYS_CLK_EN;
+	writel(val, dp->regs + SOURCE_CRYPTO_CAR);
+
+	val = ~(MAILBOX_INT_MASK_BIT | PIF_INT_MASK_BIT) & ALL_INT_MASK;
+	writel(val, dp->regs + APB_INT_MASK);
+}
+
+static int cdn_dp_mailbox_read(struct cdn_dp_device *dp)
+{
+	int val, ret;
+
+	if (!dp->fw_loaded)
+		return -EPERM;
+
+	ret = readx_poll_timeout(readl, dp->regs + MAILBOX_EMPTY_ADDR,
+				 val, !val, MAILBOX_RETRY_US,
+				 MAILBOX_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(dp->dev, "failed to read mailbox, keep alive = %x\n",
+			readl(dp->regs + KEEP_ALIVE));
+		return ret;
+	}
+
+	return readl(dp->regs + MAILBOX0_RD_DATA) & 0xff;
+}
+
+static int cdp_dp_mailbox_write(struct cdn_dp_device *dp, u8 val)
+{
+	int ret, full;
+
+	if (!dp->fw_loaded)
+		return -EPERM;
+
+	ret = readx_poll_timeout(readl, dp->regs + MAILBOX_FULL_ADDR,
+				 full, !full, MAILBOX_RETRY_US,
+				 MAILBOX_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(dp->dev, "mailbox is full, keep alive = %x\n",
+			readl(dp->regs + KEEP_ALIVE));
+		return ret;
+	}
+
+	writel(val, dp->regs + MAILBOX0_WR_DATA);
+
+	return 0;
+}
+
+static int cdn_dp_mailbox_validate_receive(struct cdn_dp_device *dp,
+					   u8 module_id, u8 opcode,
+					   u8 req_size)
+{
+	u32 mbox_size, i;
+	u8 header[4];
+	int ret;
+
+	/* read the header of the message */
+	for (i = 0; i < 4; i++)  {
+		ret = cdn_dp_mailbox_read(dp);
+		if (ret < 0)
+			return ret;
+
+		header[i] = ret;
+	}
+
+	mbox_size = (header[2] << 8) | header[3];
+
+	if (opcode != header[0] || module_id != header[1] ||
+	    req_size != mbox_size) {
+		dev_err(dp->dev, "mailbox validate failed");
+
+		/*
+		 * If the message in mailbox is not what we want, we need to
+		 * clear the mailbox by read.
+		 */
+		for (i = 0; i < mbox_size; i++)
+			if (cdn_dp_mailbox_read(dp) < 0)
+				break;
+
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int cdn_dp_mailbox_read_receive(struct cdn_dp_device *dp,
+				       u8 *buff, u8 buff_size)
+{
+	u32 i;
+	int ret;
+
+	for (i = 0; i < buff_size; i++) {
+		ret = cdn_dp_mailbox_read(dp);
+		if (ret < 0)
+			return ret;
+
+		buff[i] = ret;
+	}
+
+	return 0;
+}
+
+static int cdn_dp_mailbox_send(struct cdn_dp_device *dp, u8 module_id,
+			       u8 opcode, u16 size, u8 *message)
+{
+	u8 header[4];
+	int ret, i;
+
+	header[0] = opcode;
+	header[1] = module_id;
+	header[2] = (size >> 8) & 0xff;
+	header[3] = size & 0xff;
+
+	for (i = 0; i < 4; i++) {
+		ret = cdp_dp_mailbox_write(dp, header[i]);
+		if (ret)
+			return ret;
+	}
+
+	for (i = 0; i < size; i++) {
+		ret = cdp_dp_mailbox_write(dp, message[i]);
+		if (ret) {
+			dev_err(dp->dev, "mailbox send failed: %d", ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
+{
+	u8 msg[6];
+
+	msg[0] = (addr >> 8) & 0xff;
+	msg[1] = addr & 0xff;
+	msg[2] = (val >> 24) & 0xff;
+	msg[3] = (val >> 16) & 0xff;
+	msg[4] = (val >> 8) & 0xff;
+	msg[5] = val & 0xff;
+	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_REGISTER,
+				   sizeof(msg), msg);
+}
+
+int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
+{
+	u8 msg[5], reg[5];
+	int ret;
+
+	msg[0] = (len >> 8) & 0xff;
+	msg[1] = len & 0xff;
+	msg[2] = (addr >> 16) & 0xff;
+	msg[3] = (addr >> 8) & 0xff;
+	msg[4] = addr & 0xff;
+	cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_READ_DPCD,
+			    sizeof(msg), msg);
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_READ_DPCD,
+					      sizeof(reg) + len);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg));
+	if (ret)
+		return ret;
+
+	return cdn_dp_mailbox_read_receive(dp, data, len);
+}
+
+int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
+{
+	u8 msg[6], reg[5];
+	int ret;
+
+	msg[0] = 0;
+	msg[1] = 1;
+	msg[2] = (addr >> 16) & 0xff;
+	msg[3] = (addr >> 8) & 0xff;
+	msg[4] = addr & 0xff;
+	msg[5] = value;
+	cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_DPCD,
+			    sizeof(msg), msg);
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_WRITE_DPCD, sizeof(reg));
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg));
+	if (ret)
+		return ret;
+
+	if (addr != (reg[2] << 16 | reg[3] << 8 | reg[4])) {
+		dev_err(dp->dev, "write dpcd failed\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
+			 u32 i_size, const u32 *d_mem, u32 d_size)
+{
+	int i, ret;
+	u32 reg;
+
+	/* reset ucpu before load firmware*/
+	writel(APB_IRAM_PATH | APB_DRAM_PATH | APB_XT_RESET,
+	       dp->regs + APB_CTRL);
+
+	for (i = 0; i < i_size; i += 4)
+		writel(*i_mem++, dp->regs + ADDR_IMEM + i);
+
+	for (i = 0; i < d_size; i += 4)
+		writel(*d_mem++, dp->regs + ADDR_DMEM + i);
+
+	/* un-reset ucpu */
+	writel(0, dp->regs + APB_CTRL);
+
+	/* check the keep alive register to make sure fw working */
+	ret = readx_poll_timeout(readl, dp->regs + KEEP_ALIVE,
+				 reg, reg, 2000, FW_ALIVE_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(dp->dev, "failed to loaded the FW reg = %x\n", reg);
+		return -EINVAL;
+	}
+
+	reg = readl(dp->regs + VER_L) & 0xff;
+	dp->fw_version = reg;
+	reg = readl(dp->regs + VER_H) & 0xff;
+	dp->fw_version |= reg << 8;
+	reg = readl(dp->regs + VER_LIB_L_ADDR) & 0xff;
+	dp->fw_version |= reg << 16;
+	reg = readl(dp->regs + VER_LIB_H_ADDR) & 0xff;
+	dp->fw_version |= reg << 24;
+
+	dev_dbg(dp->dev, "firmware version: %x\n", dp->fw_version);
+
+	return 0;
+}
+
+int cdn_dp_reg_write_bit(struct cdn_dp_device *dp, u16 addr, u8 start_bit,
+			 u8 bits_no, u32 val)
+{
+	u8 field[8];
+
+	field[0] = (addr >> 8) & 0xff;
+	field[1] = addr & 0xff;
+	field[2] = start_bit;
+	field[3] = bits_no;
+	field[4] = (val >> 24) & 0xff;
+	field[5] = (val >> 16) & 0xff;
+	field[6] = (val >> 8) & 0xff;
+	field[7] = val & 0xff;
+
+	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_FIELD,
+				   sizeof(field), field);
+}
+
+int cdn_dp_active(struct cdn_dp_device *dp, bool enable)
+{
+	u8 active = enable ? 1 : 0;
+	u8 resp;
+	int ret;
+
+	/* set firmware status, 1: active; 0: standby */
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_GENERAL,
+				  GENERAL_MAIN_CONTROL, sizeof(active),
+				  &active);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_GENERAL,
+					      GENERAL_MAIN_CONTROL,
+					      sizeof(resp));
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, &resp, sizeof(resp));
+	if (ret)
+		return ret;
+
+	return resp ? 0 : -EINVAL;
+}
+
+int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes)
+{
+	u8 msg[8];
+	int ret;
+
+	msg[0] = CDN_DP_MAX_LINK_RATE;
+	msg[1] = lanes;
+	msg[2] = VOLTAGE_LEVEL_2;
+	msg[3] = PRE_EMPHASIS_LEVEL_3;
+	msg[4] = PTS1 | PTS2 | PTS3 | PTS4;
+	msg[5] = FAST_LT_NOT_SUPPORT;
+	msg[6] = LANE_MAPPING_NORMAL;
+	msg[7] = ENHANCED;
+
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
+				  DPTX_SET_HOST_CAPABILITIES,
+				  sizeof(msg), msg);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_reg_write(dp, DP_AUX_SWAP_INVERSION_CONTROL,
+			       AUX_HOST_INVERT);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+int cdn_dp_event_config(struct cdn_dp_device *dp)
+{
+	u8 msg[5];
+
+	memset(msg, 0, sizeof(msg));
+
+	msg[0] = DPTX_EVENT_ENABLE_HPD | DPTX_EVENT_ENABLE_TRAINING;
+
+	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_ENABLE_EVENT,
+				   sizeof(msg), msg);
+}
+
+u32 cdn_dp_get_event(struct cdn_dp_device *dp)
+{
+	return readl(dp->regs + SW_EVENTS0);
+}
+
+int cdn_dp_get_hpd_status(struct cdn_dp_device *dp)
+{
+	u8 status;
+	int ret;
+
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_HPD_STATE,
+				  0, NULL);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_HPD_STATE, sizeof(status));
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, &status, sizeof(status));
+	if (ret)
+		return ret;
+
+	return status;
+}
+
+int cdn_dp_get_edid_block(void *data, u8 *edid,
+			  unsigned int block, size_t length)
+{
+	struct cdn_dp_device *dp = data;
+	u8 msg[2], reg[2];
+	int ret;
+
+	msg[0] = block / 2;
+	msg[1] = block % 2;
+
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_GET_EDID,
+				  sizeof(msg), msg);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_GET_EDID,
+					      sizeof(reg) + length);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg));
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, edid, length);
+	if (ret)
+		return ret;
+
+	if (reg[0] != length || reg[1] != block / 2) {
+		dev_err(dp->dev, "edid block size err\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int cdn_dp_training_start(struct cdn_dp_device *dp)
+{
+	u8 msg, event[2];
+	unsigned long timeout;
+	int ret;
+
+	msg = LINK_TRAINING_RUN;
+
+	/* start training */
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_TRAINING_CONTROL,
+				  sizeof(msg), &msg);
+	if (ret)
+		return ret;
+
+	timeout = jiffies + msecs_to_jiffies(LINK_TRAINING_TIMEOUT_MS);
+	while (time_before(jiffies, timeout)) {
+		msleep(LINK_TRAINING_RETRY_MS);
+		ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
+					  DPTX_READ_EVENT, 0, NULL);
+		if (ret)
+			return ret;
+
+		ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+						      DPTX_READ_EVENT,
+						      sizeof(event));
+		if (ret)
+			return ret;
+
+		ret = cdn_dp_mailbox_read_receive(dp, event, sizeof(event));
+		if (ret)
+			return ret;
+
+		if (event[1] & EQ_PHASE_FINISHED)
+			return 0;
+	}
+
+	return -ETIMEDOUT;
+}
+
+int cdn_dp_get_training_status(struct cdn_dp_device *dp)
+{
+	u8 status[10];
+	int ret;
+
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_READ_LINK_STAT,
+				  0, NULL);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_READ_LINK_STAT,
+					      sizeof(status));
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, status, sizeof(status));
+	if (ret)
+		return ret;
+
+	dp->link.rate = status[0];
+	dp->link.num_lanes = status[1];
+
+	return 0;
+}
+
+int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active)
+{
+	u8 msg;
+
+	msg = !!active;
+
+	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_SET_VIDEO,
+				   sizeof(msg), &msg);
+}
+
+static int cdn_dp_get_msa_misc(struct video_info *video,
+			       struct drm_display_mode *mode)
+{
+	u8 val[2];
+	u32 msa_misc;
+
+	switch (video->color_fmt) {
+	case PXL_RGB:
+	case Y_ONLY:
+		val[0] = 0;
+		break;
+	/* set YUV default color space conversion to BT601 */
+	case YCBCR_4_4_4:
+		val[0] = 6 + BT_601 * 8;
+		break;
+	case YCBCR_4_2_2:
+		val[0] = 5 + BT_601 * 8;
+		break;
+	case YCBCR_4_2_0:
+		val[0] = 5;
+		break;
+	};
+
+	switch (video->color_depth) {
+	case 6:
+		val[1] = 0;
+		break;
+	case 8:
+		val[1] = 1;
+		break;
+	case 10:
+		val[1] = 2;
+		break;
+	case 12:
+		val[1] = 3;
+		break;
+	case 16:
+		val[1] = 4;
+		break;
+	};
+
+	msa_misc = 2 * val[0] + 32 * val[1] +
+		   ((video->color_fmt == Y_ONLY) ? (1 << 14) : 0);
+
+	return msa_misc;
+}
+
+int cdn_dp_config_video(struct cdn_dp_device *dp)
+{
+	struct video_info *video = &dp->video_info;
+	struct drm_display_mode *mode = &dp->mode;
+	u64 symbol;
+	u32 val, link_rate;
+	u8 bit_per_pix;
+	u8 tu_size_reg = TU_SIZE;
+	int ret;
+
+	bit_per_pix = (video->color_fmt == YCBCR_4_2_2) ?
+		      (video->color_depth * 2) : (video->color_depth * 3);
+
+	link_rate = drm_dp_bw_code_to_link_rate(dp->link.rate);
+
+	val = VIF_BYPASS_INTERLACE;
+	ret = cdn_dp_reg_write(dp, BND_HSYNC2VSYNC, val);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_reg_write(dp, HSYNC2VSYNC_POL_CTRL, 0);
+	if (ret)
+		return ret;
+
+	/* get a best tu_size and symbol */
+	do {
+		tu_size_reg += 2;
+		symbol = tu_size_reg * mode->clock * bit_per_pix;
+		symbol /= dp->link.num_lanes * link_rate * 8;
+	} while ((symbol == 1) || (tu_size_reg - symbol < 4));
+
+	val = symbol + (tu_size_reg << 8);
+	ret = cdn_dp_reg_write(dp, DP_FRAMER_TU, val);
+	if (ret)
+		return ret;
+
+	/* set the FIFO Buffer size */
+	val = ((mode->clock * (symbol + 1) / 1000) + link_rate);
+	val /= (dp->link.num_lanes * link_rate);
+	val = 8 * (symbol + 1) / bit_per_pix - val;
+	val += 2;
+	ret = cdn_dp_reg_write(dp, DP_VC_TABLE(15), val);
+
+	switch (video->color_depth) {
+	case 6:
+		val = BCS_6;
+		break;
+	case 8:
+		val = BCS_8;
+		break;
+	case 10:
+		val = BCS_10;
+		break;
+	case 12:
+		val = BCS_12;
+		break;
+	case 16:
+		val = BCS_16;
+		break;
+	};
+
+	val += video->color_fmt << 8;
+	ret = cdn_dp_reg_write(dp, DP_FRAMER_PXL_REPR, val);
+	if (ret)
+		return ret;
+
+	val = video->h_sync_polarity ? DP_FRAMER_SP_HSP : 0;
+	val |= video->v_sync_polarity ? DP_FRAMER_SP_VSP : 0;
+	ret = cdn_dp_reg_write(dp, DP_FRAMER_SP, val);
+	if (ret)
+		return ret;
+
+	val = (mode->hsync_start - mode->hdisplay) << 16;
+	val |= mode->htotal - mode->hsync_end;
+	ret = cdn_dp_reg_write(dp, DP_FRONT_BACK_PORCH, val);
+	if (ret)
+		return ret;
+
+	val = mode->hdisplay * bit_per_pix / 8;
+	ret = cdn_dp_reg_write(dp, DP_BYTE_COUNT, val);
+	if (ret)
+		return ret;
+
+	val = mode->htotal | ((mode->htotal - mode->hsync_start) << 16);
+	ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_0, val);
+	if (ret)
+		return ret;
+
+	val = mode->hsync_end - mode->hsync_start;
+	val |= (mode->hdisplay << 16) | (video->h_sync_polarity << 15);
+	ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_1, val);
+	if (ret)
+		return ret;
+
+	val = mode->vtotal;
+	val |= ((mode->vtotal - mode->vsync_start) << 16);
+	ret = cdn_dp_reg_write(dp, MSA_VERTICAL_0, val);
+	if (ret)
+		return ret;
+
+	val = mode->vsync_end - mode->vsync_start;
+	val |= mode->vdisplay << 16 | (video->v_sync_polarity << 15);
+	ret = cdn_dp_reg_write(dp, MSA_VERTICAL_1, val);
+	if (ret)
+		return ret;
+
+	val = cdn_dp_get_msa_misc(video, mode);
+	ret = cdn_dp_reg_write(dp, MSA_MISC, val);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_reg_write(dp, STREAM_CONFIG, 1);
+	if (ret)
+		return ret;
+
+	val = mode->hsync_end - mode->hsync_start;
+	val |= (mode->hdisplay << 16);
+	ret = cdn_dp_reg_write(dp, DP_HORIZONTAL, val);
+	if (ret)
+		return ret;
+
+	val = mode->vtotal;
+	val -= (mode->vtotal - mode->vdisplay);
+	val |= (mode->vtotal - mode->vsync_start) << 16;
+	ret = cdn_dp_reg_write(dp, DP_VERTICAL_0, val);
+	if (ret)
+		return ret;
+
+	val = mode->vtotal;
+	ret = cdn_dp_reg_write(dp, DP_VERTICAL_1, val);
+	if (ret)
+		return ret;
+
+	val =  0;
+	return cdn_dp_reg_write_bit(dp, DP_VB_ID, 2, 1, val);
+}
+
+int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio)
+{
+	u32 val;
+	int ret;
+
+	ret = cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, 0);
+	if (ret)
+		return ret;
+
+	val = SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS;
+	val |= SPDIF_FIFO_MID_RANGE(0xe0);
+	val |= SPDIF_JITTER_THRSH(0xe0);
+	val |= SPDIF_JITTER_AVG_WIN(7);
+	writel(val, dp->regs + SPDIF_CTRL_ADDR);
+
+	/* clearn the audio config and reset */
+	writel(0, dp->regs + AUDIO_SRC_CNTL);
+	writel(0, dp->regs + AUDIO_SRC_CNFG);
+	writel(AUDIO_SW_RST, dp->regs + AUDIO_SRC_CNTL);
+	writel(0, dp->regs + AUDIO_SRC_CNTL);
+
+	/* reset smpl2pckt component  */
+	writel(0, dp->regs + SMPL2PKT_CNTL);
+	writel(AUDIO_SW_RST, dp->regs + SMPL2PKT_CNTL);
+	writel(0, dp->regs + SMPL2PKT_CNTL);
+
+	/* reset FIFO */
+	writel(AUDIO_SW_RST, dp->regs + FIFO_CNTL);
+	writel(0, dp->regs + FIFO_CNTL);
+
+	if (audio->format == AFMT_SPDIF)
+		clk_disable_unprepare(dp->spdif_clk);
+
+	return 0;
+}
+
+int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable)
+{
+	return cdn_dp_reg_write_bit(dp, DP_VB_ID, 4, 1, enable);
+}
+
+static void cdn_dp_audio_config_i2s(struct cdn_dp_device *dp,
+				    struct audio_info *audio)
+{
+	int sub_pckt_num = 1, i2s_port_en_val = 0xf, i;
+	u32 val;
+
+	if (audio->channels == 2) {
+		if (dp->link.num_lanes == 1)
+			sub_pckt_num = 2;
+		else
+			sub_pckt_num = 4;
+
+		i2s_port_en_val = 1;
+	} else if (audio->channels == 4) {
+		i2s_port_en_val = 3;
+	}
+
+	writel(0x0, dp->regs + SPDIF_CTRL_ADDR);
+
+	writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL);
+
+	val = MAX_NUM_CH(audio->channels);
+	val |= NUM_OF_I2S_PORTS(audio->channels);
+	val |= AUDIO_TYPE_LPCM;
+	val |= CFG_SUB_PCKT_NUM(sub_pckt_num);
+	writel(val, dp->regs + SMPL2PKT_CNFG);
+
+	if (audio->sample_width == 16)
+		val = 0;
+	else if (audio->sample_width == 24)
+		val = 1 << 9;
+	else
+		val = 2 << 9;
+
+	val |= AUDIO_CH_NUM(audio->channels);
+	val |= I2S_DEC_PORT_EN(i2s_port_en_val);
+	val |= TRANS_SMPL_WIDTH_32;
+	writel(val, dp->regs + AUDIO_SRC_CNFG);
+
+	for (i = 0; i < (audio->channels + 1) / 2; i++) {
+		if (audio->sample_width == 16)
+			val = (0x08 << 8) | (0x08 << 20);
+		else if (audio->sample_width == 24)
+			val = (0x0b << 8) | (0x0b << 20);
+
+		val |= ((2 * i) << 4) | ((2 * i + 1) << 16);
+		writel(val, dp->regs + STTS_BIT_CH(i));
+	}
+
+	switch (audio->sample_rate) {
+	case 32000:
+		val = SAMPLING_FREQ(3) |
+		      ORIGINAL_SAMP_FREQ(0xc);
+		break;
+	case 44100:
+		val = SAMPLING_FREQ(0) |
+		      ORIGINAL_SAMP_FREQ(0xf);
+		break;
+	case 48000:
+		val = SAMPLING_FREQ(2) |
+		      ORIGINAL_SAMP_FREQ(0xd);
+		break;
+	case 88200:
+		val = SAMPLING_FREQ(8) |
+		      ORIGINAL_SAMP_FREQ(0x7);
+		break;
+	case 96000:
+		val = SAMPLING_FREQ(0xa) |
+		      ORIGINAL_SAMP_FREQ(5);
+		break;
+	case 176400:
+		val = SAMPLING_FREQ(0xc) |
+		      ORIGINAL_SAMP_FREQ(3);
+		break;
+	case 192000:
+		val = SAMPLING_FREQ(0xe) |
+		      ORIGINAL_SAMP_FREQ(1);
+		break;
+	}
+	val |= 4;
+	writel(val, dp->regs + COM_CH_STTS_BITS);
+
+	writel(SMPL2PKT_EN, dp->regs + SMPL2PKT_CNTL);
+	writel(I2S_DEC_START, dp->regs + AUDIO_SRC_CNTL);
+}
+
+static void cdn_dp_audio_config_spdif(struct cdn_dp_device *dp)
+{
+	u32 val;
+
+	val = SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS;
+	val |= SPDIF_FIFO_MID_RANGE(0xe0);
+	val |= SPDIF_JITTER_THRSH(0xe0);
+	val |= SPDIF_JITTER_AVG_WIN(7);
+	writel(val, dp->regs + SPDIF_CTRL_ADDR);
+
+	writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL);
+
+	val = MAX_NUM_CH(2) | AUDIO_TYPE_LPCM | CFG_SUB_PCKT_NUM(4);
+	writel(val, dp->regs + SMPL2PKT_CNFG);
+	writel(SMPL2PKT_EN, dp->regs + SMPL2PKT_CNTL);
+
+	val = SPDIF_ENABLE | SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS;
+	val |= SPDIF_FIFO_MID_RANGE(0xe0);
+	val |= SPDIF_JITTER_THRSH(0xe0);
+	val |= SPDIF_JITTER_AVG_WIN(7);
+	writel(val, dp->regs + SPDIF_CTRL_ADDR);
+
+	clk_prepare_enable(dp->spdif_clk);
+	clk_set_rate(dp->spdif_clk, CDN_DP_SPDIF_CLK);
+}
+
+int cdn_dp_audio_config_set(struct cdn_dp_device *dp, struct audio_info *audio)
+{
+	int ret;
+
+	/* reset the spdif clk before config */
+	if (audio->format == AFMT_SPDIF) {
+		reset_control_assert(dp->spdif_rst);
+		reset_control_deassert(dp->spdif_rst);
+	}
+
+	ret = cdn_dp_reg_write(dp, CM_LANE_CTRL, LANE_REF_CYC);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_reg_write(dp, CM_CTRL, 0);
+	if (ret)
+		return ret;
+
+	if (audio->format == AFMT_I2S)
+		cdn_dp_audio_config_i2s(dp, audio);
+	else
+		cdn_dp_audio_config_spdif(dp);
+
+	return cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, AUDIO_PACK_EN);
+}
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
new file mode 100644
index 0000000..1affeed
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
@@ -0,0 +1,479 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Chris Zhong <zyw at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _CDN_DP_REG_H
+#define _CDN_DP_REG_H
+
+#include <linux/bitops.h>
+
+#define ADDR_IMEM		0x10000
+#define ADDR_DMEM		0x20000
+
+/* APB CFG addr */
+#define APB_CTRL			0
+#define XT_INT_CTRL			0x04
+#define MAILBOX_FULL_ADDR		0x08
+#define MAILBOX_EMPTY_ADDR		0x0c
+#define MAILBOX0_WR_DATA		0x10
+#define MAILBOX0_RD_DATA		0x14
+#define KEEP_ALIVE			0x18
+#define VER_L				0x1c
+#define VER_H				0x20
+#define VER_LIB_L_ADDR			0x24
+#define VER_LIB_H_ADDR			0x28
+#define SW_DEBUG_L			0x2c
+#define SW_DEBUG_H			0x30
+#define MAILBOX_INT_MASK		0x34
+#define MAILBOX_INT_STATUS		0x38
+#define SW_CLK_L			0x3c
+#define SW_CLK_H			0x40
+#define SW_EVENTS0			0x44
+#define SW_EVENTS1			0x48
+#define SW_EVENTS2			0x4c
+#define SW_EVENTS3			0x50
+#define XT_OCD_CTRL			0x60
+#define APB_INT_MASK			0x6c
+#define APB_STATUS_MASK			0x70
+
+/* audio decoder addr */
+#define AUDIO_SRC_CNTL			0x30000
+#define AUDIO_SRC_CNFG			0x30004
+#define COM_CH_STTS_BITS		0x30008
+#define STTS_BIT_CH(x)			(0x3000c + ((x) << 2))
+#define SPDIF_CTRL_ADDR			0x3004c
+#define SPDIF_CH1_CS_3100_ADDR		0x30050
+#define SPDIF_CH1_CS_6332_ADDR		0x30054
+#define SPDIF_CH1_CS_9564_ADDR		0x30058
+#define SPDIF_CH1_CS_12796_ADDR		0x3005c
+#define SPDIF_CH1_CS_159128_ADDR	0x30060
+#define SPDIF_CH1_CS_191160_ADDR	0x30064
+#define SPDIF_CH2_CS_3100_ADDR		0x30068
+#define SPDIF_CH2_CS_6332_ADDR		0x3006c
+#define SPDIF_CH2_CS_9564_ADDR		0x30070
+#define SPDIF_CH2_CS_12796_ADDR		0x30074
+#define SPDIF_CH2_CS_159128_ADDR	0x30078
+#define SPDIF_CH2_CS_191160_ADDR	0x3007c
+#define SMPL2PKT_CNTL			0x30080
+#define SMPL2PKT_CNFG			0x30084
+#define FIFO_CNTL			0x30088
+#define FIFO_STTS			0x3008c
+
+/* source pif addr */
+#define SOURCE_PIF_WR_ADDR		0x30800
+#define SOURCE_PIF_WR_REQ		0x30804
+#define SOURCE_PIF_RD_ADDR		0x30808
+#define SOURCE_PIF_RD_REQ		0x3080c
+#define SOURCE_PIF_DATA_WR		0x30810
+#define SOURCE_PIF_DATA_RD		0x30814
+#define SOURCE_PIF_FIFO1_FLUSH		0x30818
+#define SOURCE_PIF_FIFO2_FLUSH		0x3081c
+#define SOURCE_PIF_STATUS		0x30820
+#define SOURCE_PIF_INTERRUPT_SOURCE	0x30824
+#define SOURCE_PIF_INTERRUPT_MASK	0x30828
+#define SOURCE_PIF_PKT_ALLOC_REG	0x3082c
+#define SOURCE_PIF_PKT_ALLOC_WR_EN	0x30830
+#define SOURCE_PIF_SW_RESET		0x30834
+
+/* bellow registers need access by mailbox */
+/* source car addr */
+#define SOURCE_HDTX_CAR			0x0900
+#define SOURCE_DPTX_CAR			0x0904
+#define SOURCE_PHY_CAR			0x0908
+#define SOURCE_CEC_CAR			0x090c
+#define SOURCE_CBUS_CAR			0x0910
+#define SOURCE_PKT_CAR			0x0918
+#define SOURCE_AIF_CAR			0x091c
+#define SOURCE_CIPHER_CAR		0x0920
+#define SOURCE_CRYPTO_CAR		0x0924
+
+/* clock meters addr */
+#define CM_CTRL				0x0a00
+#define CM_I2S_CTRL			0x0a04
+#define CM_SPDIF_CTRL			0x0a08
+#define CM_VID_CTRL			0x0a0c
+#define CM_LANE_CTRL			0x0a10
+#define I2S_NM_STABLE			0x0a14
+#define I2S_NCTS_STABLE			0x0a18
+#define SPDIF_NM_STABLE			0x0a1c
+#define SPDIF_NCTS_STABLE		0x0a20
+#define NMVID_MEAS_STABLE		0x0a24
+#define I2S_MEAS			0x0a40
+#define SPDIF_MEAS			0x0a80
+#define NMVID_MEAS			0x0ac0
+
+/* source vif addr */
+#define BND_HSYNC2VSYNC			0x0b00
+#define HSYNC2VSYNC_F1_L1		0x0b04
+#define HSYNC2VSYNC_F2_L1		0x0b08
+#define HSYNC2VSYNC_STATUS		0x0b0c
+#define HSYNC2VSYNC_POL_CTRL		0x0b10
+
+/* dptx phy addr */
+#define DP_TX_PHY_CONFIG_REG		0x2000
+#define DP_TX_PHY_STATUS_REG		0x2004
+#define DP_TX_PHY_SW_RESET		0x2008
+#define DP_TX_PHY_SCRAMBLER_SEED	0x200c
+#define DP_TX_PHY_TRAINING_01_04	0x2010
+#define DP_TX_PHY_TRAINING_05_08	0x2014
+#define DP_TX_PHY_TRAINING_09_10	0x2018
+#define TEST_COR			0x23fc
+
+/* dptx hpd addr */
+#define HPD_IRQ_DET_MIN_TIMER		0x2100
+#define HPD_IRQ_DET_MAX_TIMER		0x2104
+#define HPD_UNPLGED_DET_MIN_TIMER	0x2108
+#define HPD_STABLE_TIMER		0x210c
+#define HPD_FILTER_TIMER		0x2110
+#define HPD_EVENT_MASK			0x211c
+#define HPD_EVENT_DET			0x2120
+
+/* dpyx framer addr */
+#define DP_FRAMER_GLOBAL_CONFIG		0x2200
+#define DP_SW_RESET			0x2204
+#define DP_FRAMER_TU			0x2208
+#define DP_FRAMER_PXL_REPR		0x220c
+#define DP_FRAMER_SP			0x2210
+#define AUDIO_PACK_CONTROL		0x2214
+#define DP_VC_TABLE(x)			(0x2218 + ((x) << 2))
+#define DP_VB_ID			0x2258
+#define DP_MTPH_LVP_CONTROL		0x225c
+#define DP_MTPH_SYMBOL_VALUES		0x2260
+#define DP_MTPH_ECF_CONTROL		0x2264
+#define DP_MTPH_ACT_CONTROL		0x2268
+#define DP_MTPH_STATUS			0x226c
+#define DP_INTERRUPT_SOURCE		0x2270
+#define DP_INTERRUPT_MASK		0x2274
+#define DP_FRONT_BACK_PORCH		0x2278
+#define DP_BYTE_COUNT			0x227c
+
+/* dptx stream addr */
+#define MSA_HORIZONTAL_0		0x2280
+#define MSA_HORIZONTAL_1		0x2284
+#define MSA_VERTICAL_0			0x2288
+#define MSA_VERTICAL_1			0x228c
+#define MSA_MISC			0x2290
+#define STREAM_CONFIG			0x2294
+#define AUDIO_PACK_STATUS		0x2298
+#define VIF_STATUS			0x229c
+#define PCK_STUFF_STATUS_0		0x22a0
+#define PCK_STUFF_STATUS_1		0x22a4
+#define INFO_PACK_STATUS		0x22a8
+#define RATE_GOVERNOR_STATUS		0x22ac
+#define DP_HORIZONTAL			0x22b0
+#define DP_VERTICAL_0			0x22b4
+#define DP_VERTICAL_1			0x22b8
+#define DP_BLOCK_SDP			0x22bc
+
+/* dptx glbl addr */
+#define DPTX_LANE_EN			0x2300
+#define DPTX_ENHNCD			0x2304
+#define DPTX_INT_MASK			0x2308
+#define DPTX_INT_STATUS			0x230c
+
+/* dp aux addr */
+#define DP_AUX_HOST_CONTROL		0x2800
+#define DP_AUX_INTERRUPT_SOURCE		0x2804
+#define DP_AUX_INTERRUPT_MASK		0x2808
+#define DP_AUX_SWAP_INVERSION_CONTROL	0x280c
+#define DP_AUX_SEND_NACK_TRANSACTION	0x2810
+#define DP_AUX_CLEAR_RX			0x2814
+#define DP_AUX_CLEAR_TX			0x2818
+#define DP_AUX_TIMER_STOP		0x281c
+#define DP_AUX_TIMER_CLEAR		0x2820
+#define DP_AUX_RESET_SW			0x2824
+#define DP_AUX_DIVIDE_2M		0x2828
+#define DP_AUX_TX_PREACHARGE_LENGTH	0x282c
+#define DP_AUX_FREQUENCY_1M_MAX		0x2830
+#define DP_AUX_FREQUENCY_1M_MIN		0x2834
+#define DP_AUX_RX_PRE_MIN		0x2838
+#define DP_AUX_RX_PRE_MAX		0x283c
+#define DP_AUX_TIMER_PRESET		0x2840
+#define DP_AUX_NACK_FORMAT		0x2844
+#define DP_AUX_TX_DATA			0x2848
+#define DP_AUX_RX_DATA			0x284c
+#define DP_AUX_TX_STATUS		0x2850
+#define DP_AUX_RX_STATUS		0x2854
+#define DP_AUX_RX_CYCLE_COUNTER		0x2858
+#define DP_AUX_MAIN_STATES		0x285c
+#define DP_AUX_MAIN_TIMER		0x2860
+#define DP_AUX_AFE_OUT			0x2864
+
+/* crypto addr */
+#define CRYPTO_HDCP_REVISION		0x5800
+#define HDCP_CRYPTO_CONFIG		0x5804
+#define CRYPTO_INTERRUPT_SOURCE		0x5808
+#define CRYPTO_INTERRUPT_MASK		0x580c
+#define CRYPTO22_CONFIG			0x5818
+#define CRYPTO22_STATUS			0x581c
+#define SHA_256_DATA_IN			0x583c
+#define SHA_256_DATA_OUT_(x)		(0x5850 + ((x) << 2))
+#define AES_32_KEY_(x)			(0x5870 + ((x) << 2))
+#define AES_32_DATA_IN			0x5880
+#define AES_32_DATA_OUT_(x)		(0x5884 + ((x) << 2))
+#define CRYPTO14_CONFIG			0x58a0
+#define CRYPTO14_STATUS			0x58a4
+#define CRYPTO14_PRNM_OUT		0x58a8
+#define CRYPTO14_KM_0			0x58ac
+#define CRYPTO14_KM_1			0x58b0
+#define CRYPTO14_AN_0			0x58b4
+#define CRYPTO14_AN_1			0x58b8
+#define CRYPTO14_YOUR_KSV_0		0x58bc
+#define CRYPTO14_YOUR_KSV_1		0x58c0
+#define CRYPTO14_MI_0			0x58c4
+#define CRYPTO14_MI_1			0x58c8
+#define CRYPTO14_TI_0			0x58cc
+#define CRYPTO14_KI_0			0x58d0
+#define CRYPTO14_KI_1			0x58d4
+#define CRYPTO14_BLOCKS_NUM		0x58d8
+#define CRYPTO14_KEY_MEM_DATA_0		0x58dc
+#define CRYPTO14_KEY_MEM_DATA_1		0x58e0
+#define CRYPTO14_SHA1_MSG_DATA		0x58e4
+#define CRYPTO14_SHA1_V_VALUE_(x)	(0x58e8 + ((x) << 2))
+#define TRNG_CTRL			0x58fc
+#define TRNG_DATA_RDY			0x5900
+#define TRNG_DATA			0x5904
+
+/* cipher addr */
+#define HDCP_REVISION			0x60000
+#define INTERRUPT_SOURCE		0x60004
+#define INTERRUPT_MASK			0x60008
+#define HDCP_CIPHER_CONFIG		0x6000c
+#define AES_128_KEY_0			0x60010
+#define AES_128_KEY_1			0x60014
+#define AES_128_KEY_2			0x60018
+#define AES_128_KEY_3			0x6001c
+#define AES_128_RANDOM_0		0x60020
+#define AES_128_RANDOM_1		0x60024
+#define CIPHER14_KM_0			0x60028
+#define CIPHER14_KM_1			0x6002c
+#define CIPHER14_STATUS			0x60030
+#define CIPHER14_RI_PJ_STATUS		0x60034
+#define CIPHER_MODE			0x60038
+#define CIPHER14_AN_0			0x6003c
+#define CIPHER14_AN_1			0x60040
+#define CIPHER22_AUTH			0x60044
+#define CIPHER14_R0_DP_STATUS		0x60048
+#define CIPHER14_BOOTSTRAP		0x6004c
+
+#define DPTX_FRMR_DATA_CLK_RSTN_EN	BIT(11)
+#define DPTX_FRMR_DATA_CLK_EN		BIT(10)
+#define DPTX_PHY_DATA_RSTN_EN		BIT(9)
+#define DPTX_PHY_DATA_CLK_EN		BIT(8)
+#define DPTX_PHY_CHAR_RSTN_EN		BIT(7)
+#define DPTX_PHY_CHAR_CLK_EN		BIT(6)
+#define SOURCE_AUX_SYS_CLK_RSTN_EN	BIT(5)
+#define SOURCE_AUX_SYS_CLK_EN		BIT(4)
+#define DPTX_SYS_CLK_RSTN_EN		BIT(3)
+#define DPTX_SYS_CLK_EN			BIT(2)
+#define CFG_DPTX_VIF_CLK_RSTN_EN	BIT(1)
+#define CFG_DPTX_VIF_CLK_EN		BIT(0)
+
+#define SOURCE_PHY_RSTN_EN		BIT(1)
+#define SOURCE_PHY_CLK_EN		BIT(0)
+
+#define SOURCE_PKT_SYS_RSTN_EN		BIT(3)
+#define SOURCE_PKT_SYS_CLK_EN		BIT(2)
+#define SOURCE_PKT_DATA_RSTN_EN		BIT(1)
+#define SOURCE_PKT_DATA_CLK_EN		BIT(0)
+
+#define SPDIF_CDR_CLK_RSTN_EN		BIT(5)
+#define SPDIF_CDR_CLK_EN		BIT(4)
+#define SOURCE_AIF_SYS_RSTN_EN		BIT(3)
+#define SOURCE_AIF_SYS_CLK_EN		BIT(2)
+#define SOURCE_AIF_CLK_RSTN_EN		BIT(1)
+#define SOURCE_AIF_CLK_EN		BIT(0)
+
+#define SOURCE_CIPHER_SYSTEM_CLK_RSTN_EN	BIT(3)
+#define SOURCE_CIPHER_SYS_CLK_EN		BIT(2)
+#define SOURCE_CIPHER_CHAR_CLK_RSTN_EN		BIT(1)
+#define SOURCE_CIPHER_CHAR_CLK_EN		BIT(0)
+
+#define SOURCE_CRYPTO_SYS_CLK_RSTN_EN	BIT(1)
+#define SOURCE_CRYPTO_SYS_CLK_EN	BIT(0)
+
+#define APB_IRAM_PATH			BIT(2)
+#define APB_DRAM_PATH			BIT(1)
+#define APB_XT_RESET			BIT(0)
+
+#define MAILBOX_INT_MASK_BIT		BIT(1)
+#define PIF_INT_MASK_BIT		BIT(0)
+#define ALL_INT_MASK			3
+
+/* mailbox */
+#define MB_OPCODE_ID			0
+#define MB_MODULE_ID			1
+#define MB_SIZE_MSB_ID			2
+#define MB_SIZE_LSB_ID			3
+#define MB_DATA_ID			4
+
+#define MB_MODULE_ID_DP_TX		0x01
+#define MB_MODULE_ID_HDCP_TX		0x07
+#define MB_MODULE_ID_HDCP_RX		0x08
+#define MB_MODULE_ID_HDCP_GENERAL	0x09
+#define MB_MODULE_ID_GENERAL		0x0a
+
+/* general opcode */
+#define GENERAL_MAIN_CONTROL            0x01
+#define GENERAL_TEST_ECHO               0x02
+#define GENERAL_BUS_SETTINGS            0x03
+#define GENERAL_TEST_ACCESS             0x04
+
+#define DPTX_SET_POWER_MNG			0x00
+#define DPTX_SET_HOST_CAPABILITIES		0x01
+#define DPTX_GET_EDID				0x02
+#define DPTX_READ_DPCD				0x03
+#define DPTX_WRITE_DPCD				0x04
+#define DPTX_ENABLE_EVENT			0x05
+#define DPTX_WRITE_REGISTER			0x06
+#define DPTX_READ_REGISTER			0x07
+#define DPTX_WRITE_FIELD			0x08
+#define DPTX_TRAINING_CONTROL			0x09
+#define DPTX_READ_EVENT				0x0a
+#define DPTX_READ_LINK_STAT			0x0b
+#define DPTX_SET_VIDEO				0x0c
+#define DPTX_SET_AUDIO				0x0d
+#define DPTX_GET_LAST_AUX_STAUS			0x0e
+#define DPTX_SET_LINK_BREAK_POINT		0x0f
+#define DPTX_FORCE_LANES			0x10
+#define DPTX_HPD_STATE				0x11
+
+#define DPTX_EVENT_ENABLE_HPD			BIT(0)
+#define DPTX_EVENT_ENABLE_TRAINING		BIT(1)
+
+#define LINK_TRAINING_NOT_ACTIVE		0
+#define LINK_TRAINING_RUN			1
+#define LINK_TRAINING_RESTART			2
+
+#define CONTROL_VIDEO_IDLE			0
+#define CONTROL_VIDEO_VALID			1
+
+#define VIF_BYPASS_INTERLACE			BIT(13)
+#define INTERLACE_FMT_DET			BIT(12)
+#define INTERLACE_DTCT_WIN			0x20
+
+#define DP_FRAMER_SP_INTERLACE_EN		BIT(2)
+#define DP_FRAMER_SP_HSP			BIT(1)
+#define DP_FRAMER_SP_VSP			BIT(0)
+
+/* capability */
+#define AUX_HOST_INVERT				3
+#define	FAST_LT_SUPPORT				1
+#define FAST_LT_NOT_SUPPORT			0
+#define LANE_MAPPING_NORMAL			0xe4
+#define LANE_MAPPING_FLIPPED			0x1b
+#define ENHANCED				1
+
+#define	FULL_LT_STARTED				BIT(0)
+#define FASE_LT_STARTED				BIT(1)
+#define CLK_RECOVERY_FINISHED			BIT(2)
+#define EQ_PHASE_FINISHED			BIT(3)
+#define FASE_LT_START_FINISHED			BIT(4)
+#define CLK_RECOVERY_FAILED			BIT(5)
+#define EQ_PHASE_FAILED				BIT(6)
+#define FASE_LT_FAILED				BIT(7)
+
+#define DPTX_HPD_EVENT				BIT(0)
+#define DPTX_TRAINING_EVENT			BIT(1)
+#define HDCP_TX_STATUS_EVENT			BIT(4)
+#define HDCP2_TX_IS_KM_STORED_EVENT		BIT(5)
+#define HDCP2_TX_STORE_KM_EVENT			BIT(6)
+#define HDCP_TX_IS_RECEIVER_ID_VALID_EVENT	BIT(7)
+
+#define TU_SIZE					30
+#define CDN_DP_MAX_LINK_RATE			DP_LINK_BW_5_4
+
+/* audio */
+#define AUDIO_PACK_EN				BIT(8)
+#define SAMPLING_FREQ(x)			(((x) & 0xf) << 16)
+#define ORIGINAL_SAMP_FREQ(x)			(((x) & 0xf) << 24)
+#define SYNC_WR_TO_CH_ZERO			BIT(1)
+#define I2S_DEC_START				BIT(1)
+#define AUDIO_SW_RST				BIT(0)
+#define SMPL2PKT_EN				BIT(1)
+#define MAX_NUM_CH(x)				(((x) & 0x1f) - 1)
+#define NUM_OF_I2S_PORTS(x)			((((x) / 2 - 1) & 0x3) << 5)
+#define AUDIO_TYPE_LPCM				(2 << 7)
+#define CFG_SUB_PCKT_NUM(x)			((((x) - 1) & 0x7) << 11)
+#define AUDIO_CH_NUM(x)				((((x) - 1) & 0x1f) << 2)
+#define TRANS_SMPL_WIDTH_16			0
+#define TRANS_SMPL_WIDTH_24			BIT(11)
+#define TRANS_SMPL_WIDTH_32			(2 << 11)
+#define I2S_DEC_PORT_EN(x)			(((x) & 0xf) << 17)
+#define SPDIF_ENABLE				BIT(21)
+#define SPDIF_AVG_SEL				BIT(20)
+#define SPDIF_JITTER_BYPASS			BIT(19)
+#define SPDIF_FIFO_MID_RANGE(x)			(((x) & 0xff) << 11)
+#define SPDIF_JITTER_THRSH(x)			(((x) & 0xff) << 3)
+#define SPDIF_JITTER_AVG_WIN(x)			((x) & 0x7)
+
+/* Refernce cycles when using lane clock as refernce */
+#define LANE_REF_CYC				0x8000
+
+enum voltage_swing_level {
+	VOLTAGE_LEVEL_0,
+	VOLTAGE_LEVEL_1,
+	VOLTAGE_LEVEL_2,
+	VOLTAGE_LEVEL_3,
+};
+
+enum pre_emphasis_level {
+	PRE_EMPHASIS_LEVEL_0,
+	PRE_EMPHASIS_LEVEL_1,
+	PRE_EMPHASIS_LEVEL_2,
+	PRE_EMPHASIS_LEVEL_3,
+};
+
+enum pattern_set {
+	PTS1		= BIT(0),
+	PTS2		= BIT(1),
+	PTS3		= BIT(2),
+	PTS4		= BIT(3),
+	DP_NONE		= BIT(4)
+};
+
+enum vic_color_depth {
+	BCS_6 = 0x1,
+	BCS_8 = 0x2,
+	BCS_10 = 0x4,
+	BCS_12 = 0x8,
+	BCS_16 = 0x10,
+};
+
+enum vic_bt_type {
+	BT_601 = 0x0,
+	BT_709 = 0x1,
+};
+
+void cdn_dp_clock_reset(struct cdn_dp_device *dp);
+
+void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, u32 clk);
+int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
+			 u32 i_size, const u32 *d_mem, u32 d_size);
+int cdn_dp_active(struct cdn_dp_device *dp, bool enable);
+int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes);
+int cdn_dp_event_config(struct cdn_dp_device *dp);
+u32 cdn_dp_get_event(struct cdn_dp_device *dp);
+int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
+int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value);
+int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len);
+int cdn_dp_get_edid_block(void *dp, u8 *edid,
+			  unsigned int block, size_t length);
+int cdn_dp_training_start(struct cdn_dp_device *dp);
+int cdn_dp_get_training_status(struct cdn_dp_device *dp);
+int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active);
+int cdn_dp_config_video(struct cdn_dp_device *dp);
+int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio);
+int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable);
+int cdn_dp_audio_config_set(struct cdn_dp_device *dp, struct audio_info *audio);
+#endif /* _CDN_DP_REG_H */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index edd7ec2..d723d8e 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -969,9 +969,11 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
 		vop_dsp_hold_valid_irq_disable(vop);
 	}
 
-	pin_pol = 0x8;
-	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1;
-	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : (1 << 1);
+	pin_pol = BIT(DCLK_INVERT);
+	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ?
+		   0 : BIT(HSYNC_POSITIVE);
+	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ?
+		   0 : BIT(VSYNC_POSITIVE);
 	VOP_CTRL_SET(vop, pin_pol, pin_pol);
 
 	switch (s->output_type) {
@@ -991,6 +993,11 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
 		VOP_CTRL_SET(vop, mipi_pin_pol, pin_pol);
 		VOP_CTRL_SET(vop, mipi_en, 1);
 		break;
+	case DRM_MODE_CONNECTOR_DisplayPort:
+		pin_pol &= ~BIT(DCLK_INVERT);
+		VOP_CTRL_SET(vop, dp_pin_pol, pin_pol);
+		VOP_CTRL_SET(vop, dp_en, 1);
+		break;
 	default:
 		DRM_ERROR("unsupport connector_type[%d]\n", s->output_type);
 	}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index ff4f52e..4820a8b 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -45,6 +45,7 @@ struct vop_ctrl {
 	struct vop_reg edp_en;
 	struct vop_reg hdmi_en;
 	struct vop_reg mipi_en;
+	struct vop_reg dp_en;
 	struct vop_reg out_mode;
 	struct vop_reg dither_down;
 	struct vop_reg dither_up;
@@ -53,6 +54,7 @@ struct vop_ctrl {
 	struct vop_reg hdmi_pin_pol;
 	struct vop_reg edp_pin_pol;
 	struct vop_reg mipi_pin_pol;
+	struct vop_reg dp_pin_pol;
 
 	struct vop_reg htotal_pw;
 	struct vop_reg hact_st_end;
@@ -242,6 +244,13 @@ enum scale_down_mode {
 	SCALE_DOWN_AVG = 0x1
 };
 
+enum vop_pol {
+	HSYNC_POSITIVE = 0,
+	VSYNC_POSITIVE = 1,
+	DEN_NEGATIVE   = 2,
+	DCLK_INVERT    = 3
+};
+
 #define FRAC_16_16(mult, div)    (((mult) << 16) / (div))
 #define SCL_FT_DEFAULT_FIXPOINT_SHIFT	12
 #define SCL_MAX_VSKIPLINES		4
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 5b1ae1f..dcf172e 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -281,6 +281,7 @@ static const struct vop_data rk3288_vop = {
 static const struct vop_ctrl rk3399_ctrl_data = {
 	.standby = VOP_REG(RK3399_SYS_CTRL, 0x1, 22),
 	.gate_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 23),
+	.dp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 11),
 	.rgb_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 12),
 	.hdmi_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 13),
 	.edp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 14),
@@ -290,6 +291,7 @@ static const struct vop_ctrl rk3399_ctrl_data = {
 	.data_blank = VOP_REG(RK3399_DSP_CTRL0, 0x1, 19),
 	.out_mode = VOP_REG(RK3399_DSP_CTRL0, 0xf, 0),
 	.rgb_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16),
+	.dp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16),
 	.hdmi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 20),
 	.edp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 24),
 	.mipi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 28),
-- 
2.6.3



From hl at rock-chips.com  Thu Jul 21 23:37:45 2016
From: hl at rock-chips.com (Lin Huang)
Date: Fri, 22 Jul 2016 14:37:45 +0800
Subject: [PATCH] clk: rockchip: add clock flag parameter when register pll
Message-ID: <1469169465-5892-1-git-send-email-hl@rock-chips.com>

From: Heiko St??bner <heiko at sntech.de>

add clock flag parameter so we can pass specific clock flag
(like CLK_GET_RATE_NOCACHE etc..)to pll driver.

Change-Id: I1e076b3efa6b5da082b6e68e2e2a4c9dfd93e3d4
Signed-off-by: Heiko St??bner <heiko at sntech.de>
Signed-off-by: Lin Huang <hl at rock-chips.com>
---
 drivers/clk/rockchip/clk-pll.c | 4 ++--
 drivers/clk/rockchip/clk.c     | 2 +-
 drivers/clk/rockchip/clk.h     | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c
index db81e45..9c1373e 100644
--- a/drivers/clk/rockchip/clk-pll.c
+++ b/drivers/clk/rockchip/clk-pll.c
@@ -837,7 +837,7 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
 		u8 num_parents, int con_offset, int grf_lock_offset,
 		int lock_shift, int mode_offset, int mode_shift,
 		struct rockchip_pll_rate_table *rate_table,
-		u8 clk_pll_flags)
+		unsigned long flags, u8 clk_pll_flags)
 {
 	const char *pll_parents[3];
 	struct clk_init_data init;
@@ -892,7 +892,7 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
 	init.name = pll_name;
 
 	/* keep all plls untouched for now */
-	init.flags = CLK_IGNORE_UNUSED;
+	init.flags = flags | CLK_IGNORE_UNUSED;
 
 	init.parent_names = &parent_names[0];
 	init.num_parents = 1;
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
index 7ffd134..1f1c74f 100644
--- a/drivers/clk/rockchip/clk.c
+++ b/drivers/clk/rockchip/clk.c
@@ -385,7 +385,7 @@ void __init rockchip_clk_register_plls(struct rockchip_clk_provider *ctx,
 				list->con_offset, grf_lock_offset,
 				list->lock_shift, list->mode_offset,
 				list->mode_shift, list->rate_table,
-				list->pll_flags);
+				list->flags, list->pll_flags);
 		if (IS_ERR(clk)) {
 			pr_err("%s: failed to register clock %s\n", __func__,
 				list->name);
diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h
index 2194ffa..3747de5 100644
--- a/drivers/clk/rockchip/clk.h
+++ b/drivers/clk/rockchip/clk.h
@@ -238,7 +238,7 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
 		u8 num_parents, int con_offset, int grf_lock_offset,
 		int lock_shift, int mode_offset, int mode_shift,
 		struct rockchip_pll_rate_table *rate_table,
-		u8 clk_pll_flags);
+		unsigned long flags, u8 clk_pll_flags);
 
 struct rockchip_cpuclk_clksel {
 	int reg;
-- 
2.6.6



From frank.wang at rock-chips.com  Fri Jul 22 00:00:42 2016
From: frank.wang at rock-chips.com (Frank Wang)
Date: Fri, 22 Jul 2016 15:00:42 +0800
Subject: [PATCH v9 0/4] Add a new Rockchip usb2 phy driver
Message-ID: <1469170846-15617-1-git-send-email-frank.wang@rock-chips.com>

The newer SoCs (rk3366, rk3399) of Rock-chip take a different usb-phy
IP block than rk3288 and before, and most of phy-related registers are
also different from the past, so a new phy driver is required necessarily.

These series patch add phy-rockchip-inno-usb2.c and the corresponding
dt-bindings.

Changes in v9:
 - Move the usb gpio config from dtsi to dts.
 - Add vin-supply (vcc5v0_sys) for vcc5v0_host.
 - Change some properties' name.

Changes in v8:
 - Added support for rk3399 SoC.
 - Prepare input clock of phy at probe time.
 - Improved sm_work function to handle phy-port state more clearly.

Changes in v7:
 - Renamed functions *usb2phy_resume/*usb2phy_suspend to *usb2phy_power_on/usb2phy_power_off.

Changes in v6:
 - Changed '_' to '-' for otg-id and otg-bvalid property in devicetree bindings.
 - Fixed the output clock would be disabled more than once while phy-port was going to suspend.
 - Improved the driver to avoid the currently empty otg-port would cause null-pointer dereferences.

Changes in v5:
 - Added 'reg' property both in devicetree bindings and driver to match the different phy-blocks.

Changes in v4:
 - Used 'phy-supply' instead of 'vbus_*-supply'.

Changes in v3:
 - Supplemented some hardware-description into the devicetree bindings.
 - Resolved the mapping defect between fixed value in driver and the property
   in devicetree.
 - Code cleanup.

Changes in v2:
 - Specified more hardware-description into the devicetree bindings.
 - Optimized some process of driver.

Frank Wang (4):
  Documentation: bindings: add DT documentation for Rockchip USB2PHY
  phy: rockchip-inno-usb2: add a new driver for Rockchip usb2phy
  arm64: dts: rockchip: add usb2-phy support for rk3399
  arm64: dts: rockchip: configure usb2-phy support for rk3399-evb

 .../bindings/phy/phy-rockchip-inno-usb2.txt        |   64 ++
 arch/arm64/boot/dts/rockchip/rk3399-evb.dts        |   44 ++
 arch/arm64/boot/dts/rockchip/rk3399.dtsi           |   42 +-
 drivers/phy/Kconfig                                |    7 +
 drivers/phy/Makefile                               |    1 +
 drivers/phy/phy-rockchip-inno-usb2.c               |  707 ++++++++++++++++++++
 6 files changed, 864 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt
 create mode 100644 drivers/phy/phy-rockchip-inno-usb2.c

-- 
1.7.9.5




From frank.wang at rock-chips.com  Fri Jul 22 00:00:43 2016
From: frank.wang at rock-chips.com (Frank Wang)
Date: Fri, 22 Jul 2016 15:00:43 +0800
Subject: [PATCH v9 1/4] Documentation: bindings: add DT documentation for
 Rockchip USB2PHY
In-Reply-To: <1469170846-15617-1-git-send-email-frank.wang@rock-chips.com>
References: <1469170846-15617-1-git-send-email-frank.wang@rock-chips.com>
Message-ID: <1469170846-15617-2-git-send-email-frank.wang@rock-chips.com>

Signed-off-by: Frank Wang <frank.wang at rock-chips.com>
Acked-by: Rob Herring <robh at kernel.org>
Reviewed-by: Heiko Stuebner <heiko at sntech.de>
---

Changes in v9: None

Changes in v8: None

Changes in v7: None

Changes in v6:
 - Changed '_' to '-' for otg-id and otg-bvalid property.

Changes in v5:
 - Added 'reg' property to identify the different phy-blocks.

Changes in v4:
 - Used 'phy-supply' instead of 'vbus_*-supply'.

Changes in v3:
 - Added 'clocks' and 'clock-names' optional properties.
 - Specified 'otg-port' and 'host-port' as the sub-node name.

Changes in v2:
 - Changed vbus_host optional property from gpio to regulator.
 - Specified vbus_otg-supply optional property.
 - Specified otg_id and otg_bvalid property.

 .../bindings/phy/phy-rockchip-inno-usb2.txt        |   64 ++++++++++++++++++++
 1 file changed, 64 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt

diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt b/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt
new file mode 100644
index 0000000..3c29c77
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt
@@ -0,0 +1,64 @@
+ROCKCHIP USB2.0 PHY WITH INNO IP BLOCK
+
+Required properties (phy (parent) node):
+ - compatible : should be one of the listed compatibles:
+	* "rockchip,rk3366-usb2phy"
+	* "rockchip,rk3399-usb2phy"
+ - reg : the address offset of grf for usb-phy configuration.
+ - #clock-cells : should be 0.
+ - clock-output-names : specify the 480m output clock name.
+
+Optional properties:
+ - clocks : phandle + phy specifier pair, for the input clock of phy.
+ - clock-names : input clock name of phy, must be "phyclk".
+
+Required nodes : a sub-node is required for each port the phy provides.
+		 The sub-node name is used to identify host or otg port,
+		 and shall be the following entries:
+	* "otg-port" : the name of otg port.
+	* "host-port" : the name of host port.
+
+Required properties (port (child) node):
+ - #phy-cells : must be 0. See ./phy-bindings.txt for details.
+ - interrupts : specify an interrupt for each entry in interrupt-names.
+ - interrupt-names : a list which shall be the following entries:
+	* "otg-id" : for the otg id interrupt.
+	* "otg-bvalid" : for the otg vbus interrupt.
+	* "linestate" : for the host/otg linestate interrupt.
+
+Optional properties:
+ - phy-supply : phandle to a regulator that provides power to VBUS.
+		See ./phy-bindings.txt for details.
+
+Example:
+
+grf: syscon at ff770000 {
+	compatible = "rockchip,rk3366-grf", "syscon", "simple-mfd";
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+...
+
+	u2phy: usb2-phy at 700 {
+		compatible = "rockchip,rk3366-usb2phy";
+		reg = <0x700 0x2c>;
+		#clock-cells = <0>;
+		clock-output-names = "sclk_otgphy0_480m";
+
+		u2phy_otg: otg-port {
+			#phy-cells = <0>;
+			interrupts = <GIC_SPI 93 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 95 IRQ_TYPE_LEVEL_HIGH>;
+			interrupt-names = "otg-id", "otg-bvalid", "linestate";
+			status = "okay";
+		};
+
+		u2phy_host: host-port {
+			#phy-cells = <0>;
+			interrupts = <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>;
+			interrupt-names = "linestate";
+			status = "okay";
+		};
+	};
+};
-- 
1.7.9.5




From frank.wang at rock-chips.com  Fri Jul 22 00:00:44 2016
From: frank.wang at rock-chips.com (Frank Wang)
Date: Fri, 22 Jul 2016 15:00:44 +0800
Subject: [PATCH v9 2/4] phy: rockchip-inno-usb2: add a new driver for Rockchip
 usb2phy
In-Reply-To: <1469170846-15617-1-git-send-email-frank.wang@rock-chips.com>
References: <1469170846-15617-1-git-send-email-frank.wang@rock-chips.com>
Message-ID: <1469170846-15617-3-git-send-email-frank.wang@rock-chips.com>

The newer SoCs (rk3366, rk3399) take a different usb-phy IP block
than rk3288 and before, and most of phy-related registers are also
different from the past, so a new phy driver is required necessarily.

Signed-off-by: Frank Wang <frank.wang at rock-chips.com>
Suggested-by: Heiko Stuebner <heiko at sntech.de>
Suggested-by: Guenter Roeck <linux at roeck-us.net>
Suggested-by: Doug Anderson <dianders at chromium.org>
Reviewed-by: Heiko Stuebner <heiko at sntech.de>
Reviewed-by: Guenter Roeck <linux at roeck-us.net>
---

Changes in v9:
 - Add "Reviewed-by" info.

Changes in v8:
 - Added support for rk3399 SoC.
 - Prepare input clock of phy at probe time.
 - Improved sm_work function to handle phy-port state more clearly.

Changes in v7:
 - renamed functions *usb2phy_resume/*usb2phy_suspend to *usb2phy_power_on/usb2phy_power_off.

Changes in v6:
 - Fixed the output clock would be disabled more than once while phy-port was going to suspend.
 - Improved the driver to avoid the currently empty otg-port would cause null-pointer dereferences.

Changes in v5:
 - Added 'reg' in the data block to match the different phy-blocks in dt.

Changes in v4:
 - Removed some processes related to 'vbus_host-supply'.

Changes in v3:
 - Resolved the mapping defect between fixed value in driver and the property
   in devicetree.
 - Optimized 480m output clock register function.
 - Code cleanup.

Changes in v2:
 - Changed vbus_host operation from gpio to regulator in *_probe.
 - Improved the fault treatment relate to 480m clock register.
 - Cleaned up some meaningless codes in *_clk480m_disable.
 - made more clear the comment of *_sm_work.

 drivers/phy/Kconfig                  |    7 +
 drivers/phy/Makefile                 |    1 +
 drivers/phy/phy-rockchip-inno-usb2.c |  707 ++++++++++++++++++++++++++++++++++
 3 files changed, 715 insertions(+)
 create mode 100644 drivers/phy/phy-rockchip-inno-usb2.c

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index b869b98..29ef15c 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -347,6 +347,13 @@ config PHY_ROCKCHIP_USB
 	help
 	  Enable this to support the Rockchip USB 2.0 PHY.
 
+config PHY_ROCKCHIP_INNO_USB2
+	tristate "Rockchip INNO USB2PHY Driver"
+	depends on ARCH_ROCKCHIP && OF
+	select GENERIC_PHY
+	help
+	  Support for Rockchip USB2.0 PHY with Innosilicon IP block.
+
 config PHY_ROCKCHIP_EMMC
 	tristate "Rockchip EMMC PHY Driver"
 	depends on ARCH_ROCKCHIP && OF
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 9c3e73c..4963fbc 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -38,6 +38,7 @@ phy-exynos-usb2-$(CONFIG_PHY_S5PV210_USB2)	+= phy-s5pv210-usb2.o
 obj-$(CONFIG_PHY_EXYNOS5_USBDRD)	+= phy-exynos5-usbdrd.o
 obj-$(CONFIG_PHY_QCOM_APQ8064_SATA)	+= phy-qcom-apq8064-sata.o
 obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o
+obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2)	+= phy-rockchip-inno-usb2.o
 obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o
 obj-$(CONFIG_PHY_ROCKCHIP_DP)		+= phy-rockchip-dp.o
 obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA)	+= phy-qcom-ipq806x-sata.o
diff --git a/drivers/phy/phy-rockchip-inno-usb2.c b/drivers/phy/phy-rockchip-inno-usb2.c
new file mode 100644
index 0000000..ac20310
--- /dev/null
+++ b/drivers/phy/phy-rockchip-inno-usb2.c
@@ -0,0 +1,707 @@
+/*
+ * Rockchip USB2.0 PHY with Innosilicon IP block driver
+ *
+ * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/gpio/consumer.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#define BIT_WRITEABLE_SHIFT	16
+#define SCHEDULE_DELAY	(60 * HZ)
+
+enum rockchip_usb2phy_port_id {
+	USB2PHY_PORT_OTG,
+	USB2PHY_PORT_HOST,
+	USB2PHY_NUM_PORTS,
+};
+
+enum rockchip_usb2phy_host_state {
+	PHY_STATE_HS_ONLINE	= 0,
+	PHY_STATE_DISCONNECT	= 1,
+	PHY_STATE_CONNECT	= 2,
+	PHY_STATE_FS_LS_ONLINE	= 4,
+};
+
+struct usb2phy_reg {
+	unsigned int	offset;
+	unsigned int	bitend;
+	unsigned int	bitstart;
+	unsigned int	disable;
+	unsigned int	enable;
+};
+
+/**
+ * struct rockchip_usb2phy_port_cfg: usb-phy port configuration.
+ * @phy_sus: phy suspend register.
+ * @ls_det_en: linestate detection enable register.
+ * @ls_det_st: linestate detection state register.
+ * @ls_det_clr: linestate detection clear register.
+ * @utmi_ls: utmi linestate state register.
+ * @utmi_hstdet: utmi host disconnect register.
+ */
+struct rockchip_usb2phy_port_cfg {
+	struct usb2phy_reg	phy_sus;
+	struct usb2phy_reg	ls_det_en;
+	struct usb2phy_reg	ls_det_st;
+	struct usb2phy_reg	ls_det_clr;
+	struct usb2phy_reg	utmi_ls;
+	struct usb2phy_reg	utmi_hstdet;
+};
+
+/**
+ * struct rockchip_usb2phy_cfg: usb-phy configuration.
+ * @reg: the address offset of grf for usb-phy config.
+ * @num_ports: specify how many ports that the phy has.
+ * @clkout_ctl: keep on/turn off output clk of phy.
+ */
+struct rockchip_usb2phy_cfg {
+	unsigned int	reg;
+	unsigned int	num_ports;
+	struct usb2phy_reg	clkout_ctl;
+	const struct rockchip_usb2phy_port_cfg	port_cfgs[USB2PHY_NUM_PORTS];
+};
+
+/**
+ * struct rockchip_usb2phy_port: usb-phy port data.
+ * @port_id: flag for otg port or host port.
+ * @suspended: phy suspended flag.
+ * @ls_irq: IRQ number assigned for linestate detection.
+ * @mutex: for register updating in sm_work.
+ * @sm_work: OTG state machine work.
+ * @phy_cfg: port register configuration, assigned by driver data.
+ */
+struct rockchip_usb2phy_port {
+	struct phy	*phy;
+	unsigned int	port_id;
+	bool		suspended;
+	int		ls_irq;
+	struct mutex	mutex;
+	struct		delayed_work sm_work;
+	const struct	rockchip_usb2phy_port_cfg *port_cfg;
+};
+
+/**
+ * struct rockchip_usb2phy: usb2.0 phy driver data.
+ * @grf: General Register Files regmap.
+ * @clk: clock struct of phy input clk.
+ * @clk480m: clock struct of phy output clk.
+ * @clk_hw: clock struct of phy output clk management.
+ * @phy_cfg: phy register configuration, assigned by driver data.
+ * @ports: phy port instance.
+ */
+struct rockchip_usb2phy {
+	struct device	*dev;
+	struct regmap	*grf;
+	struct clk	*clk;
+	struct clk	*clk480m;
+	struct clk_hw	clk480m_hw;
+	const struct rockchip_usb2phy_cfg	*phy_cfg;
+	struct rockchip_usb2phy_port	ports[USB2PHY_NUM_PORTS];
+};
+
+static inline int property_enable(struct rockchip_usb2phy *rphy,
+				  const struct usb2phy_reg *reg, bool en)
+{
+	unsigned int val, mask, tmp;
+
+	tmp = en ? reg->enable : reg->disable;
+	mask = GENMASK(reg->bitend, reg->bitstart);
+	val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT);
+
+	return regmap_write(rphy->grf, reg->offset, val);
+}
+
+static inline bool property_enabled(struct rockchip_usb2phy *rphy,
+				    const struct usb2phy_reg *reg)
+{
+	int ret;
+	unsigned int tmp, orig;
+	unsigned int mask = GENMASK(reg->bitend, reg->bitstart);
+
+	ret = regmap_read(rphy->grf, reg->offset, &orig);
+	if (ret)
+		return false;
+
+	tmp = (orig & mask) >> reg->bitstart;
+	return tmp == reg->enable;
+}
+
+static int rockchip_usb2phy_clk480m_enable(struct clk_hw *hw)
+{
+	struct rockchip_usb2phy *rphy =
+		container_of(hw, struct rockchip_usb2phy, clk480m_hw);
+	int ret;
+
+	/* turn on 480m clk output if it is off */
+	if (!property_enabled(rphy, &rphy->phy_cfg->clkout_ctl)) {
+		ret = property_enable(rphy, &rphy->phy_cfg->clkout_ctl, true);
+		if (ret)
+			return ret;
+
+		/* waitting for the clk become stable */
+		mdelay(1);
+	}
+
+	return 0;
+}
+
+static void rockchip_usb2phy_clk480m_disable(struct clk_hw *hw)
+{
+	struct rockchip_usb2phy *rphy =
+		container_of(hw, struct rockchip_usb2phy, clk480m_hw);
+
+	/* turn off 480m clk output */
+	property_enable(rphy, &rphy->phy_cfg->clkout_ctl, false);
+}
+
+static int rockchip_usb2phy_clk480m_enabled(struct clk_hw *hw)
+{
+	struct rockchip_usb2phy *rphy =
+		container_of(hw, struct rockchip_usb2phy, clk480m_hw);
+
+	return property_enabled(rphy, &rphy->phy_cfg->clkout_ctl);
+}
+
+static unsigned long
+rockchip_usb2phy_clk480m_recalc_rate(struct clk_hw *hw,
+				     unsigned long parent_rate)
+{
+	return 480000000;
+}
+
+static const struct clk_ops rockchip_usb2phy_clkout_ops = {
+	.enable = rockchip_usb2phy_clk480m_enable,
+	.disable = rockchip_usb2phy_clk480m_disable,
+	.is_enabled = rockchip_usb2phy_clk480m_enabled,
+	.recalc_rate = rockchip_usb2phy_clk480m_recalc_rate,
+};
+
+static void rockchip_usb2phy_clk480m_unregister(void *data)
+{
+	struct rockchip_usb2phy *rphy = data;
+
+	of_clk_del_provider(rphy->dev->of_node);
+	clk_unregister(rphy->clk480m);
+}
+
+static int
+rockchip_usb2phy_clk480m_register(struct rockchip_usb2phy *rphy)
+{
+	struct device_node *node = rphy->dev->of_node;
+	struct clk_init_data init;
+	const char *clk_name;
+	int ret;
+
+	init.flags = 0;
+	init.name = "clk_usbphy_480m";
+	init.ops = &rockchip_usb2phy_clkout_ops;
+
+	/* optional override of the clockname */
+	of_property_read_string(node, "clock-output-names", &init.name);
+
+	if (rphy->clk) {
+		clk_name = __clk_get_name(rphy->clk);
+		init.parent_names = &clk_name;
+		init.num_parents = 1;
+	} else {
+		init.parent_names = NULL;
+		init.num_parents = 0;
+	}
+
+	rphy->clk480m_hw.init = &init;
+
+	/* register the clock */
+	rphy->clk480m = clk_register(rphy->dev, &rphy->clk480m_hw);
+	if (IS_ERR(rphy->clk480m)) {
+		ret = PTR_ERR(rphy->clk480m);
+		goto err_ret;
+	}
+
+	ret = of_clk_add_provider(node, of_clk_src_simple_get, rphy->clk480m);
+	if (ret < 0)
+		goto err_clk_provider;
+
+	ret = devm_add_action(rphy->dev, rockchip_usb2phy_clk480m_unregister,
+			      rphy);
+	if (ret < 0)
+		goto err_unreg_action;
+
+	return 0;
+
+err_unreg_action:
+	of_clk_del_provider(node);
+err_clk_provider:
+	clk_unregister(rphy->clk480m);
+err_ret:
+	return ret;
+}
+
+static int rockchip_usb2phy_init(struct phy *phy)
+{
+	struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
+	struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent);
+	int ret;
+
+	if (rport->port_id == USB2PHY_PORT_HOST) {
+		/* clear linestate and enable linestate detect irq */
+		mutex_lock(&rport->mutex);
+
+		ret = property_enable(rphy, &rport->port_cfg->ls_det_clr, true);
+		if (ret) {
+			mutex_unlock(&rport->mutex);
+			return ret;
+		}
+
+		ret = property_enable(rphy, &rport->port_cfg->ls_det_en, true);
+		if (ret) {
+			mutex_unlock(&rport->mutex);
+			return ret;
+		}
+
+		mutex_unlock(&rport->mutex);
+		schedule_delayed_work(&rport->sm_work, SCHEDULE_DELAY);
+	}
+
+	return 0;
+}
+
+static int rockchip_usb2phy_power_on(struct phy *phy)
+{
+	struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
+	struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent);
+	int ret;
+
+	dev_dbg(&rport->phy->dev, "port power on\n");
+
+	if (!rport->suspended)
+		return 0;
+
+	ret = clk_prepare_enable(rphy->clk480m);
+	if (ret)
+		return ret;
+
+	ret = property_enable(rphy, &rport->port_cfg->phy_sus, false);
+	if (ret)
+		return ret;
+
+	rport->suspended = false;
+	return 0;
+}
+
+static int rockchip_usb2phy_power_off(struct phy *phy)
+{
+	struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
+	struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent);
+	int ret;
+
+	dev_dbg(&rport->phy->dev, "port power off\n");
+
+	if (rport->suspended)
+		return 0;
+
+	ret = property_enable(rphy, &rport->port_cfg->phy_sus, true);
+	if (ret)
+		return ret;
+
+	rport->suspended = true;
+	clk_disable_unprepare(rphy->clk480m);
+
+	return 0;
+}
+
+static int rockchip_usb2phy_exit(struct phy *phy)
+{
+	struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
+
+	if (rport->port_id == USB2PHY_PORT_HOST)
+		cancel_delayed_work_sync(&rport->sm_work);
+
+	return 0;
+}
+
+static const struct phy_ops rockchip_usb2phy_ops = {
+	.init		= rockchip_usb2phy_init,
+	.exit		= rockchip_usb2phy_exit,
+	.power_on	= rockchip_usb2phy_power_on,
+	.power_off	= rockchip_usb2phy_power_off,
+	.owner		= THIS_MODULE,
+};
+
+/*
+ * The function manage host-phy port state and suspend/resume phy port
+ * to save power.
+ *
+ * we rely on utmi_linestate and utmi_hostdisconnect to identify whether
+ * devices is disconnect or not. Besides, we do not need care it is FS/LS
+ * disconnected or HS disconnected, actually, we just only need get the
+ * device is disconnected at last through rearm the delayed work,
+ * to suspend the phy port in _PHY_STATE_DISCONNECT_ case.
+ *
+ * NOTE: It may invoke *phy_powr_off or *phy_power_on which will invoke
+ * some clk related APIs, so do not invoke it from interrupt context directly.
+ */
+static void rockchip_usb2phy_sm_work(struct work_struct *work)
+{
+	struct rockchip_usb2phy_port *rport =
+		container_of(work, struct rockchip_usb2phy_port, sm_work.work);
+	struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
+	unsigned int sh = rport->port_cfg->utmi_hstdet.bitend -
+			  rport->port_cfg->utmi_hstdet.bitstart + 1;
+	unsigned int ul, uhd, state;
+	unsigned int ul_mask, uhd_mask;
+	int ret;
+
+	mutex_lock(&rport->mutex);
+
+	ret = regmap_read(rphy->grf, rport->port_cfg->utmi_ls.offset, &ul);
+	if (ret < 0)
+		goto next_schedule;
+
+	ret = regmap_read(rphy->grf, rport->port_cfg->utmi_hstdet.offset,
+			  &uhd);
+	if (ret < 0)
+		goto next_schedule;
+
+	uhd_mask = GENMASK(rport->port_cfg->utmi_hstdet.bitend,
+			   rport->port_cfg->utmi_hstdet.bitstart);
+	ul_mask = GENMASK(rport->port_cfg->utmi_ls.bitend,
+			  rport->port_cfg->utmi_ls.bitstart);
+
+	/* stitch on utmi_ls and utmi_hstdet as phy state */
+	state = ((uhd & uhd_mask) >> rport->port_cfg->utmi_hstdet.bitstart) |
+		(((ul & ul_mask) >> rport->port_cfg->utmi_ls.bitstart) << sh);
+
+	switch (state) {
+	case PHY_STATE_HS_ONLINE:
+		dev_dbg(&rport->phy->dev, "HS online\n");
+		break;
+	case PHY_STATE_FS_LS_ONLINE:
+		/*
+		 * For FS/LS device, the online state share with connect state
+		 * from utmi_ls and utmi_hstdet register, so we distinguish
+		 * them via suspended flag.
+		 *
+		 * Plus, there are two cases, one is D- Line pull-up, and D+
+		 * line pull-down, the state is 4; another is D+ line pull-up,
+		 * and D- line pull-down, the state is 2.
+		 */
+		if (!rport->suspended) {
+			/* D- line pull-up, D+ line pull-down */
+			dev_dbg(&rport->phy->dev, "FS/LS online\n");
+			break;
+		}
+		/* fall through */
+	case PHY_STATE_CONNECT:
+		if (rport->suspended) {
+			dev_dbg(&rport->phy->dev, "Connected\n");
+			rockchip_usb2phy_power_on(rport->phy);
+			rport->suspended = false;
+		} else {
+			/* D+ line pull-up, D- line pull-down */
+			dev_dbg(&rport->phy->dev, "FS/LS online\n");
+		}
+		break;
+	case PHY_STATE_DISCONNECT:
+		if (!rport->suspended) {
+			dev_dbg(&rport->phy->dev, "Disconnected\n");
+			rockchip_usb2phy_power_off(rport->phy);
+			rport->suspended = true;
+		}
+
+		/*
+		 * activate the linestate detection to get the next device
+		 * plug-in irq.
+		 */
+		property_enable(rphy, &rport->port_cfg->ls_det_clr, true);
+		property_enable(rphy, &rport->port_cfg->ls_det_en, true);
+
+		/*
+		 * we don't need to rearm the delayed work when the phy port
+		 * is suspended.
+		 */
+		mutex_unlock(&rport->mutex);
+		return;
+	default:
+		dev_dbg(&rport->phy->dev, "unknown phy state\n");
+		break;
+	}
+
+next_schedule:
+	mutex_unlock(&rport->mutex);
+	schedule_delayed_work(&rport->sm_work, SCHEDULE_DELAY);
+}
+
+static irqreturn_t rockchip_usb2phy_linestate_irq(int irq, void *data)
+{
+	struct rockchip_usb2phy_port *rport = data;
+	struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
+
+	if (!property_enabled(rphy, &rport->port_cfg->ls_det_st))
+		return IRQ_NONE;
+
+	mutex_lock(&rport->mutex);
+
+	/* disable linestate detect irq and clear its status */
+	property_enable(rphy, &rport->port_cfg->ls_det_en, false);
+	property_enable(rphy, &rport->port_cfg->ls_det_clr, true);
+
+	mutex_unlock(&rport->mutex);
+
+	/*
+	 * In this case for host phy port, a new device is plugged in,
+	 * meanwhile, if the phy port is suspended, we need rearm the work to
+	 * resume it and mange its states; otherwise, we do nothing about that.
+	 */
+	if (rport->suspended && rport->port_id == USB2PHY_PORT_HOST)
+		rockchip_usb2phy_sm_work(&rport->sm_work.work);
+
+	return IRQ_HANDLED;
+}
+
+static int rockchip_usb2phy_host_port_init(struct rockchip_usb2phy *rphy,
+					   struct rockchip_usb2phy_port *rport,
+					   struct device_node *child_np)
+{
+	int ret;
+
+	rport->port_id = USB2PHY_PORT_HOST;
+	rport->port_cfg = &rphy->phy_cfg->port_cfgs[USB2PHY_PORT_HOST];
+	rport->suspended = true;
+
+	mutex_init(&rport->mutex);
+	INIT_DELAYED_WORK(&rport->sm_work, rockchip_usb2phy_sm_work);
+
+	rport->ls_irq = of_irq_get_byname(child_np, "linestate");
+	if (rport->ls_irq < 0) {
+		dev_err(rphy->dev, "no linestate irq provided\n");
+		return rport->ls_irq;
+	}
+
+	ret = devm_request_threaded_irq(rphy->dev, rport->ls_irq, NULL,
+					rockchip_usb2phy_linestate_irq,
+					IRQF_ONESHOT,
+					"rockchip_usb2phy", rport);
+	if (ret) {
+		dev_err(rphy->dev, "failed to request irq handle\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int rockchip_usb2phy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct device_node *child_np;
+	struct phy_provider *provider;
+	struct rockchip_usb2phy *rphy;
+	const struct rockchip_usb2phy_cfg *phy_cfgs;
+	const struct of_device_id *match;
+	unsigned int reg;
+	int index, ret;
+
+	rphy = devm_kzalloc(dev, sizeof(*rphy), GFP_KERNEL);
+	if (!rphy)
+		return -ENOMEM;
+
+	match = of_match_device(dev->driver->of_match_table, dev);
+	if (!match || !match->data) {
+		dev_err(dev, "phy configs are not assigned!\n");
+		return -EINVAL;
+	}
+
+	if (!dev->parent || !dev->parent->of_node)
+		return -EINVAL;
+
+	rphy->grf = syscon_node_to_regmap(dev->parent->of_node);
+	if (IS_ERR(rphy->grf))
+		return PTR_ERR(rphy->grf);
+
+	if (of_property_read_u32(np, "reg", &reg)) {
+		dev_err(dev, "the reg property is not assigned in %s node\n",
+			np->name);
+		return -EINVAL;
+	}
+
+	rphy->dev = dev;
+	phy_cfgs = match->data;
+	platform_set_drvdata(pdev, rphy);
+
+	/* find out a proper config which can be matched with dt. */
+	index = 0;
+	while (phy_cfgs[index].reg) {
+		if (phy_cfgs[index].reg == reg) {
+			rphy->phy_cfg = &phy_cfgs[index];
+			break;
+		}
+
+		++index;
+	}
+
+	if (!rphy->phy_cfg) {
+		dev_err(dev, "no phy-config can be matched with %s node\n",
+			np->name);
+		return -EINVAL;
+	}
+
+	rphy->clk = of_clk_get_by_name(np, "phyclk");
+	if (!IS_ERR(rphy->clk)) {
+		clk_prepare_enable(rphy->clk);
+	} else {
+		dev_info(&pdev->dev, "no phyclk specified\n");
+		rphy->clk = NULL;
+	}
+
+	ret = rockchip_usb2phy_clk480m_register(rphy);
+	if (ret) {
+		dev_err(dev, "failed to register 480m output clock\n");
+		goto disable_clks;
+	}
+
+	index = 0;
+	for_each_available_child_of_node(np, child_np) {
+		struct rockchip_usb2phy_port *rport = &rphy->ports[index];
+		struct phy *phy;
+
+		/*
+		 * This driver aim to support both otg-port and host-port,
+		 * but unfortunately, the otg part is not ready in current,
+		 * so this comments and below codes are interim, which should
+		 * be changed after otg-port is supplied soon.
+		 */
+		if (of_node_cmp(child_np->name, "host-port"))
+			goto next_child;
+
+		phy = devm_phy_create(dev, child_np, &rockchip_usb2phy_ops);
+		if (IS_ERR(phy)) {
+			dev_err(dev, "failed to create phy\n");
+			ret = PTR_ERR(phy);
+			goto put_child;
+		}
+
+		rport->phy = phy;
+		phy_set_drvdata(rport->phy, rport);
+
+		ret = rockchip_usb2phy_host_port_init(rphy, rport, child_np);
+		if (ret)
+			goto put_child;
+
+next_child:
+		/* to prevent out of boundary */
+		if (++index >= rphy->phy_cfg->num_ports)
+			break;
+	}
+
+	provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+	return PTR_ERR_OR_ZERO(provider);
+
+put_child:
+	of_node_put(child_np);
+disable_clks:
+	if (rphy->clk) {
+		clk_disable_unprepare(rphy->clk);
+		clk_put(rphy->clk);
+	}
+	return ret;
+}
+
+static const struct rockchip_usb2phy_cfg rk3366_phy_cfgs[] = {
+	{
+		.reg = 0x700,
+		.num_ports	= 2,
+		.clkout_ctl	= { 0x0724, 15, 15, 1, 0 },
+		.port_cfgs	= {
+			[USB2PHY_PORT_HOST] = {
+				.phy_sus	= { 0x0728, 15, 0, 0, 0x1d1 },
+				.ls_det_en	= { 0x0680, 4, 4, 0, 1 },
+				.ls_det_st	= { 0x0690, 4, 4, 0, 1 },
+				.ls_det_clr	= { 0x06a0, 4, 4, 0, 1 },
+				.utmi_ls	= { 0x049c, 14, 13, 0, 1 },
+				.utmi_hstdet	= { 0x049c, 12, 12, 0, 1 }
+			}
+		},
+	},
+	{ /* sentinel */ }
+};
+
+static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = {
+	{
+		.reg = 0xe450,
+		.num_ports	= 2,
+		.clkout_ctl	= { 0xe450, 4, 4, 1, 0 },
+		.port_cfgs	= {
+			[USB2PHY_PORT_HOST] = {
+				.phy_sus	= { 0xe458, 1, 0, 0x2, 0x1 },
+				.ls_det_en	= { 0xe3c0, 6, 6, 0, 1 },
+				.ls_det_st	= { 0xe3e0, 6, 6, 0, 1 },
+				.ls_det_clr	= { 0xe3d0, 6, 6, 0, 1 },
+				.utmi_ls	= { 0xe2ac, 22, 21, 0, 1 },
+				.utmi_hstdet	= { 0xe2ac, 23, 23, 0, 1 }
+			}
+		},
+	},
+	{
+		.reg = 0xe460,
+		.num_ports	= 2,
+		.clkout_ctl	= { 0xe460, 4, 4, 1, 0 },
+		.port_cfgs	= {
+			[USB2PHY_PORT_HOST] = {
+				.phy_sus	= { 0xe468, 1, 0, 0x2, 0x1 },
+				.ls_det_en	= { 0xe3c0, 11, 11, 0, 1 },
+				.ls_det_st	= { 0xe3e0, 11, 11, 0, 1 },
+				.ls_det_clr	= { 0xe3d0, 11, 11, 0, 1 },
+				.utmi_ls	= { 0xe2ac, 26, 25, 0, 1 },
+				.utmi_hstdet	= { 0xe2ac, 27, 27, 0, 1 }
+			}
+		},
+	},
+	{ /* sentinel */ }
+};
+
+static const struct of_device_id rockchip_usb2phy_dt_match[] = {
+	{ .compatible = "rockchip,rk3366-usb2phy", .data = &rk3366_phy_cfgs },
+	{ .compatible = "rockchip,rk3399-usb2phy", .data = &rk3399_phy_cfgs },
+	{}
+};
+MODULE_DEVICE_TABLE(of, rockchip_usb2phy_dt_match);
+
+static struct platform_driver rockchip_usb2phy_driver = {
+	.probe		= rockchip_usb2phy_probe,
+	.driver		= {
+		.name	= "rockchip-usb2phy",
+		.of_match_table = rockchip_usb2phy_dt_match,
+	},
+};
+module_platform_driver(rockchip_usb2phy_driver);
+
+MODULE_AUTHOR("Frank Wang <frank.wang at rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip USB2.0 PHY driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.9.5




From frank.wang at rock-chips.com  Fri Jul 22 00:00:45 2016
From: frank.wang at rock-chips.com (Frank Wang)
Date: Fri, 22 Jul 2016 15:00:45 +0800
Subject: [PATCH v9 3/4] arm64: dts: rockchip: add usb2-phy support for rk3399
In-Reply-To: <1469170846-15617-1-git-send-email-frank.wang@rock-chips.com>
References: <1469170846-15617-1-git-send-email-frank.wang@rock-chips.com>
Message-ID: <1469170846-15617-4-git-send-email-frank.wang@rock-chips.com>

Add usb2-phy nodes and specify phys phandle for ehci.

Signed-off-by: Frank Wang <frank.wang at rock-chips.com>
---

Changes in v9:
 - Move the usb gpio config to rk3399-evb.dts
 - Fix ehci phy-names property.

 arch/arm64/boot/dts/rockchip/rk3399.dtsi |   42 +++++++++++++++++++++++++++++-
 1 file changed, 41 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index d7f8e06..843d51c 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -221,6 +221,8 @@
 		interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
 		clocks = <&cru HCLK_HOST0>, <&cru HCLK_HOST0_ARB>;
 		clock-names = "hclk_host0", "hclk_host0_arb";
+		phys = <&u2phy0_host>;
+		phy-names = "usb";
 		status = "disabled";
 	};
 
@@ -239,6 +241,8 @@
 		interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
 		clocks = <&cru HCLK_HOST1>, <&cru HCLK_HOST1_ARB>;
 		clock-names = "hclk_host1", "hclk_host1_arb";
+		phys = <&u2phy1_host>;
+		phy-names = "usb";
 		status = "disabled";
 	};
 
@@ -481,8 +485,44 @@
 	};
 
 	grf: syscon at ff770000 {
-		compatible = "rockchip,rk3399-grf", "syscon";
+		compatible = "rockchip,rk3399-grf", "syscon", "simple-mfd";
 		reg = <0x0 0xff770000 0x0 0x10000>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		u2phy0: usb2-phy at e450 {
+			compatible = "rockchip,rk3399-usb2phy";
+			reg = <0xe450 0x10>;
+			clocks = <&cru SCLK_USB2PHY0_REF>;
+			clock-names = "phyclk";
+			#clock-cells = <0>;
+			clock-output-names = "clk_usbphy0_480m";
+			status = "disabled";
+
+			u2phy0_host: host-port {
+				#phy-cells = <0>;
+				interrupts = <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>;
+				interrupt-names = "linestate";
+				status = "disabled";
+			};
+		};
+
+		u2phy1: usb2-phy at e460 {
+			compatible = "rockchip,rk3399-usb2phy";
+			reg = <0xe460 0x10>;
+			clocks = <&cru SCLK_USB2PHY1_REF>;
+			clock-names = "phyclk";
+			#clock-cells = <0>;
+			clock-output-names = "clk_usbphy1_480m";
+			status = "disabled";
+
+			u2phy1_host: host-port {
+				#phy-cells = <0>;
+				interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
+				interrupt-names = "linestate";
+				status = "disabled";
+			};
+		};
 	};
 
 	watchdog at ff840000 {
-- 
1.7.9.5




From frank.wang at rock-chips.com  Fri Jul 22 00:00:46 2016
From: frank.wang at rock-chips.com (Frank Wang)
Date: Fri, 22 Jul 2016 15:00:46 +0800
Subject: [PATCH v9 4/4] arm64: dts: rockchip: configure usb2-phy support for
 rk3399-evb
In-Reply-To: <1469170846-15617-1-git-send-email-frank.wang@rock-chips.com>
References: <1469170846-15617-1-git-send-email-frank.wang@rock-chips.com>
Message-ID: <1469170846-15617-5-git-send-email-frank.wang@rock-chips.com>

Add vcc5v0_host regulator for usb2-phy and enable host-port support.

Signed-off-by: Frank Wang <frank.wang at rock-chips.com>
---
 arch/arm64/boot/dts/rockchip/rk3399-evb.dts |   44 +++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3399-evb.dts b/arch/arm64/boot/dts/rockchip/rk3399-evb.dts
index 1a3eb14..56aeedb 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-evb.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-evb.dts
@@ -69,6 +69,25 @@
 		regulator-max-microvolt = <3300000>;
 	};
 
+	vcc5v0_sys: vcc5v0-sys {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc5v0_sys";
+		regulator-always-on;
+		regulator-boot-on;
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+	};
+
+	vcc5v0_host: vcc5v0-host-regulator {
+		compatible = "regulator-fixed";
+		enable-active-high;
+		gpio = <&gpio4 25 GPIO_ACTIVE_HIGH>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&host_vbus_drv>;
+		regulator-name = "vcc5v0_host";
+		vin-supply = <&vcc5v0_sys>;
+	};
+
 	vcc_phy: vcc-phy-regulator {
 		compatible = "regulator-fixed";
 		regulator-name = "vcc_phy";
@@ -89,6 +108,24 @@
 	status = "okay";
 };
 
+&u2phy0 {
+	status = "okay";
+
+	u2phy0_host: host-port {
+		phy-supply = <&vcc5v0_host>;
+		status = "okay";
+	};
+};
+
+&u2phy1 {
+	status = "okay";
+
+	u2phy1_host: host-port {
+		phy-supply = <&vcc5v0_host>;
+		status = "okay";
+	};
+};
+
 &uart2 {
 	status = "okay";
 };
@@ -121,4 +158,11 @@
 				<1 18 RK_FUNC_GPIO &pcfg_pull_down>;
 		};
 	};
+
+	usb2 {
+		host_vbus_drv: host-vbus-drv {
+			rockchip,pins =
+				<4 25 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
 };
-- 
1.7.9.5




From hl at rock-chips.com  Fri Jul 22 02:07:13 2016
From: hl at rock-chips.com (Lin Huang)
Date: Fri, 22 Jul 2016 17:07:13 +0800
Subject: [PATCH v3 0/7] rk3399 support ddr frequency scaling 
Message-ID: <1469178440-4668-1-git-send-email-hl@rock-chips.com>

rk3399 platform have dfi controller can monitor ddr load,
and dcf controller to handle ddr register so we can get the
right ddr frequency and make ddr controller happy work(which
will implement in bl31). So we do ddr frequency scaling with
following flow:

	     kernel                                bl31

	monitor ddr load
		|
		|
	get_target_rate
		|
		|           pass rate to bl31
	clk_set_rate(ddr) --------------------->run dcf flow
		|                                   |
		|                                   |
	wait dcf interrupt<-------------------trigger dcf interrupt  
		|
		|
	      return

Lin Huang (6):
  clk: rockchip: add new clock-type for the ddrclk
  clk: rockchip: rk3399: add SCLK_DDRCLK ID for ddrc
  clk: rockchip: rk3399: add ddrc clock support
  PM / devfreq: event: support rockchip dfi controller
  PM / devfreq: rockchip: add devfreq driver for rk3399 dmc
  drm/rockchip: Add dmc notifier in vop driver

Shengfei xu (1):
  firmware: rockchip: sip: Add rockchip SIP runtime service

 drivers/clk/rockchip/Makefile               |   1 +
 drivers/clk/rockchip/clk-ddr.c              | 139 ++++++++
 drivers/clk/rockchip/clk-rk3399.c           |  19 ++
 drivers/clk/rockchip/clk.c                  |   9 +
 drivers/clk/rockchip/clk.h                  |  27 ++
 drivers/devfreq/Kconfig                     |   1 +
 drivers/devfreq/Makefile                    |   1 +
 drivers/devfreq/event/Kconfig               |   7 +
 drivers/devfreq/event/Makefile              |   1 +
 drivers/devfreq/event/rockchip-dfi.c        | 253 +++++++++++++++
 drivers/devfreq/rockchip/Kconfig            |  15 +
 drivers/devfreq/rockchip/Makefile           |   2 +
 drivers/devfreq/rockchip/rk3399_dmc.c       | 482 ++++++++++++++++++++++++++++
 drivers/devfreq/rockchip/rockchip_dmc.c     | 143 +++++++++
 drivers/firmware/Kconfig                    |   7 +
 drivers/firmware/Makefile                   |   1 +
 drivers/firmware/rockchip_sip.c             |  64 ++++
 drivers/firmware/rockchip_sip.h             |  59 ++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c |  75 ++++-
 include/dt-bindings/clock/rk3399-cru.h      |   1 +
 include/soc/rockchip/rockchip_dmc.h         |  45 +++
 21 files changed, 1346 insertions(+), 6 deletions(-)
 create mode 100644 drivers/clk/rockchip/clk-ddr.c
 create mode 100644 drivers/devfreq/event/rockchip-dfi.c
 create mode 100644 drivers/devfreq/rockchip/Kconfig
 create mode 100644 drivers/devfreq/rockchip/Makefile
 create mode 100644 drivers/devfreq/rockchip/rk3399_dmc.c
 create mode 100644 drivers/devfreq/rockchip/rockchip_dmc.c
 create mode 100644 drivers/firmware/rockchip_sip.c
 create mode 100644 drivers/firmware/rockchip_sip.h
 create mode 100644 include/soc/rockchip/rockchip_dmc.h

-- 
2.6.6



From hl at rock-chips.com  Fri Jul 22 02:07:14 2016
From: hl at rock-chips.com (Lin Huang)
Date: Fri, 22 Jul 2016 17:07:14 +0800
Subject: [PATCH v3 1/7] firmware: rockchip: sip: Add rockchip SIP runtime
 service
In-Reply-To: <1469178440-4668-1-git-send-email-hl@rock-chips.com>
References: <1469178440-4668-1-git-send-email-hl@rock-chips.com>
Message-ID: <1469178440-4668-2-git-send-email-hl@rock-chips.com>

From: Shengfei xu <xsf at rock-chips.com>

This patch adds support for the SiP interface, we can pass dram
paramtert to bl31, and control ddr frequency scaling in bl31.

Signed-off-by: Shengfei xu <xsf at rock-chips.com>
Signed-off-by: Lin Huang <hl at rock-chips.com>
---
Changes in v3:
- None

Changes in v2:
- None

Changes in v1:
- None

 drivers/firmware/Kconfig        |  7 +++++
 drivers/firmware/Makefile       |  1 +
 drivers/firmware/rockchip_sip.c | 64 +++++++++++++++++++++++++++++++++++++++++
 drivers/firmware/rockchip_sip.h | 59 +++++++++++++++++++++++++++++++++++++
 4 files changed, 131 insertions(+)
 create mode 100644 drivers/firmware/rockchip_sip.c
 create mode 100644 drivers/firmware/rockchip_sip.h

diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 0e22f24..6f585c5 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -206,6 +206,13 @@ config QCOM_SCM_64
 config HAVE_ARM_SMCCC
 	bool
 
+config ROCKCHIP_SIP
+	bool "Rockchip SIP interface"
+	depends on ARM64 && ARM_PSCI_FW
+	help
+	  Say Y here if you want to enable SIP callbacks for Rockchip platforms
+	  This option enables support for communicating with the ATF.
+
 source "drivers/firmware/broadcom/Kconfig"
 source "drivers/firmware/google/Kconfig"
 source "drivers/firmware/efi/Kconfig"
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index 44a59dc..e9eab5b 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -25,3 +25,4 @@ obj-y				+= broadcom/
 obj-$(CONFIG_GOOGLE_FIRMWARE)	+= google/
 obj-$(CONFIG_EFI)		+= efi/
 obj-$(CONFIG_UEFI_CPER)		+= efi/
+obj-$(CONFIG_ROCKCHIP_SIP)	+= rockchip_sip.o
diff --git a/drivers/firmware/rockchip_sip.c b/drivers/firmware/rockchip_sip.c
new file mode 100644
index 0000000..7756af9
--- /dev/null
+++ b/drivers/firmware/rockchip_sip.c
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ *
+ * Copyright (C) 2016 ARM Limited
+ */
+#include <linux/errno.h>
+#include <linux/linkage.h>
+#include <linux/of.h>
+#include <linux/pm.h>
+#include <linux/printk.h>
+#include "rockchip_sip.h"
+
+typedef unsigned long (psci_fn)(unsigned long, unsigned long,
+				unsigned long, unsigned long);
+asmlinkage psci_fn __invoke_psci_fn_smc;
+
+#define CONFIG_DRAM_INIT	0x00
+#define CONFIG_DRAM_SET_RATE	0x01
+#define CONFIG_DRAM_ROUND_RATE	0x02
+#define CONFIG_DRAM_SET_AT_SR	0x03
+#define CONFIG_DRAM_GET_BW	0x04
+#define CONFIG_DRAM_GET_RATE	0x05
+#define CONFIG_DRAM_CLR_IRQ	0x06
+#define CONFIG_DRAM_SET_PARAM   0x07
+
+uint64_t sip_smc_ddr_init(void)
+{
+	return __invoke_psci_fn_smc(SIP_DDR_FREQ, 0,
+				    0, CONFIG_DRAM_INIT);
+}
+
+uint64_t sip_smc_set_ddr_param(uint64_t param)
+{
+	return __invoke_psci_fn_smc(SIP_DDR_FREQ, param,
+				    0, CONFIG_DRAM_SET_PARAM);
+}
+
+uint64_t sip_smc_set_ddr_rate(uint64_t rate)
+{
+	return __invoke_psci_fn_smc(SIP_DDR_FREQ, rate, 0,
+				    CONFIG_DRAM_SET_RATE);
+}
+
+uint64_t sip_smc_get_ddr_rate(void)
+{
+	return __invoke_psci_fn_smc(SIP_DDR_FREQ, 0, 0, CONFIG_DRAM_GET_RATE);
+}
+
+uint64_t sip_smc_clr_ddr_irq(void)
+{
+	return __invoke_psci_fn_smc(SIP_DDR_FREQ, 0, 0, CONFIG_DRAM_CLR_IRQ);
+}
+
+uint64_t sip_smc_get_call_count(void)
+{
+	return __invoke_psci_fn_smc(SIP_SVC_CALL_COUNT, 0, 0, 0);
+}
diff --git a/drivers/firmware/rockchip_sip.h b/drivers/firmware/rockchip_sip.h
new file mode 100644
index 0000000..6487734
--- /dev/null
+++ b/drivers/firmware/rockchip_sip.h
@@ -0,0 +1,59 @@
+/* Copyright (c) 2010-2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+#ifndef __SIP_INT_H
+#define __SIP_INT_H
+
+/* SMC function IDs for SiP Service queries */
+#define SIP_SVC_CALL_COUNT		0x8200ff00
+#define SIP_SVC_UID			0x8200ff01
+#define SIP_SVC_VERSION			0x8200ff03
+#define SIP_DDR_FREQ			0xC2000008
+
+#if IS_ENABLED(CONFIG_ROCKCHIP_SIP)
+uint64_t sip_smc_set_ddr_rate(uint64_t rate);
+uint64_t sip_smc_get_ddr_rate(void);
+uint64_t sip_smc_clr_ddr_irq(void);
+uint64_t sip_smc_get_call_count(void);
+uint64_t sip_smc_ddr_init(void);
+uint64_t sip_smc_set_ddr_param(uint64_t param);
+#else
+static inline uint64_t sip_smc_set_ddr_rate(uint64_t rate)
+{
+	return 0;
+}
+
+static inline uint64_t sip_smc_get_ddr_rate(void)
+{
+	return 0;
+}
+
+static inline uint64_t sip_smc_clr_ddr_irq(void)
+{
+	return 0;
+}
+
+static inline uint64_t sip_smc_get_call_count(void)
+{
+	return 0;
+}
+
+static inline uint64_t sip_smc_ddr_init(void)
+{
+	return 0;
+}
+
+static inline uint64_t sip_smc_set_ddr_param(uint64_t param)
+{
+	return 0;
+}
+#endif
+#endif
-- 
2.6.6



From hl at rock-chips.com  Fri Jul 22 02:07:16 2016
From: hl at rock-chips.com (Lin Huang)
Date: Fri, 22 Jul 2016 17:07:16 +0800
Subject: [PATCH v3 3/7] clk: rockchip: rk3399: add SCLK_DDRCLK ID for ddrc
In-Reply-To: <1469178440-4668-1-git-send-email-hl@rock-chips.com>
References: <1469178440-4668-1-git-send-email-hl@rock-chips.com>
Message-ID: <1469178440-4668-4-git-send-email-hl@rock-chips.com>

Signed-off-by: Lin Huang <hl at rock-chips.com>
---
Changes in v3:
-None

Changes in v2:
- None 
Changes in v1:
- None

 include/dt-bindings/clock/rk3399-cru.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/include/dt-bindings/clock/rk3399-cru.h b/include/dt-bindings/clock/rk3399-cru.h
index 50a44cf..8a0f0442 100644
--- a/include/dt-bindings/clock/rk3399-cru.h
+++ b/include/dt-bindings/clock/rk3399-cru.h
@@ -131,6 +131,7 @@
 #define SCLK_DPHY_RX0_CFG		165
 #define SCLK_RMII_SRC			166
 #define SCLK_PCIEPHY_REF100M		167
+#define SCLK_DDRCLK			168
 
 #define DCLK_VOP0			180
 #define DCLK_VOP1			181
-- 
2.6.6



From hl at rock-chips.com  Fri Jul 22 02:07:15 2016
From: hl at rock-chips.com (Lin Huang)
Date: Fri, 22 Jul 2016 17:07:15 +0800
Subject: [PATCH v3 2/7] clk: rockchip: add new clock-type for the ddrclk
In-Reply-To: <1469178440-4668-1-git-send-email-hl@rock-chips.com>
References: <1469178440-4668-1-git-send-email-hl@rock-chips.com>
Message-ID: <1469178440-4668-3-git-send-email-hl@rock-chips.com>

On new rockchip platform(rk3399 etc), there have dcf controller to
do ddr frequency scaling, and this controller will implement in
arm-trust-firmware. We add a special clock-type to handle that.

Signed-off-by: Lin Huang <hl at rock-chips.com>
---
Changes in v3:
- use sip call to set/read ddr rate

Changes in v2:
- use GENMASK instead val_mask
- use divider_recalc_rate() instead DIV_ROUND_UP_ULL
- cleanup code

Changes in v1:
- None

 drivers/clk/rockchip/Makefile  |   1 +
 drivers/clk/rockchip/clk-ddr.c | 139 +++++++++++++++++++++++++++++++++++++++++
 drivers/clk/rockchip/clk.c     |   9 +++
 drivers/clk/rockchip/clk.h     |  27 ++++++++
 4 files changed, 176 insertions(+)
 create mode 100644 drivers/clk/rockchip/clk-ddr.c

diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile
index f47a2fa..b5f2c8e 100644
--- a/drivers/clk/rockchip/Makefile
+++ b/drivers/clk/rockchip/Makefile
@@ -8,6 +8,7 @@ obj-y	+= clk-pll.o
 obj-y	+= clk-cpu.o
 obj-y	+= clk-inverter.o
 obj-y	+= clk-mmc-phase.o
+obj-y	+= clk-ddr.o
 obj-$(CONFIG_RESET_CONTROLLER)	+= softrst.o
 
 obj-y	+= clk-rk3036.o
diff --git a/drivers/clk/rockchip/clk-ddr.c b/drivers/clk/rockchip/clk-ddr.c
new file mode 100644
index 0000000..6f5f05e
--- /dev/null
+++ b/drivers/clk/rockchip/clk-ddr.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2016 Rockchip Electronics Co. Ltd.
+ * Author: Lin Huang <hl at rock-chips.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+
+#include "../../firmware/rockchip_sip.h"
+#include "clk.h"
+
+struct rockchip_ddrclk {
+	struct clk_hw	hw;
+	void __iomem	*reg_base;
+	int		mux_offset;
+	int		mux_shift;
+	int		mux_width;
+	int		mux_flag;
+	int		div_shift;
+	int		div_width;
+	int		div_flag;
+	spinlock_t	*lock;
+};
+
+#define to_rockchip_ddrclk_hw(hw) container_of(hw, struct rockchip_ddrclk, hw)
+
+static int rockchip_ddrclk_set_rate(struct clk_hw *hw, unsigned long drate,
+				    unsigned long prate)
+{
+	u64 value;
+	struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw);
+	unsigned long flags;
+
+	spin_lock_irqsave(ddrclk->lock, flags);
+	value = sip_smc_set_ddr_rate(drate);
+	spin_unlock_irqrestore(ddrclk->lock, flags);
+
+	return 0;
+}
+
+static unsigned long
+rockchip_ddrclk_recalc_rate(struct clk_hw *hw,
+			    unsigned long parent_rate)
+{
+	return sip_smc_get_ddr_rate();
+}
+
+static long clk_ddrclk_round_rate(struct clk_hw *hw, unsigned long rate,
+				  unsigned long *prate)
+{
+	return rate;
+}
+
+static u8 rockchip_ddrclk_get_parent(struct clk_hw *hw)
+{
+	struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw);
+	int num_parents = clk_hw_get_num_parents(hw);
+	u32 val;
+
+	val = clk_readl(ddrclk->reg_base +
+			ddrclk->mux_offset) >> ddrclk->mux_shift;
+	val &= GENMASK(ddrclk->mux_width - 1, 0);
+
+	if (val >= num_parents)
+		return -EINVAL;
+
+	return val;
+}
+
+static const struct clk_ops rockchip_ddrclk_ops = {
+	.recalc_rate = rockchip_ddrclk_recalc_rate,
+	.set_rate = rockchip_ddrclk_set_rate,
+	.round_rate = clk_ddrclk_round_rate,
+	.get_parent = rockchip_ddrclk_get_parent,
+};
+
+struct clk *rockchip_clk_register_ddrclk(const char *name, int flags,
+					 const char *const *parent_names,
+					 u8 num_parents, int mux_offset,
+					 int mux_shift, int mux_width,
+					 int mux_flag, int div_shift,
+					 int div_width, int div_flag,
+					 void __iomem *reg_base,
+					 spinlock_t *lock)
+{
+	struct rockchip_ddrclk *ddrclk;
+	struct clk_init_data init;
+	struct clk *clk;
+
+	ddrclk = kzalloc(sizeof(*ddrclk), GFP_KERNEL);
+	if (!ddrclk)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	init.parent_names = parent_names;
+	init.num_parents = num_parents;
+	init.ops = &rockchip_ddrclk_ops;
+
+	init.flags = flags;
+	init.flags |= CLK_SET_RATE_NO_REPARENT;
+	init.flags |= CLK_GET_RATE_NOCACHE;
+
+	ddrclk->reg_base = reg_base;
+	ddrclk->lock = lock;
+	ddrclk->hw.init = &init;
+	ddrclk->mux_offset = mux_offset;
+	ddrclk->mux_shift = mux_shift;
+	ddrclk->mux_width = mux_width;
+	ddrclk->mux_flag = mux_flag;
+	ddrclk->div_shift = div_shift;
+	ddrclk->div_width = div_width;
+	ddrclk->div_flag = div_flag;
+
+	clk = clk_register(NULL, &ddrclk->hw);
+	if (IS_ERR(clk)) {
+		pr_err("%s: could not register ddrclk %s\n", __func__,	name);
+		goto free_ddrclk;
+	}
+
+	return clk;
+
+free_ddrclk:
+	kfree(ddrclk);
+
+	return NULL;
+}
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
index 7ffd134..6ac1aa5 100644
--- a/drivers/clk/rockchip/clk.c
+++ b/drivers/clk/rockchip/clk.c
@@ -484,6 +484,15 @@ void __init rockchip_clk_register_branches(
 				list->gate_offset, list->gate_shift,
 				list->gate_flags, flags, &ctx->lock);
 			break;
+		case branch_ddrc:
+			clk = rockchip_clk_register_ddrclk(
+				list->name, list->flags,
+				list->parent_names, list->num_parents,
+				list->muxdiv_offset, list->mux_shift,
+				list->mux_width, list->mux_flags,
+				list->div_shift, list->div_width,
+				list->div_flags, ctx->reg_base, &ctx->lock);
+			break;
 		}
 
 		/* none of the cases above matched */
diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h
index 2194ffa..4033fe4 100644
--- a/drivers/clk/rockchip/clk.h
+++ b/drivers/clk/rockchip/clk.h
@@ -281,6 +281,13 @@ struct clk *rockchip_clk_register_mmc(const char *name,
 				const char *const *parent_names, u8 num_parents,
 				void __iomem *reg, int shift);
 
+struct clk *rockchip_clk_register_ddrclk(const char *name, int flags,
+			const char *const *parent_names, u8 num_parents,
+			int mux_offset, int mux_shift, int mux_width,
+			int mux_flag, int div_shift, int div_width,
+			int div_flag, void __iomem *reg_base,
+			spinlock_t *lock);
+
 #define ROCKCHIP_INVERTER_HIWORD_MASK	BIT(0)
 
 struct clk *rockchip_clk_register_inverter(const char *name,
@@ -299,6 +306,7 @@ enum rockchip_clk_branch_type {
 	branch_mmc,
 	branch_inverter,
 	branch_factor,
+	branch_ddrc,
 };
 
 struct rockchip_clk_branch {
@@ -488,6 +496,25 @@ struct rockchip_clk_branch {
 		.child		= ch,				\
 	}
 
+#define COMPOSITE_DDRC(_id, cname, pnames, f, mo, ms, mw, mf,	\
+			 ds, dw, df)				\
+	{							\
+		.id		= _id,				\
+		.branch_type	= branch_ddrc,			\
+		.name		= cname,			\
+		.parent_names	= pnames,			\
+		.num_parents	= ARRAY_SIZE(pnames),		\
+		.flags		= f,				\
+		.muxdiv_offset	= mo,				\
+		.mux_shift	= ms,				\
+		.mux_width	= mw,				\
+		.mux_flags	= mf,				\
+		.div_shift	= ds,				\
+		.div_width	= dw,				\
+		.div_flags	= df,				\
+		.gate_offset	= -1,				\
+	}
+
 #define MUX(_id, cname, pnames, f, o, s, w, mf)			\
 	{							\
 		.id		= _id,				\
-- 
2.6.6



From hl at rock-chips.com  Fri Jul 22 02:07:17 2016
From: hl at rock-chips.com (Lin Huang)
Date: Fri, 22 Jul 2016 17:07:17 +0800
Subject: [PATCH v3 4/7] clk: rockchip: rk3399: add ddrc clock support
In-Reply-To: <1469178440-4668-1-git-send-email-hl@rock-chips.com>
References: <1469178440-4668-1-git-send-email-hl@rock-chips.com>
Message-ID: <1469178440-4668-5-git-send-email-hl@rock-chips.com>

add ddrc clock setting, so we can do ddr frequency
scaling on rk3399 platform in future.

Signed-off-by: Lin Huang <hl at rock-chips.com>
---
Changes in v3:
- None

Changes in v2:
- remove clk_ddrc_dpll_src from critical clock list

Changes in v1:
- remove ddrc source CLK_IGNORE_UNUSED flag
- move clk_ddrc and clk_ddrc_dpll_src to critical

 drivers/clk/rockchip/clk-rk3399.c | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c
index c109d80..7ef9d82 100644
--- a/drivers/clk/rockchip/clk-rk3399.c
+++ b/drivers/clk/rockchip/clk-rk3399.c
@@ -118,6 +118,10 @@ PNAME(mux_armclkb_p)				= { "clk_core_b_lpll_src",
 						    "clk_core_b_bpll_src",
 						    "clk_core_b_dpll_src",
 						    "clk_core_b_gpll_src" };
+PNAME(mux_ddrclk_p)				= { "clk_ddrc_lpll_src",
+						    "clk_ddrc_bpll_src",
+						    "clk_ddrc_dpll_src",
+						    "clk_ddrc_gpll_src" };
 PNAME(mux_aclk_cci_p)				= { "cpll_aclk_cci_src",
 						    "gpll_aclk_cci_src",
 						    "npll_aclk_cci_src",
@@ -1377,6 +1381,18 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = {
 	COMPOSITE_NOMUX(0, "clk_test", "clk_test_pre", CLK_IGNORE_UNUSED,
 			RK3368_CLKSEL_CON(58), 0, 5, DFLAGS,
 			RK3368_CLKGATE_CON(13), 11, GFLAGS),
+
+	/* ddrc */
+	GATE(0, "clk_ddrc_lpll_src", "lpll", 0, RK3399_CLKGATE_CON(3),
+	     0, GFLAGS),
+	GATE(0, "clk_ddrc_bpll_src", "bpll", 0, RK3399_CLKGATE_CON(3),
+	     1, GFLAGS),
+	GATE(0, "clk_ddrc_dpll_src", "dpll", 0, RK3399_CLKGATE_CON(3),
+	     2, GFLAGS),
+	GATE(0, "clk_ddrc_gpll_src", "gpll", 0, RK3399_CLKGATE_CON(3),
+	     3, GFLAGS),
+	COMPOSITE_DDRC(SCLK_DDRCLK, "clk_ddrc", mux_ddrclk_p, 0,
+		       RK3399_CLKSEL_CON(6), 4, 2, MFLAGS, 0, 3, DFLAGS),
 };
 
 static struct rockchip_clk_branch rk3399_clk_pmu_branches[] __initdata = {
@@ -1487,6 +1503,9 @@ static const char *const rk3399_cru_critical_clocks[] __initconst = {
 	"gpll_hclk_perilp1_src",
 	"gpll_aclk_perilp0_src",
 	"gpll_aclk_perihp_src",
+
+	/* ddrc */
+	"clk_ddrc"
 };
 
 static const char *const rk3399_pmucru_critical_clocks[] __initconst = {
-- 
2.6.6



From hl at rock-chips.com  Fri Jul 22 02:07:18 2016
From: hl at rock-chips.com (Lin Huang)
Date: Fri, 22 Jul 2016 17:07:18 +0800
Subject: [PATCH v3 5/7] PM / devfreq: event: support rockchip dfi controller
In-Reply-To: <1469178440-4668-1-git-send-email-hl@rock-chips.com>
References: <1469178440-4668-1-git-send-email-hl@rock-chips.com>
Message-ID: <1469178440-4668-6-git-send-email-hl@rock-chips.com>

on rk3399 platform, there is dfi conroller can monitor
ddr load, base on this result, we can do ddr freqency
scaling.

Signed-off-by: Lin Huang <hl at rock-chips.com>
Acked-by: Chanwoo Choi <cw00.choi at samsung.com>
---
Changes in v3:
- None

Changes in v2:
- use clk_disable_unprepare and clk_enable_prepare
- remove clk_enable_prepare in probe
- remove rockchip_dfi_remove function

Changes in v1:
- None

 drivers/devfreq/event/Kconfig        |   7 +
 drivers/devfreq/event/Makefile       |   1 +
 drivers/devfreq/event/rockchip-dfi.c | 253 +++++++++++++++++++++++++++++++++++
 3 files changed, 261 insertions(+)
 create mode 100644 drivers/devfreq/event/rockchip-dfi.c

diff --git a/drivers/devfreq/event/Kconfig b/drivers/devfreq/event/Kconfig
index eb6f74a..0d32d68 100644
--- a/drivers/devfreq/event/Kconfig
+++ b/drivers/devfreq/event/Kconfig
@@ -30,4 +30,11 @@ config DEVFREQ_EVENT_EXYNOS_PPMU
 	  (Platform Performance Monitoring Unit) counters to estimate the
 	  utilization of each module.
 
+config DEVFREQ_EVENT_ROCKCHIP_DFI
+	bool "ROCKCHIP DFI DEVFREQ event Driver"
+	depends on ARCH_ROCKCHIP
+	help
+	  This add the devfreq-event driver for Rockchip SoC. It provides DFI
+	  (DDR Monitor Module) driver to count ddr load.
+
 endif # PM_DEVFREQ_EVENT
diff --git a/drivers/devfreq/event/Makefile b/drivers/devfreq/event/Makefile
index 3d6afd3..dda7090 100644
--- a/drivers/devfreq/event/Makefile
+++ b/drivers/devfreq/event/Makefile
@@ -2,3 +2,4 @@
 
 obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_NOCP) += exynos-nocp.o
 obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_PPMU) += exynos-ppmu.o
+obj-$(CONFIG_DEVFREQ_EVENT_ROCKCHIP_DFI) += rockchip-dfi.o
diff --git a/drivers/devfreq/event/rockchip-dfi.c b/drivers/devfreq/event/rockchip-dfi.c
new file mode 100644
index 0000000..96a0307
--- /dev/null
+++ b/drivers/devfreq/event/rockchip-dfi.c
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
+ * Author: Lin Huang <hl at rock-chips.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/clk.h>
+#include <linux/devfreq-event.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/of.h>
+
+#define RK3399_DMC_NUM_CH	2
+
+/* DDRMON_CTRL */
+#define DDRMON_CTRL	0x04
+#define CLR_DDRMON_CTRL	(0x1f0000 << 0)
+#define LPDDR4_EN	(0x10001 << 4)
+#define HARDWARE_EN	(0x10001 << 3)
+#define LPDDR3_EN	(0x10001 << 2)
+#define SOFTWARE_EN	(0x10001 << 1)
+#define TIME_CNT_EN	(0x10001 << 0)
+
+#define DDRMON_CH0_COUNT_NUM		0x28
+#define DDRMON_CH0_DFI_ACCESS_NUM	0x2c
+#define DDRMON_CH1_COUNT_NUM		0x3c
+#define DDRMON_CH1_DFI_ACCESS_NUM	0x40
+
+/* pmu grf */
+#define PMUGRF_OS_REG2	0x308
+#define DDRTYPE_SHIFT	13
+#define DDRTYPE_MASK	7
+
+enum {
+	DDR3 = 3,
+	LPDDR3 = 6,
+	LPDDR4 = 7,
+	UNUSED = 0xFF
+};
+
+struct dmc_usage {
+	u32 access;
+	u32 total;
+};
+
+struct rockchip_dfi {
+	struct devfreq_event_dev *edev;
+	struct devfreq_event_desc *desc;
+	struct dmc_usage ch_usage[RK3399_DMC_NUM_CH];
+	struct device *dev;
+	void __iomem *regs;
+	struct regmap *regmap_pmu;
+	struct clk *clk;
+};
+
+static void rockchip_dfi_start_hardware_counter(struct devfreq_event_dev *edev)
+{
+	struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
+	void __iomem *dfi_regs = info->regs;
+	u32 val;
+	u32 ddr_type;
+
+	/* get ddr type */
+	regmap_read(info->regmap_pmu, PMUGRF_OS_REG2, &val);
+	ddr_type = (val >> DDRTYPE_SHIFT) & DDRTYPE_MASK;
+
+	/* clear DDRMON_CTRL setting */
+	writel_relaxed(CLR_DDRMON_CTRL, dfi_regs + DDRMON_CTRL);
+
+	/* set ddr type to dfi */
+	if (ddr_type == LPDDR3)
+		writel_relaxed(LPDDR3_EN, dfi_regs + DDRMON_CTRL);
+	else if (ddr_type == LPDDR4)
+		writel_relaxed(LPDDR4_EN, dfi_regs + DDRMON_CTRL);
+
+	/* enable count, use software mode */
+	writel_relaxed(SOFTWARE_EN, dfi_regs + DDRMON_CTRL);
+}
+
+static void rockchip_dfi_stop_hardware_counter(struct devfreq_event_dev *edev)
+{
+	struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
+	void __iomem *dfi_regs = info->regs;
+	u32 val;
+
+	val = readl_relaxed(dfi_regs + DDRMON_CTRL);
+	val &= ~SOFTWARE_EN;
+	writel_relaxed(val, dfi_regs + DDRMON_CTRL);
+}
+
+static int rockchip_dfi_get_busier_ch(struct devfreq_event_dev *edev)
+{
+	struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
+	u32 tmp, max = 0;
+	u32 i, busier_ch = 0;
+	void __iomem *dfi_regs = info->regs;
+
+	rockchip_dfi_stop_hardware_counter(edev);
+
+	/* Find out which channel is busier */
+	for (i = 0; i < RK3399_DMC_NUM_CH; i++) {
+		info->ch_usage[i].access = readl_relaxed(dfi_regs +
+				DDRMON_CH0_DFI_ACCESS_NUM + i * 20);
+		info->ch_usage[i].total = readl_relaxed(dfi_regs +
+				DDRMON_CH0_COUNT_NUM + i * 20);
+		tmp = info->ch_usage[i].access;
+		if (tmp > max) {
+			busier_ch = i;
+			max = tmp;
+		}
+	}
+	rockchip_dfi_start_hardware_counter(edev);
+
+	return busier_ch;
+}
+
+static int rockchip_dfi_disable(struct devfreq_event_dev *edev)
+{
+	struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
+
+	rockchip_dfi_stop_hardware_counter(edev);
+	clk_disable_unprepare(info->clk);
+
+	return 0;
+}
+
+static int rockchip_dfi_enable(struct devfreq_event_dev *edev)
+{
+	struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
+	int ret;
+
+	ret = clk_prepare_enable(info->clk);
+	if (ret) {
+		dev_err(&edev->dev, "failed to enable dfi clk: %d\n", ret);
+		return ret;
+	}
+
+	rockchip_dfi_start_hardware_counter(edev);
+	return 0;
+}
+
+static int rockchip_dfi_set_event(struct devfreq_event_dev *edev)
+{
+	return 0;
+}
+
+static int rockchip_dfi_get_event(struct devfreq_event_dev *edev,
+				  struct devfreq_event_data *edata)
+{
+	struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
+	int busier_ch;
+
+	busier_ch = rockchip_dfi_get_busier_ch(edev);
+
+	edata->load_count = info->ch_usage[busier_ch].access;
+	edata->total_count = info->ch_usage[busier_ch].total;
+
+	return 0;
+}
+
+static const struct devfreq_event_ops rockchip_dfi_ops = {
+	.disable = rockchip_dfi_disable,
+	.enable = rockchip_dfi_enable,
+	.get_event = rockchip_dfi_get_event,
+	.set_event = rockchip_dfi_set_event,
+};
+
+static const struct of_device_id rockchip_dfi_id_match[] = {
+	{ .compatible = "rockchip,rk3399-dfi" },
+	{ },
+};
+
+static int rockchip_dfi_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct rockchip_dfi *data;
+	struct resource *res;
+	struct devfreq_event_desc *desc;
+	struct device_node *np = pdev->dev.of_node, *node;
+
+	data = devm_kzalloc(dev, sizeof(struct rockchip_dfi), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	data->regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(data->regs))
+		return PTR_ERR(data->regs);
+
+	data->clk = devm_clk_get(dev, "pclk_ddr_mon");
+	if (IS_ERR(data->clk)) {
+		dev_err(dev, "Cannot get the clk dmc_clk\n");
+		return PTR_ERR(data->clk);
+	};
+
+	/* try to find the optional reference to the pmu syscon */
+	node = of_parse_phandle(np, "rockchip,pmu", 0);
+	if (node) {
+		data->regmap_pmu = syscon_node_to_regmap(node);
+		if (IS_ERR(data->regmap_pmu))
+			return PTR_ERR(data->regmap_pmu);
+	}
+	data->dev = dev;
+
+	desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
+	if (!desc)
+		return -ENOMEM;
+
+	desc->ops = &rockchip_dfi_ops;
+	desc->driver_data = data;
+	desc->name = np->name;
+	data->desc = desc;
+
+	data->edev = devm_devfreq_event_add_edev(&pdev->dev, desc);
+	if (IS_ERR(data->edev)) {
+		dev_err(&pdev->dev,
+			"failed to add devfreq-event device\n");
+		return PTR_ERR(data->edev);
+	}
+
+	platform_set_drvdata(pdev, data);
+
+	return 0;
+}
+
+static struct platform_driver rockchip_dfi_driver = {
+	.probe	= rockchip_dfi_probe,
+	.driver = {
+		.name	= "rockchip-dfi",
+		.of_match_table = rockchip_dfi_id_match,
+	},
+};
+module_platform_driver(rockchip_dfi_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Lin Huang <hl at rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip dfi driver");
-- 
2.6.6



From hl at rock-chips.com  Fri Jul 22 02:07:19 2016
From: hl at rock-chips.com (Lin Huang)
Date: Fri, 22 Jul 2016 17:07:19 +0800
Subject: [PATCH v3 6/7] PM / devfreq: rockchip: add devfreq driver for rk3399
 dmc
In-Reply-To: <1469178440-4668-1-git-send-email-hl@rock-chips.com>
References: <1469178440-4668-1-git-send-email-hl@rock-chips.com>
Message-ID: <1469178440-4668-7-git-send-email-hl@rock-chips.com>

base on dfi result, we do ddr frequency scaling, register
dmc driver to devfreq framework, and use simple-ondemand
policy.

Signed-off-by: Lin Huang <hl at rock-chips.com>
---
Changes in v3:
- operate dram setting through sip call
- imporve set rate flow

Changes in v2:
- None
 
Changes in v1:
- move dfi controller to event
- fix set voltage sequence when set rate fail
- change Kconfig type from tristate to bool
- move unuse EXPORT_SYMBOL_GPL()

 drivers/devfreq/Kconfig                 |   1 +
 drivers/devfreq/Makefile                |   1 +
 drivers/devfreq/rockchip/Kconfig        |  15 +
 drivers/devfreq/rockchip/Makefile       |   2 +
 drivers/devfreq/rockchip/rk3399_dmc.c   | 482 ++++++++++++++++++++++++++++++++
 drivers/devfreq/rockchip/rockchip_dmc.c | 143 ++++++++++
 include/soc/rockchip/rockchip_dmc.h     |  45 +++
 7 files changed, 689 insertions(+)
 create mode 100644 drivers/devfreq/rockchip/Kconfig
 create mode 100644 drivers/devfreq/rockchip/Makefile
 create mode 100644 drivers/devfreq/rockchip/rk3399_dmc.c
 create mode 100644 drivers/devfreq/rockchip/rockchip_dmc.c
 create mode 100644 include/soc/rockchip/rockchip_dmc.h

diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig
index a5be56e..cb67246 100644
--- a/drivers/devfreq/Kconfig
+++ b/drivers/devfreq/Kconfig
@@ -101,5 +101,6 @@ config ARM_TEGRA_DEVFREQ
          operating frequencies and voltages with OPP support.
 
 source "drivers/devfreq/event/Kconfig"
+source "drivers/devfreq/rockchip/Kconfig"
 
 endif # PM_DEVFREQ
diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile
index 09f11d9..48e2ae6 100644
--- a/drivers/devfreq/Makefile
+++ b/drivers/devfreq/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_DEVFREQ_GOV_PASSIVE)	+= governor_passive.o
 # DEVFREQ Drivers
 obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ)	+= exynos-bus.o
 obj-$(CONFIG_ARM_TEGRA_DEVFREQ)		+= tegra-devfreq.o
+obj-$(CONFIG_ARCH_ROCKCHIP)		+= rockchip/
 
 # DEVFREQ Event Drivers
 obj-$(CONFIG_PM_DEVFREQ_EVENT)		+= event/
diff --git a/drivers/devfreq/rockchip/Kconfig b/drivers/devfreq/rockchip/Kconfig
new file mode 100644
index 0000000..7fb1cff
--- /dev/null
+++ b/drivers/devfreq/rockchip/Kconfig
@@ -0,0 +1,15 @@
+config ARM_ROCKCHIP_DMC_DEVFREQ
+	bool "ARM ROCKCHIP DMC DEVFREQ Driver"
+	depends on ARCH_ROCKCHIP
+	help
+	  This adds the DEVFREQ driver framework for the rockchip dmc.
+
+config ARM_RK3399_DMC_DEVFREQ
+	bool "ARM RK3399 DMC DEVFREQ Driver"
+	depends on ARM_ROCKCHIP_DMC_DEVFREQ
+	select PM_OPP
+	select DEVFREQ_GOV_SIMPLE_ONDEMAND
+	help
+          This adds the DEVFREQ driver for the RK3399 dmc. It sets the frequency
+          for the memory controller and reads the usage counts from hardware.
+
diff --git a/drivers/devfreq/rockchip/Makefile b/drivers/devfreq/rockchip/Makefile
new file mode 100644
index 0000000..caca525
--- /dev/null
+++ b/drivers/devfreq/rockchip/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_ARM_ROCKCHIP_DMC_DEVFREQ)	+= rockchip_dmc.o
+obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ)	+= rk3399_dmc.o
diff --git a/drivers/devfreq/rockchip/rk3399_dmc.c b/drivers/devfreq/rockchip/rk3399_dmc.c
new file mode 100644
index 0000000..f1d6120
--- /dev/null
+++ b/drivers/devfreq/rockchip/rk3399_dmc.c
@@ -0,0 +1,482 @@
+/*
+ * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
+ * Author: Lin Huang <hl at rock-chips.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/devfreq.h>
+#include <linux/devfreq-event.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
+#include <linux/regulator/consumer.h>
+#include <linux/rwsem.h>
+#include <linux/suspend.h>
+#include <linux/syscore_ops.h>
+
+#include <soc/rockchip/rockchip_dmc.h>
+#include "../../firmware/rockchip_sip.h"
+
+struct dram_timing {
+	unsigned int ddr3_speed_bin;
+	unsigned int pd_idle;
+	unsigned int sr_idle;
+	unsigned int sr_mc_gate_idle;
+	unsigned int srpd_lite_idle;
+	unsigned int standby_idle;
+	unsigned int dram_dll_dis_freq;
+	unsigned int phy_dll_dis_freq;
+	unsigned int ddr3_odt_dis_freq;
+	unsigned int ddr3_drv;
+	unsigned int ddr3_odt;
+	unsigned int phy_ddr3_ca_drv;
+	unsigned int phy_ddr3_dq_drv;
+	unsigned int phy_ddr3_odt;
+	unsigned int lpddr3_odt_dis_freq;
+	unsigned int lpddr3_drv;
+	unsigned int lpddr3_odt;
+	unsigned int phy_lpddr3_ca_drv;
+	unsigned int phy_lpddr3_dq_drv;
+	unsigned int phy_lpddr3_odt;
+	unsigned int lpddr4_odt_dis_freq;
+	unsigned int lpddr4_drv;
+	unsigned int lpddr4_dq_odt;
+	unsigned int lpddr4_ca_odt;
+	unsigned int phy_lpddr4_ca_drv;
+	unsigned int phy_lpddr4_ck_cs_drv;
+	unsigned int phy_lpddr4_dq_drv;
+	unsigned int phy_lpddr4_odt;
+};
+
+struct rk3399_dmcfreq {
+	struct device *dev;
+	struct devfreq *devfreq;
+	struct devfreq_simple_ondemand_data ondemand_data;
+	struct clk *dmc_clk;
+	struct devfreq_event_dev *edev;
+	struct mutex lock;
+	struct notifier_block dmc_nb;
+	struct dram_timing *timing;
+	wait_queue_head_t	wait_dcf_queue;
+	int irq;
+	int wait_dcf_flag;
+	struct regulator *vdd_center;
+	unsigned long rate, target_rate;
+	unsigned long volt, target_volt;
+};
+
+static int rk3399_dmcfreq_target(struct device *dev, unsigned long *freq,
+				 u32 flags)
+{
+	struct platform_device *pdev = container_of(dev, struct platform_device,
+						    dev);
+	struct rk3399_dmcfreq *dmcfreq = platform_get_drvdata(pdev);
+	struct dev_pm_opp *opp;
+	unsigned long old_clk_rate = dmcfreq->rate;
+	unsigned long target_volt, target_rate;
+	int err;
+
+	rcu_read_lock();
+	opp = devfreq_recommended_opp(dev, freq, flags);
+	if (IS_ERR(opp)) {
+		rcu_read_unlock();
+		return PTR_ERR(opp);
+	}
+	target_rate = dev_pm_opp_get_freq(opp);
+	target_volt = dev_pm_opp_get_voltage(opp);
+	opp = devfreq_recommended_opp(dev, &dmcfreq->rate, flags);
+	if (IS_ERR(opp)) {
+		rcu_read_unlock();
+		return PTR_ERR(opp);
+	}
+	dmcfreq->volt = dev_pm_opp_get_voltage(opp);
+	rcu_read_unlock();
+
+	if (dmcfreq->rate == target_rate)
+		return 0;
+
+	mutex_lock(&dmcfreq->lock);
+	/*
+	 * if frequency scaling from low to high, adjust voltage first;
+	 * if frequency scaling from high to low, adjuset frequency first;
+	 */
+	if (old_clk_rate < target_rate) {
+		err = regulator_set_voltage(dmcfreq->vdd_center, target_volt,
+					    target_volt);
+		if (err) {
+			dev_err(dev, "Unable to set vol %lu\n", target_volt);
+			goto out;
+		}
+	}
+
+	dmc_event(DMCFREQ_ADJUST);
+	dmcfreq->wait_dcf_flag = 1;
+
+	err = clk_set_rate(dmcfreq->dmc_clk, target_rate);
+	if (err) {
+		dev_err(dev,
+			"Unable to set freq %lu. Current freq %lu. Error %d\n",
+			target_rate, old_clk_rate, err);
+		regulator_set_voltage(dmcfreq->vdd_center, dmcfreq->volt,
+				      dmcfreq->volt);
+		dmc_event(DMCFREQ_FINISH);
+		goto out;
+	}
+
+	/*
+	 * wait until bcf irq happen, it means freq scaling finish in bl31,
+	 * use 100ms as timeout time
+	 */
+	wait_event_timeout(dmcfreq->wait_dcf_queue,
+			   !dmcfreq->wait_dcf_flag, HZ / 10);
+
+	dmc_event(DMCFREQ_FINISH);
+
+	/*
+	 * check the dpll rate
+	 * there only two result we will get,
+	 * 1. ddr frequency scaling fail, we still get the old rate
+	 * 2, ddr frequency scaling sucessful, we get the rate we set
+	 */
+	dmcfreq->rate = clk_get_rate(dmcfreq->dmc_clk);
+
+	/* if get the incorrect rate, set voltage to old value */
+	if (dmcfreq->rate != target_rate) {
+		dev_err(dev, "get wrong ddr frequency, Request freq %lu,\
+			Current freq %lu\n", target_rate, dmcfreq->rate);
+		regulator_set_voltage(dmcfreq->vdd_center, dmcfreq->volt,
+				      dmcfreq->volt);
+	} else if (old_clk_rate > target_rate)
+		err = regulator_set_voltage(dmcfreq->vdd_center, target_volt,
+					    target_volt);
+	if (err)
+		dev_err(dev, "Unable to set vol %lu\n", target_volt);
+
+out:
+	mutex_unlock(&dmcfreq->lock);
+	return err;
+}
+
+static int rk3399_dmcfreq_get_dev_status(struct device *dev,
+					 struct devfreq_dev_status *stat)
+{
+	struct platform_device *pdev = container_of(dev, struct platform_device,
+						    dev);
+	struct rk3399_dmcfreq *dmcfreq = platform_get_drvdata(pdev);
+	struct devfreq_event_data edata;
+
+	devfreq_event_get_event(dmcfreq->edev, &edata);
+
+	stat->current_frequency = dmcfreq->rate;
+	stat->busy_time = edata.load_count;
+	stat->total_time = edata.total_count;
+
+	return 0;
+}
+
+static int rk3399_dmcfreq_get_cur_freq(struct device *dev, unsigned long *freq)
+{
+	struct platform_device *pdev = container_of(dev, struct platform_device,
+						    dev);
+	struct rk3399_dmcfreq *dmcfreq = platform_get_drvdata(pdev);
+
+	*freq = dmcfreq->rate;
+
+	return 0;
+}
+
+static void rk3399_dmcfreq_exit(struct device *dev)
+{
+	struct platform_device *pdev = container_of(dev, struct platform_device,
+						    dev);
+	struct rk3399_dmcfreq *dmcfreq = platform_get_drvdata(pdev);
+
+	devfreq_unregister_opp_notifier(dev, dmcfreq->devfreq);
+}
+
+static struct devfreq_dev_profile rk3399_devfreq_dmc_profile = {
+	.polling_ms	= 200,
+	.target		= rk3399_dmcfreq_target,
+	.get_dev_status	= rk3399_dmcfreq_get_dev_status,
+	.get_cur_freq	= rk3399_dmcfreq_get_cur_freq,
+	.exit		= rk3399_dmcfreq_exit,
+};
+
+static __maybe_unused int rk3399_dmcfreq_suspend(struct device *dev)
+{
+	rockchip_dmc_disable();
+	return 0;
+}
+
+static __maybe_unused int rk3399_dmcfreq_resume(struct device *dev)
+{
+	rockchip_dmc_enable();
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(rk3399_dmcfreq_pm, rk3399_dmcfreq_suspend,
+			 rk3399_dmcfreq_resume);
+
+static int rk3399_dmc_enable_notify(struct notifier_block *nb,
+				    unsigned long event, void *data)
+{
+	struct rk3399_dmcfreq *dmcfreq =
+			      container_of(nb, struct rk3399_dmcfreq, dmc_nb);
+	unsigned long freq = ULONG_MAX;
+
+	if (event == DMC_ENABLE) {
+		devfreq_event_enable_edev(dmcfreq->edev);
+		devfreq_resume_device(dmcfreq->devfreq);
+		return NOTIFY_OK;
+	} else if (event == DMC_DISABLE) {
+		devfreq_event_disable_edev(dmcfreq->edev);
+		devfreq_suspend_device(dmcfreq->devfreq);
+
+		/* when disable dmc, set sdram to max frequency */
+		rk3399_dmcfreq_target(dmcfreq->dev, &freq, 0);
+		return NOTIFY_OK;
+	}
+
+	return NOTIFY_DONE;
+}
+
+static irqreturn_t rk3399_dmc_irq(int irq, void *dev_id)
+{
+	struct rk3399_dmcfreq *dmcfreq = dev_id;
+
+	dmcfreq->wait_dcf_flag = 0;
+	wake_up(&dmcfreq->wait_dcf_queue);
+
+	/* clr dcf irq */
+	sip_smc_clr_ddr_irq();
+
+	return IRQ_HANDLED;
+}
+
+static int of_do_get_timing(struct device_node *np,
+		struct dram_timing *timing)
+{
+	int ret;
+
+	ret = of_property_read_u32(np, "ddr3_speed_bin",
+				   &timing->ddr3_speed_bin);
+	ret |= of_property_read_u32(np, "pd_idle", &timing->pd_idle);
+	ret |= of_property_read_u32(np, "sr_idle", &timing->sr_idle);
+	ret |= of_property_read_u32(np, "sr_mc_gate_idle",
+				    &timing->sr_mc_gate_idle);
+	ret |= of_property_read_u32(np, "srpd_lite_idle",
+				    &timing->srpd_lite_idle);
+	ret |= of_property_read_u32(np, "standby_idle", &timing->standby_idle);
+	ret |= of_property_read_u32(np, "dram_dll_dis_freq",
+				    &timing->dram_dll_dis_freq);
+	ret |= of_property_read_u32(np, "phy_dll_dis_freq",
+				    &timing->phy_dll_dis_freq);
+	ret |= of_property_read_u32(np, "ddr3_odt_dis_freq",
+				    &timing->ddr3_odt_dis_freq);
+	ret |= of_property_read_u32(np, "ddr3_drv", &timing->ddr3_drv);
+	ret |= of_property_read_u32(np, "ddr3_odt", &timing->ddr3_odt);
+	ret |= of_property_read_u32(np, "phy_ddr3_ca_drv",
+				    &timing->phy_ddr3_ca_drv);
+	ret |= of_property_read_u32(np, "phy_ddr3_dq_drv",
+				    &timing->phy_ddr3_dq_drv);
+	ret |= of_property_read_u32(np, "phy_ddr3_odt", &timing->phy_ddr3_odt);
+	ret |= of_property_read_u32(np, "lpddr3_odt_dis_freq",
+				    &timing->lpddr3_odt_dis_freq);
+	ret |= of_property_read_u32(np, "lpddr3_drv", &timing->lpddr3_drv);
+	ret |= of_property_read_u32(np, "lpddr3_odt", &timing->lpddr3_odt);
+	ret |= of_property_read_u32(np, "phy_lpddr3_ca_drv",
+				    &timing->phy_lpddr3_ca_drv);
+	ret |= of_property_read_u32(np, "phy_lpddr3_dq_drv",
+				    &timing->phy_lpddr3_dq_drv);
+	ret |= of_property_read_u32(np, "phy_lpddr3_odt",
+				    &timing->phy_lpddr3_odt);
+	ret |= of_property_read_u32(np, "lpddr4_odt_dis_freq",
+				    &timing->lpddr4_odt_dis_freq);
+	ret |= of_property_read_u32(np, "lpddr4_drv",
+				    &timing->lpddr4_drv);
+	ret |= of_property_read_u32(np, "lpddr4_dq_odt",
+				    &timing->lpddr4_dq_odt);
+	ret |= of_property_read_u32(np, "lpddr4_ca_odt",
+				    &timing->lpddr4_ca_odt);
+	ret |= of_property_read_u32(np, "phy_lpddr4_ca_drv",
+				    &timing->phy_lpddr4_ca_drv);
+	ret |= of_property_read_u32(np, "phy_lpddr4_ck_cs_drv",
+				    &timing->phy_lpddr4_ck_cs_drv);
+	ret |= of_property_read_u32(np, "phy_lpddr4_dq_drv",
+				    &timing->phy_lpddr4_dq_drv);
+	ret |= of_property_read_u32(np, "phy_lpddr4_odt",
+				    &timing->phy_lpddr4_odt);
+
+	return ret;
+}
+
+static struct dram_timing *of_get_ddr_timings(struct device *dev,
+					      struct device_node *np)
+{
+	struct dram_timing	*timing = NULL;
+	struct device_node	*np_tim;
+
+	np_tim = of_parse_phandle(np, "ddr_timing", 0);
+	if (np_tim) {
+		timing = devm_kzalloc(dev, sizeof(*timing), GFP_KERNEL);
+		if (!timing)
+			goto err;
+		if (of_do_get_timing(np_tim, timing)) {
+			devm_kfree(dev, timing);
+			goto err;
+		}
+		return timing;
+	}
+
+err:
+	if (timing) {
+		devm_kfree(dev, timing);
+		timing = NULL;
+	}
+
+	return timing;
+}
+
+static int rk3399_dmcfreq_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct rk3399_dmcfreq *data;
+	int ret, irq, index;
+	uint64_t param = 0;
+	uint32_t *timing;
+	struct device_node *np = pdev->dev.of_node;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "no dmc irq resource\n");
+		return -EINVAL;
+	}
+
+	data = devm_kzalloc(dev, sizeof(struct rk3399_dmcfreq), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	mutex_init(&data->lock);
+
+	data->vdd_center = devm_regulator_get(dev, "center");
+	if (IS_ERR(data->vdd_center)) {
+		dev_err(dev, "Cannot get the regulator \"center\"\n");
+		return PTR_ERR(data->vdd_center);
+	}
+
+	data->dmc_clk = devm_clk_get(dev, "dmc_clk");
+	if (IS_ERR(data->dmc_clk)) {
+		dev_err(dev, "Cannot get the clk dmc_clk\n");
+		return PTR_ERR(data->dmc_clk);
+	};
+
+	data->edev = devfreq_event_get_edev_by_phandle(dev, 0);
+	if (IS_ERR(data->edev))
+		return -EPROBE_DEFER;
+
+	ret = devfreq_event_enable_edev(data->edev);
+	if (ret < 0) {
+		dev_err(dev, "failed to enable devfreq-event devices\n");
+		return ret;
+	}
+
+	/*
+	 * We add a devfreq driver to our parent since it has a device tree node
+	 * with operating points.
+	 */
+	if (dev_pm_opp_of_add_table(dev)) {
+		dev_err(dev, "Invalid operating-points in device tree.\n");
+		return -EINVAL;
+	}
+
+	of_property_read_u32(np, "upthreshold",
+			     &data->ondemand_data.upthreshold);
+
+	of_property_read_u32(np, "downdifferential",
+			     &data->ondemand_data.downdifferential);
+
+	data->rate = clk_get_rate(data->dmc_clk);
+	rk3399_devfreq_dmc_profile.initial_freq = data->rate;
+	data->devfreq = devfreq_add_device(dev,
+					   &rk3399_devfreq_dmc_profile,
+					   "simple_ondemand",
+					   &data->ondemand_data);
+	if (IS_ERR(data->devfreq))
+		return PTR_ERR(data->devfreq);
+
+	devfreq_register_opp_notifier(dev, data->devfreq);
+
+	data->dmc_nb.notifier_call = rk3399_dmc_enable_notify;
+	dmc_register_notifier(&data->dmc_nb);
+	data->irq = irq;
+	ret = devm_request_irq(dev, irq, rk3399_dmc_irq, 0,
+			       dev_name(dev), data);
+	if (ret) {
+		dev_err(dev, "failed to request dmc irq: %d\n", ret);
+		return ret;
+	}
+
+	/* get dram timing and pass it to bl31 */
+	data->timing = of_get_ddr_timings(dev, np);
+	if (data->timing) {
+		timing = (uint32_t *)data->timing;
+		for (index = 0; index < (sizeof(struct dram_timing) / 4);
+		     index++) {
+			param = index;
+			param = param << 32 | *timing++;
+			sip_smc_set_ddr_param(param);
+			param = 0;
+		}
+	}
+	sip_smc_ddr_init();
+
+	init_waitqueue_head(&data->wait_dcf_queue);
+	data->wait_dcf_flag = 0;
+
+	data->dev = dev;
+	platform_set_drvdata(pdev, data);
+
+	return 0;
+}
+
+static int rk3399_dmcfreq_remove(struct platform_device *pdev)
+{
+	struct rk3399_dmcfreq *dmcfreq = platform_get_drvdata(pdev);
+
+	devfreq_remove_device(dmcfreq->devfreq);
+	regulator_put(dmcfreq->vdd_center);
+
+	return 0;
+}
+
+static const struct of_device_id rk3399dmc_devfreq_of_match[] = {
+	{ .compatible = "rockchip,rk3399-dmc" },
+	{ },
+};
+
+static struct platform_driver rk3399_dmcfreq_driver = {
+	.probe	= rk3399_dmcfreq_probe,
+	.remove	= rk3399_dmcfreq_remove,
+	.driver = {
+		.name	= "rk3399-dmc-freq",
+		.pm	= &rk3399_dmcfreq_pm,
+		.of_match_table = rk3399dmc_devfreq_of_match,
+	},
+};
+module_platform_driver(rk3399_dmcfreq_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("RK3399 dmcfreq driver with devfreq framework");
diff --git a/drivers/devfreq/rockchip/rockchip_dmc.c b/drivers/devfreq/rockchip/rockchip_dmc.c
new file mode 100644
index 0000000..78baee5
--- /dev/null
+++ b/drivers/devfreq/rockchip/rockchip_dmc.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
+ * Author: Lin Huang <hl at rock-chips.com>
+ * Base on: https://chromium-review.googlesource.com/#/c/231477/
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/mutex.h>
+#include <soc/rockchip/rockchip_dmc.h>
+
+static int num_wait;
+static int num_disable;
+static BLOCKING_NOTIFIER_HEAD(dmc_notifier_list);
+static DEFINE_MUTEX(dmc_en_lock);
+static DEFINE_MUTEX(dmc_sync_lock);
+
+/**
+ * rockchip_dmc_enabled - Returns true if dmc freq is enabled, false otherwise.
+ */
+bool rockchip_dmc_enabled(void)
+{
+	return num_disable <= 0 && num_wait <= 1;
+}
+
+/**
+ * rockchip_dmc_enable - Enable dmc frequency scaling. Will only enable
+ * frequency scaling if there are 1 or fewer notifiers. Call to undo
+ * rockchip_dmc_disable.
+ */
+void rockchip_dmc_enable(void)
+{
+	mutex_lock(&dmc_en_lock);
+	num_disable--;
+	WARN_ON(num_disable < 0);
+	if (rockchip_dmc_enabled())
+		dmc_event(DMC_ENABLE);
+	mutex_unlock(&dmc_en_lock);
+}
+
+/**
+ * rockchip_dmc_disable - Disable dmc frequency scaling. Call when something
+ * cannot coincide with dmc frequency scaling.
+ */
+void rockchip_dmc_disable(void)
+{
+	mutex_lock(&dmc_en_lock);
+	if (rockchip_dmc_enabled())
+		dmc_event(DMC_DISABLE);
+	num_disable++;
+	mutex_unlock(&dmc_en_lock);
+}
+
+void dmc_event(int event)
+{
+	mutex_lock(&dmc_sync_lock);
+	blocking_notifier_call_chain(&dmc_notifier_list, event, NULL);
+	mutex_unlock(&dmc_sync_lock);
+}
+
+/**
+ * dmc_register_notifier - register a driver to dmc chain
+ * @nb: notifier function to register
+ */
+int dmc_register_notifier(struct notifier_block *nb)
+{
+	int ret;
+
+	if (!nb)
+		return -EINVAL;
+
+	ret = blocking_notifier_chain_register(&dmc_notifier_list, nb);
+
+	return ret;
+}
+
+/**
+ * dmc_unregister_notifier - unregister a driver from dmc chain
+ * @nb: remove notifier function
+ */
+int dmc_unregister_notifier(struct notifier_block *nb)
+{
+	int ret;
+
+	if (!nb)
+		return -EINVAL;
+
+	ret = blocking_notifier_chain_unregister(&dmc_notifier_list, nb);
+
+	return ret;
+}
+
+/**
+ * rockchip_dmc_get - Register the notifier block for the dmc chain.
+ * @nb The dmc notifier block to register
+ */
+int rockchip_dmc_get(struct notifier_block *nb)
+{
+	if (!nb)
+		return -EINVAL;
+
+	mutex_lock(&dmc_en_lock);
+
+	/*
+	 * if have two notifier(enable two vop etc),
+	 * need to disable dmc
+	 */
+	if (num_wait == 1 && num_disable <= 0)
+		dmc_event(DMC_DISABLE);
+	num_wait++;
+	dmc_register_notifier(nb);
+	mutex_unlock(&dmc_en_lock);
+
+	return 0;
+}
+
+/**
+ * rockchip_dmc_put - Remove the notifier block from the dmc chain.
+ * @nb The dmc notifier block to unregister
+ */
+int rockchip_dmc_put(struct notifier_block *nb)
+{
+	if (!nb)
+		return -EINVAL;
+
+	mutex_lock(&dmc_en_lock);
+	num_wait--;
+
+	/* if notifier from 2 back to 1, enable dmc again */
+	if (num_wait == 1 && num_disable <= 0)
+		dmc_event(DMC_ENABLE);
+	dmc_unregister_notifier(nb);
+	mutex_unlock(&dmc_en_lock);
+
+	return 0;
+}
diff --git a/include/soc/rockchip/rockchip_dmc.h b/include/soc/rockchip/rockchip_dmc.h
new file mode 100644
index 0000000..3f69cbf
--- /dev/null
+++ b/include/soc/rockchip/rockchip_dmc.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ */
+
+#ifndef __SOC_ROCKCHIP_DMC_H
+#define __SOC_ROCKCHIP_DMC_H
+
+#include <linux/notifier.h>
+
+#define DMC_ENABLE	0
+#define DMC_DISABLE	1
+#define DMCFREQ_ADJUST	2
+#define DMCFREQ_FINISH	3
+
+#if IS_ENABLED(CONFIG_ARM_ROCKCHIP_DMC_DEVFREQ)
+int rockchip_dmc_get(struct notifier_block *nb);
+int rockchip_dmc_put(struct notifier_block *nb);
+#else
+static inline int rockchip_dmc_get(struct notifier_block *nb)
+{
+	return 0;
+}
+
+static inline int rockchip_dmc_put(struct notifier_block *nb)
+{
+	return 0;
+}
+#endif
+
+void dmc_event(int event);
+int dmc_register_notifier(struct notifier_block *nb);
+int dmc_unregister_notifier(struct notifier_block *nb);
+void rockchip_dmc_enable(void);
+void rockchip_dmc_disable(void);
+bool rockchip_dmc_enabled(void);
+#endif
-- 
2.6.6



From hl at rock-chips.com  Fri Jul 22 02:07:20 2016
From: hl at rock-chips.com (Lin Huang)
Date: Fri, 22 Jul 2016 17:07:20 +0800
Subject: [PATCH v3 7/7] drm/rockchip: Add dmc notifier in vop driver
In-Reply-To: <1469178440-4668-1-git-send-email-hl@rock-chips.com>
References: <1469178440-4668-1-git-send-email-hl@rock-chips.com>
Message-ID: <1469178440-4668-8-git-send-email-hl@rock-chips.com>

when in ddr frequency scaling process, vop can not do
enable or disable operate, since dcf will base on vop vblank
time to do frequency scaling and need to get vop irq if there
have vop enabled. So need register to dmc notifier, and we can
get the dmc status.

Signed-off-by: Lin Huang <hl at rock-chips.com>
---
Changes in v3:
- when do vop eanble/disable, dmc will wait until it finish

Changes in v2:
- None

Changes in v1:
- use wait_event instead usleep

 drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 75 ++++++++++++++++++++++++++---
 1 file changed, 69 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 6255e5b..4220629 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -31,6 +31,8 @@
 #include <linux/reset.h>
 #include <linux/delay.h>
 
+#include <soc/rockchip/rockchip_dmc.h>
+
 #include "rockchip_drm_drv.h"
 #include "rockchip_drm_gem.h"
 #include "rockchip_drm_fb.h"
@@ -120,6 +122,11 @@ struct vop {
 
 	const struct vop_data *data;
 
+	struct notifier_block dmc_nb;
+	int dmc_in_process;
+	int vop_switch_status;
+	wait_queue_head_t	wait_dmc_queue;
+	wait_queue_head_t	wait_vop_switch_queue;
 	uint32_t *regsbak;
 	void __iomem *regs;
 
@@ -430,21 +437,56 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop)
 	spin_unlock_irqrestore(&vop->irq_lock, flags);
 }
 
+static int dmc_notify(struct notifier_block *nb, unsigned long event,
+		      void *data)
+{
+	struct vop *vop = container_of(nb, struct vop, dmc_nb);
+
+	if (event == DMCFREQ_ADJUST) {
+
+		/*
+		 * check if vop in enable or disable process,
+		 * if yes, wait until it finish, use 200ms as
+		 * timeout.
+		 */
+		wait_event_timeout(vop->wait_vop_switch_queue,
+			   !vop->vop_switch_status, HZ / 5);
+		vop->dmc_in_process = 1;
+	} else if (event == DMCFREQ_FINISH) {
+		vop->dmc_in_process = 0;
+		wake_up(&vop->wait_dmc_queue);
+	}
+
+	return NOTIFY_OK;
+}
+
 static void vop_enable(struct drm_crtc *crtc)
 {
 	struct vop *vop = to_vop(crtc);
 	int ret;
 
+	if (vop->is_enabled)
+		return;
+
+	/*
+	 * if in dmc scaling frequency process, wait until it finish
+	 * use 100ms as timeout time.
+	 */
+	wait_event_timeout(vop->wait_dmc_queue,
+			   !vop->dmc_in_process, HZ / 5);
+
+	vop->vop_switch_status = 1;
+
 	ret = pm_runtime_get_sync(vop->dev);
 	if (ret < 0) {
 		dev_err(vop->dev, "failed to get pm runtime: %d\n", ret);
-		return;
+		goto err;
 	}
 
 	ret = clk_enable(vop->hclk);
 	if (ret < 0) {
 		dev_err(vop->dev, "failed to enable hclk - %d\n", ret);
-		return;
+		goto err;
 	}
 
 	ret = clk_enable(vop->dclk);
@@ -458,7 +500,6 @@ static void vop_enable(struct drm_crtc *crtc)
 		dev_err(vop->dev, "failed to enable aclk - %d\n", ret);
 		goto err_disable_dclk;
 	}
-
 	/*
 	 * Slave iommu shares power, irq and clock with vop.  It was associated
 	 * automatically with this master device via common driver code.
@@ -486,7 +527,9 @@ static void vop_enable(struct drm_crtc *crtc)
 	enable_irq(vop->irq);
 
 	drm_crtc_vblank_on(crtc);
-
+	vop->vop_switch_status = 0;
+	wake_up(&vop->wait_vop_switch_queue);
+	rockchip_dmc_get(&vop->dmc_nb);
 	return;
 
 err_disable_aclk:
@@ -495,6 +538,10 @@ err_disable_dclk:
 	clk_disable(vop->dclk);
 err_disable_hclk:
 	clk_disable(vop->hclk);
+err:
+	vop->vop_switch_status = 0;
+	wake_up(&vop->wait_vop_switch_queue);
+	return;
 }
 
 static void vop_crtc_disable(struct drm_crtc *crtc)
@@ -505,6 +552,15 @@ static void vop_crtc_disable(struct drm_crtc *crtc)
 	WARN_ON(vop->event);
 
 	/*
+	 * if in dmc scaling frequency process, wait until it finish
+	 * use 100ms as timeout time.
+	 */
+	wait_event_timeout(vop->wait_dmc_queue,
+			   !vop->dmc_in_process, HZ / 5);
+
+	vop->vop_switch_status = 1;
+
+	/*
 	 * We need to make sure that all windows are disabled before we
 	 * disable that crtc. Otherwise we might try to scan from a destroyed
 	 * buffer later.
@@ -517,7 +573,6 @@ static void vop_crtc_disable(struct drm_crtc *crtc)
 		VOP_WIN_SET(vop, win, enable, 0);
 		spin_unlock(&vop->reg_lock);
 	}
-
 	drm_crtc_vblank_off(crtc);
 
 	/*
@@ -548,7 +603,6 @@ static void vop_crtc_disable(struct drm_crtc *crtc)
 	 * vop standby complete, so iommu detach is safe.
 	 */
 	rockchip_drm_dma_detach_device(vop->drm_dev, vop->dev);
-
 	clk_disable(vop->dclk);
 	clk_disable(vop->aclk);
 	clk_disable(vop->hclk);
@@ -561,6 +615,9 @@ static void vop_crtc_disable(struct drm_crtc *crtc)
 
 		crtc->state->event = NULL;
 	}
+	vop->vop_switch_status = 0;
+	wake_up(&vop->wait_vop_switch_queue);
+	rockchip_dmc_put(&vop->dmc_nb);
 }
 
 static void vop_plane_destroy(struct drm_plane *plane)
@@ -1247,6 +1304,7 @@ static int vop_create_crtc(struct vop *vop)
 		goto err_cleanup_crtc;
 	}
 
+	vop->dmc_nb.notifier_call = dmc_notify;
 	init_completion(&vop->dsp_hold_completion);
 	init_completion(&vop->wait_update_complete);
 	crtc->port = port;
@@ -1468,6 +1526,11 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
 	/* IRQ is initially disabled; it gets enabled in power_on */
 	disable_irq(vop->irq);
 
+	init_waitqueue_head(&vop->wait_vop_switch_queue);
+	vop->vop_switch_status = 0;
+	init_waitqueue_head(&vop->wait_dmc_queue);
+	vop->dmc_in_process = 0;
+
 	ret = vop_create_crtc(vop);
 	if (ret)
 		return ret;
-- 
2.6.6



From cw00.choi at samsung.com  Fri Jul 22 02:29:13 2016
From: cw00.choi at samsung.com (Chanwoo Choi)
Date: Fri, 22 Jul 2016 18:29:13 +0900
Subject: [v6 PATCH 1/6] extcon: Add Type-C and DP support
In-Reply-To: <1469106808-28297-2-git-send-email-zyw@rock-chips.com>
References: <1469106808-28297-1-git-send-email-zyw@rock-chips.com>
 <CGME20160721131520epcas1p402684ff2569a23d7e5e011da9fb07667@epcas1p4.samsung.com>
 <1469106808-28297-2-git-send-email-zyw@rock-chips.com>
Message-ID: <5791E769.9060403@samsung.com>

Hi Chris,

I'm sorry for late reply. I finished the first draft to support the extcon property.
You can check the patches[1]. But, I need more time to test it. After tested it,
I'll send the patches.

[1] https://git.kernel.org/cgit/linux/kernel/git/chanwoo/extcon.git/log/?h=extcon-test

Chanwoo Choi (4):
  extcon: Add the extcon_type to group each connector into five category
  extcon: Add the support for extcon property according to type of connector
  extcon: Rename the extcon_set/get_state() to maintain the function naming pattern
  extcon: Add the sync APIs to support the  notification for extcon property

Chris Zhong (1):
  extcon: Add EXTCON_DISP_DP and the property for USB Type-C

Regards,
Chanwoo Choi

On 2016? 07? 21? 22:13, Chris Zhong wrote:
> Add EXTCON_DISP_DP for the Display external connector. For Type-C
> connector the DisplayPort can work as an Alternate Mode(VESA DisplayPort
> Alt Mode on USB Type-C Standard). The Type-C support both normal and
> flipped orientation, so add a property to extcon.
> 
> Signe-off-by: Chris Zhong <zyw at rock-chips.com>
> 
> Signed-off-by: Chris Zhong <zyw at rock-chips.com>
> ---
> 
> Changes in v6:
> - move the EXTCON_PROP_TYPEC_POLARITY to EXTCON_TYPE_USB in _supported
> Series-changes: 5
> - support get property
> 
> Changes in v5: None
> Changes in v4: None
> Changes in v3: None
> Changes in v2: None
> Changes in v1: None
> 
>  drivers/extcon/extcon.c | 26 ++++++++++++++++++++++++++
>  include/linux/extcon.h  | 13 +++++++++++++
>  2 files changed, 39 insertions(+)
> 
> diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c
> index a1117db..f79b510 100644
> --- a/drivers/extcon/extcon.c
> +++ b/drivers/extcon/extcon.c
> @@ -157,6 +157,11 @@ struct __extcon_info {
>  		.id = EXTCON_DISP_VGA,
>  		.name = "VGA",
>  	},
> +	[EXTCON_DISP_DP] = {
> +		.type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB,
> +		.id = EXTCON_DISP_DP,
> +		.name = "DP",
> +	},
>  
>  	/* Miscellaneous external connector */
>  	[EXTCON_DOCK] = {
> @@ -270,6 +275,7 @@ static bool is_extcon_property_supported(unsigned int id,
>  		switch (prop) {
>  		case EXTCON_PROP_USB_ID:
>  		case EXTCON_PROP_USB_VBUS:
> +		case EXTCON_PROP_TYPEC_POLARITY:
>  			return true;
>  		default:
>  			break;
> @@ -547,6 +553,26 @@ int extcon_get_cable_property(struct extcon_dev *edev, unsigned int id,
>  				enum extcon_property prop,
>  				union extcon_property_value *val)
>  {
> +	struct extcon_cable *cable;
> +	int index;
> +
> +	if (!edev)
> +		return -EINVAL;
> +
> +	/* Check the property whether is supported or not */
> +	if (!is_extcon_property_supported(id, prop))
> +		return -EINVAL;
> +
> +	/* Find the cable index of external connector by using id */
> +	index = find_cable_index_by_id(edev, id);
> +	if (index < 0)
> +		return index;
> +
> +	/* Store the property value */
> +	cable = &edev->cables[index];
> +
> +	val->intval = cable->propval[prop].intval;
> +
>  	return 0;
>  }
>  
> diff --git a/include/linux/extcon.h b/include/linux/extcon.h
> index f6f0a8d..50ef87f 100644
> --- a/include/linux/extcon.h
> +++ b/include/linux/extcon.h
> @@ -77,6 +77,7 @@ enum extcon_type {
>  #define EXTCON_DISP_MHL		41	/* Mobile High-Definition Link */
>  #define EXTCON_DISP_DVI		42	/* Digital Visual Interface */
>  #define EXTCON_DISP_VGA		43	/* Video Graphics Array */
> +#define EXTCON_DISP_DP		44	/* DisplayPort */
>  
>  /* Miscellaneous external connector */
>  #define EXTCON_DOCK		60
> @@ -108,9 +109,13 @@ enum extcon_property {
>  	 * - EXTCON_PROP_USB_USB
>  	 * @type:	integer (int value)
>  	 * @value:	0 (low) or 1 (high)
> +	 * - EXTCON_PROP_TYPEC_POLARITY,
> +	 * @type:	integer (int value)
> +	 * @value:	0 (normal) or 1 (flip)
>  	 */
>  	EXTCON_PROP_USB_ID = 0,
>  	EXTCON_PROP_USB_VBUS,
> +	EXTCON_PROP_TYPEC_POLARITY,
>  
>  	/* Properties of EXTCON_TYPE_CHG. */
>  	/* Properties of EXTCON_TYPE_JACK. */
> @@ -225,6 +230,14 @@ extern int extcon_get_cable_state_(struct extcon_dev *edev, unsigned int id);
>  extern int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id,
>  				   bool cable_state);
>  
> +extern int extcon_get_cable_property(struct extcon_dev *edev, unsigned int id,
> +				     enum extcon_property prop,
> +				     union extcon_property_value *val);
> +
> +extern int extcon_set_cable_property(struct extcon_dev *edev, unsigned int id,
> +				     enum extcon_property prop,
> +				     union extcon_property_value val);
> +
>  /*
>   * Following APIs are to monitor every action of a notifier.
>   * Registrar gets notified for every external port of a connection device.
> 



From zyw at rock-chips.com  Fri Jul 22 02:40:13 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Fri, 22 Jul 2016 17:40:13 +0800
Subject: [v6 PATCH 1/6] extcon: Add Type-C and DP support
In-Reply-To: <5791E769.9060403@samsung.com>
References: <1469106808-28297-1-git-send-email-zyw@rock-chips.com>
 <CGME20160721131520epcas1p402684ff2569a23d7e5e011da9fb07667@epcas1p4.samsung.com>
 <1469106808-28297-2-git-send-email-zyw@rock-chips.com>
 <5791E769.9060403@samsung.com>
Message-ID: <5791E9FD.10407@rock-chips.com>

Hi Chanwoo Choi

Thanks for your help, I am going to post V7 next week base on these 
latest extcon patch.

Regards
Chris Zhong

On 07/22/2016 05:29 PM, Chanwoo Choi wrote:
> Hi Chris,
>
> I'm sorry for late reply. I finished the first draft to support the extcon property.
> You can check the patches[1]. But, I need more time to test it. After tested it,
> I'll send the patches.
>
> [1] https://git.kernel.org/cgit/linux/kernel/git/chanwoo/extcon.git/log/?h=extcon-test
>
> Chanwoo Choi (4):
>    extcon: Add the extcon_type to group each connector into five category
>    extcon: Add the support for extcon property according to type of connector
>    extcon: Rename the extcon_set/get_state() to maintain the function naming pattern
>    extcon: Add the sync APIs to support the  notification for extcon property
>
> Chris Zhong (1):
>    extcon: Add EXTCON_DISP_DP and the property for USB Type-C
>
> Regards,
> Chanwoo Choi
>
> On 2016? 07? 21? 22:13, Chris Zhong wrote:
>> Add EXTCON_DISP_DP for the Display external connector. For Type-C
>> connector the DisplayPort can work as an Alternate Mode(VESA DisplayPort
>> Alt Mode on USB Type-C Standard). The Type-C support both normal and
>> flipped orientation, so add a property to extcon.
>>
>> Signe-off-by: Chris Zhong <zyw at rock-chips.com>
>>
>> Signed-off-by: Chris Zhong <zyw at rock-chips.com>
>> ---
>>
>> Changes in v6:
>> - move the EXTCON_PROP_TYPEC_POLARITY to EXTCON_TYPE_USB in _supported
>> Series-changes: 5
>> - support get property
>>
>> Changes in v5: None
>> Changes in v4: None
>> Changes in v3: None
>> Changes in v2: None
>> Changes in v1: None
>>
>>   drivers/extcon/extcon.c | 26 ++++++++++++++++++++++++++
>>   include/linux/extcon.h  | 13 +++++++++++++
>>   2 files changed, 39 insertions(+)
>>
>> diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c
>> index a1117db..f79b510 100644
>> --- a/drivers/extcon/extcon.c
>> +++ b/drivers/extcon/extcon.c
>> @@ -157,6 +157,11 @@ struct __extcon_info {
>>   		.id = EXTCON_DISP_VGA,
>>   		.name = "VGA",
>>   	},
>> +	[EXTCON_DISP_DP] = {
>> +		.type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB,
>> +		.id = EXTCON_DISP_DP,
>> +		.name = "DP",
>> +	},
>>   
>>   	/* Miscellaneous external connector */
>>   	[EXTCON_DOCK] = {
>> @@ -270,6 +275,7 @@ static bool is_extcon_property_supported(unsigned int id,
>>   		switch (prop) {
>>   		case EXTCON_PROP_USB_ID:
>>   		case EXTCON_PROP_USB_VBUS:
>> +		case EXTCON_PROP_TYPEC_POLARITY:
>>   			return true;
>>   		default:
>>   			break;
>> @@ -547,6 +553,26 @@ int extcon_get_cable_property(struct extcon_dev *edev, unsigned int id,
>>   				enum extcon_property prop,
>>   				union extcon_property_value *val)
>>   {
>> +	struct extcon_cable *cable;
>> +	int index;
>> +
>> +	if (!edev)
>> +		return -EINVAL;
>> +
>> +	/* Check the property whether is supported or not */
>> +	if (!is_extcon_property_supported(id, prop))
>> +		return -EINVAL;
>> +
>> +	/* Find the cable index of external connector by using id */
>> +	index = find_cable_index_by_id(edev, id);
>> +	if (index < 0)
>> +		return index;
>> +
>> +	/* Store the property value */
>> +	cable = &edev->cables[index];
>> +
>> +	val->intval = cable->propval[prop].intval;
>> +
>>   	return 0;
>>   }
>>   
>> diff --git a/include/linux/extcon.h b/include/linux/extcon.h
>> index f6f0a8d..50ef87f 100644
>> --- a/include/linux/extcon.h
>> +++ b/include/linux/extcon.h
>> @@ -77,6 +77,7 @@ enum extcon_type {
>>   #define EXTCON_DISP_MHL		41	/* Mobile High-Definition Link */
>>   #define EXTCON_DISP_DVI		42	/* Digital Visual Interface */
>>   #define EXTCON_DISP_VGA		43	/* Video Graphics Array */
>> +#define EXTCON_DISP_DP		44	/* DisplayPort */
>>   
>>   /* Miscellaneous external connector */
>>   #define EXTCON_DOCK		60
>> @@ -108,9 +109,13 @@ enum extcon_property {
>>   	 * - EXTCON_PROP_USB_USB
>>   	 * @type:	integer (int value)
>>   	 * @value:	0 (low) or 1 (high)
>> +	 * - EXTCON_PROP_TYPEC_POLARITY,
>> +	 * @type:	integer (int value)
>> +	 * @value:	0 (normal) or 1 (flip)
>>   	 */
>>   	EXTCON_PROP_USB_ID = 0,
>>   	EXTCON_PROP_USB_VBUS,
>> +	EXTCON_PROP_TYPEC_POLARITY,
>>   
>>   	/* Properties of EXTCON_TYPE_CHG. */
>>   	/* Properties of EXTCON_TYPE_JACK. */
>> @@ -225,6 +230,14 @@ extern int extcon_get_cable_state_(struct extcon_dev *edev, unsigned int id);
>>   extern int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id,
>>   				   bool cable_state);
>>   
>> +extern int extcon_get_cable_property(struct extcon_dev *edev, unsigned int id,
>> +				     enum extcon_property prop,
>> +				     union extcon_property_value *val);
>> +
>> +extern int extcon_set_cable_property(struct extcon_dev *edev, unsigned int id,
>> +				     enum extcon_property prop,
>> +				     union extcon_property_value val);
>> +
>>   /*
>>    * Following APIs are to monitor every action of a notifier.
>>    * Registrar gets notified for every external port of a connection device.
>>
>
>
>




From heiko at sntech.de  Fri Jul 22 03:00:24 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Fri, 22 Jul 2016 12:00:24 +0200
Subject: [PATCH v3 1/7] firmware: rockchip: sip: Add rockchip SIP runtime
 service
In-Reply-To: <1469178440-4668-2-git-send-email-hl@rock-chips.com>
References: <1469178440-4668-1-git-send-email-hl@rock-chips.com>
 <1469178440-4668-2-git-send-email-hl@rock-chips.com>
Message-ID: <1742973.lJ4EpAnn7j@diego>

Hi,

Am Freitag, 22. Juli 2016, 17:07:14 schrieb Lin Huang:
> From: Shengfei xu <xsf at rock-chips.com>
> 
> This patch adds support for the SiP interface, we can pass dram
> paramtert to bl31, and control ddr frequency scaling in bl31.
> 
> Signed-off-by: Shengfei xu <xsf at rock-chips.com>
> Signed-off-by: Lin Huang <hl at rock-chips.com>

[...]

> +++ b/drivers/firmware/rockchip_sip.c
> @@ -0,0 +1,64 @@
> +/*
> + * 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.
> + *
> + * Copyright (C) 2016 ARM Limited
> + */
> +#include <linux/errno.h>
> +#include <linux/linkage.h>
> +#include <linux/of.h>
> +#include <linux/pm.h>
> +#include <linux/printk.h>
> +#include "rockchip_sip.h"
> +
> +typedef unsigned long (psci_fn)(unsigned long, unsigned long,
> +				unsigned long, unsigned long);
> +asmlinkage psci_fn __invoke_psci_fn_smc;
> +
> +#define CONFIG_DRAM_INIT	0x00
> +#define CONFIG_DRAM_SET_RATE	0x01
> +#define CONFIG_DRAM_ROUND_RATE	0x02
> +#define CONFIG_DRAM_SET_AT_SR	0x03
> +#define CONFIG_DRAM_GET_BW	0x04
> +#define CONFIG_DRAM_GET_RATE	0x05
> +#define CONFIG_DRAM_CLR_IRQ	0x06
> +#define CONFIG_DRAM_SET_PARAM   0x07
> +
> +uint64_t sip_smc_ddr_init(void)

if I'm reading the ATF writers guide [0] correctly, the SiP calls are soc-
specific things:
"For example, two SoC providers can use the same Function ID within the SiP
Service calls OEN range to mean different things - as these calls should be
specific to the SoC. "

So I guess the function calls, as well as the constants should have a prefix.
ROCKCHIP_SIP_DDR_FREQ, rockchip_sip_smc_set_ddr_param(), etc.

Looking at Zynq in the ATF [1] seems to strengthen that theory with the 
similar constants having the ZYNQMP_ prefix.

I guess it doesn't matter to much on the ATF side, as you build a soc-specific 
image, but on the kernel-side I think it should unambiguos, especially as 
functions and constants are part of a public header.

I think the code itself looks fine though :-)


Heiko

[0] https://github.com/ARM-software/arm-trusted-firmware/blob/master/docs/rt-svc-writers-guide.md
[1] https://github.com/ARM-software/arm-trusted-firmware/blob/master/plat/xilinx/zynqmp/sip_svc_setup.c



> +{
> +	return __invoke_psci_fn_smc(SIP_DDR_FREQ, 0,
> +				    0, CONFIG_DRAM_INIT);
> +}
> +
> +uint64_t sip_smc_set_ddr_param(uint64_t param)
> +{
> +	return __invoke_psci_fn_smc(SIP_DDR_FREQ, param,
> +				    0, CONFIG_DRAM_SET_PARAM);
> +}
> +
> +uint64_t sip_smc_set_ddr_rate(uint64_t rate)
> +{
> +	return __invoke_psci_fn_smc(SIP_DDR_FREQ, rate, 0,
> +				    CONFIG_DRAM_SET_RATE);
> +}
> +
> +uint64_t sip_smc_get_ddr_rate(void)
> +{
> +	return __invoke_psci_fn_smc(SIP_DDR_FREQ, 0, 0, CONFIG_DRAM_GET_RATE);
> +}
> +
> +uint64_t sip_smc_clr_ddr_irq(void)
> +{
> +	return __invoke_psci_fn_smc(SIP_DDR_FREQ, 0, 0, CONFIG_DRAM_CLR_IRQ);
> +}
> +
> +uint64_t sip_smc_get_call_count(void)
> +{
> +	return __invoke_psci_fn_smc(SIP_SVC_CALL_COUNT, 0, 0, 0);
> +}
> diff --git a/drivers/firmware/rockchip_sip.h
> b/drivers/firmware/rockchip_sip.h new file mode 100644
> index 0000000..6487734
> --- /dev/null
> +++ b/drivers/firmware/rockchip_sip.h
> @@ -0,0 +1,59 @@
> +/* Copyright (c) 2010-2015, The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only 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.
> + */
> +#ifndef __SIP_INT_H
> +#define __SIP_INT_H
> +
> +/* SMC function IDs for SiP Service queries */
> +#define SIP_SVC_CALL_COUNT		0x8200ff00
> +#define SIP_SVC_UID			0x8200ff01
> +#define SIP_SVC_VERSION			0x8200ff03
> +#define SIP_DDR_FREQ			0xC2000008
> +
> +#if IS_ENABLED(CONFIG_ROCKCHIP_SIP)
> +uint64_t sip_smc_set_ddr_rate(uint64_t rate);
> +uint64_t sip_smc_get_ddr_rate(void);
> +uint64_t sip_smc_clr_ddr_irq(void);
> +uint64_t sip_smc_get_call_count(void);
> +uint64_t sip_smc_ddr_init(void);
> +uint64_t sip_smc_set_ddr_param(uint64_t param);
> +#else
> +static inline uint64_t sip_smc_set_ddr_rate(uint64_t rate)
> +{
> +	return 0;
> +}
> +
> +static inline uint64_t sip_smc_get_ddr_rate(void)
> +{
> +	return 0;
> +}
> +
> +static inline uint64_t sip_smc_clr_ddr_irq(void)
> +{
> +	return 0;
> +}
> +
> +static inline uint64_t sip_smc_get_call_count(void)
> +{
> +	return 0;
> +}
> +
> +static inline uint64_t sip_smc_ddr_init(void)
> +{
> +	return 0;
> +}
> +
> +static inline uint64_t sip_smc_set_ddr_param(uint64_t param)
> +{
> +	return 0;
> +}
> +#endif
> +#endif



From heiko at sntech.de  Fri Jul 22 03:08:33 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Fri, 22 Jul 2016 12:08:33 +0200
Subject: [PATCH v3 3/7] clk: rockchip: rk3399: add SCLK_DDRCLK ID for ddrc
In-Reply-To: <1469178440-4668-4-git-send-email-hl@rock-chips.com>
References: <1469178440-4668-1-git-send-email-hl@rock-chips.com>
 <1469178440-4668-4-git-send-email-hl@rock-chips.com>
Message-ID: <1486821.Jd4tDmJ7c4@diego>

Am Freitag, 22. Juli 2016, 17:07:16 schrieb Lin Huang:
> Signed-off-by: Lin Huang <hl at rock-chips.com>
> ---
> Changes in v3:
> -None
> 
> Changes in v2:
> - None
> Changes in v1:
> - None
> 
>  include/dt-bindings/clock/rk3399-cru.h | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/include/dt-bindings/clock/rk3399-cru.h
> b/include/dt-bindings/clock/rk3399-cru.h index 50a44cf..8a0f0442 100644
> --- a/include/dt-bindings/clock/rk3399-cru.h
> +++ b/include/dt-bindings/clock/rk3399-cru.h
> @@ -131,6 +131,7 @@
>  #define SCLK_DPHY_RX0_CFG		165
>  #define SCLK_RMII_SRC			166
>  #define SCLK_PCIEPHY_REF100M		167
> +#define SCLK_DDRCLK			168

nit: SCLK_DDR (i.e. without the CLK at the end)?

>  #define DCLK_VOP0			180
>  #define DCLK_VOP1			181



From seanpaul at chromium.org  Fri Jul 22 08:03:57 2016
From: seanpaul at chromium.org (Sean Paul)
Date: Fri, 22 Jul 2016 11:03:57 -0400
Subject: [PATCH v1 2/2] drm/bridge: analogix_dp: turn off the panel when
 eDP need to disable
In-Reply-To: <57917011.6000108@rock-chips.com>
References: <1469106874-4912-1-git-send-email-ykk@rock-chips.com>
 <1469106886-4979-1-git-send-email-ykk@rock-chips.com>
 <CAOw6vbK7CnFwH_Tyrq6ab4DBxVLa8-JCYEM-Tu+mOYsKmNULtg@mail.gmail.com>
 <57917011.6000108@rock-chips.com>
Message-ID: <CAOw6vbLXDuEW1egN3N8XxXdW8hdrY8W9T-v_d6hWDnRcRqZe_A@mail.gmail.com>

On Thu, Jul 21, 2016 at 9:00 PM, Yakir Yang <ykk at rock-chips.com> wrote:
> Sean,
>
> Thanks for your fast respond :-)
>
> But this patch is not the latest one, I have upgraded this to "v1.1" version
> to fix the eDP can't be disabled problem:
>     [PATCH v1.1 2/2] drm/bridge: analogix_dp: turn off the panel when eDP
> need to disable
>         Changes in v1.1: - unprepare the panel at the end of
> bridge->disable() function
>
> In spite of this, I would take your comments with my "v1.1" patch.
>
> On 07/21/2016 10:28 PM, Sean Paul wrote:
>
> On Thu, Jul 21, 2016 at 9:14 AM, Yakir Yang <ykk at rock-chips.com> wrote:
>
> Some panels (like Sharp LQ123P1JX31) need to be turn off when eDP
> controller stop to send valid video signal, otherwhise panel would
> go burn in, and keep flicker and flicker.
>
> So it's better to turn off the panel when eDP need to disable, and
> we need to turn on the panel in connector->detect() callback, so
> that driver would detect panel status rightly.
>
> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
> ---
>  drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 18 ++++++++++--------
>  1 file changed, 10 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> index 32715da..ea059b3 100644
> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> @@ -961,6 +961,14 @@ analogix_dp_detect(struct drm_connector *connector,
> bool force)
>  {
>         struct analogix_dp_device *dp = to_dp(connector);
>
> +       /*
> +        * Panle would prepare for several times here, but don't worry it
>
> s/Panle/Panel/
>
>
> Done
>
> +        * would only enable the hardware at the first prepare time.
>
> Errr, this shouldn't go in detect. How about putting this in
> bridge_enable instead?
>
>
> Nope, if we put this in bridge_enable, then eDP would never be enabled.
> Here're the calling flow.
>
> --> analogix_dp_probe
>   --> analogix_dp_bind  # we still haven't prepared the panel here, that
> means panel have been powered up
>     --> analogix_dp_detect  # Oops, losing panel valid hotplug signal, cause
> panel have been powered up
>     --> ** Keep detecting **


Yeah, after playing around with the patch yesterday I discovered this
for myself. I still don't think detect() should be changing hardware
state. Perhaps you could add a matching unprepare() after you attempt
to detect the panel?

Sean

>
> +        */
> +       if (dp->plat_data->panel)
> +               if (drm_panel_prepare(dp->plat_data->panel))
>
> Personally, I don't like doing work in a conditional since you're
> throwing the return code away. Could you break this out into:
>
> ret = drm_panel_prepare(dp->plat_data->panel);
> if (ret)
>   DRM_ERROR("failed to setup the panel ret=%d\n", ret);
>
>
> Okay
>
>
> +                       DRM_ERROR("failed to setup the panel\n");
> +
>         if (analogix_dp_detect_hpd(dp))
>                 return connector_status_disconnected;
>
> @@ -1063,7 +1071,8 @@ static void analogix_dp_bridge_disable(struct
> drm_bridge *bridge)
>                 return;
>
>         if (dp->plat_data->panel) {
> -               if (drm_panel_disable(dp->plat_data->panel)) {
> +               if (drm_panel_disable(dp->plat_data->panel) ||
> +                   drm_panel_unprepare(dp->plat_data->panel)) {
>
> Same comment here, please break this out into separate statements for
> better readability/logging.
>
>
> Okay,
>
>
> Thanks,
> - Yakir
>
>
>                         DRM_ERROR("failed to disable the panel\n");
>                         return;
>                 }
> @@ -1333,13 +1342,6 @@ int analogix_dp_bind(struct device *dev, struct
> drm_device *drm_dev,
>
>         phy_power_on(dp->phy);
>
> -       if (dp->plat_data->panel) {
> -               if (drm_panel_prepare(dp->plat_data->panel)) {
> -                       DRM_ERROR("failed to setup the panel\n");
> -                       return -EBUSY;
> -               }
> -       }
> -
>         analogix_dp_init_dp(dp);
>
>         ret = devm_request_threaded_irq(&pdev->dev, dp->irq,
> --
> 1.9.1
>
>
>
>
>


From groeck at google.com  Fri Jul 22 10:23:32 2016
From: groeck at google.com (Guenter Roeck)
Date: Fri, 22 Jul 2016 10:23:32 -0700
Subject: [v6 PATCH 1/6] extcon: Add Type-C and DP support
In-Reply-To: <5791E9FD.10407@rock-chips.com>
References: <1469106808-28297-1-git-send-email-zyw@rock-chips.com>
 <CGME20160721131520epcas1p402684ff2569a23d7e5e011da9fb07667@epcas1p4.samsung.com>
 <1469106808-28297-2-git-send-email-zyw@rock-chips.com>
 <5791E769.9060403@samsung.com> <5791E9FD.10407@rock-chips.com>
Message-ID: <CABXOdTe5QgpVgUCHgGKGgX-XqhhXR_hrKk3rsPZAp+8urdnxzQ@mail.gmail.com>

To create a baseline, I'll pick up the changes from the extcon
repository and make them available in chromeos-4.4.

Guenter

On Fri, Jul 22, 2016 at 2:40 AM, Chris Zhong <zyw at rock-chips.com> wrote:
> Hi Chanwoo Choi
>
> Thanks for your help, I am going to post V7 next week base on these latest
> extcon patch.
>
> Regards
> Chris Zhong
>
>
> On 07/22/2016 05:29 PM, Chanwoo Choi wrote:
>>
>> Hi Chris,
>>
>> I'm sorry for late reply. I finished the first draft to support the extcon
>> property.
>> You can check the patches[1]. But, I need more time to test it. After
>> tested it,
>> I'll send the patches.
>>
>> [1]
>> https://git.kernel.org/cgit/linux/kernel/git/chanwoo/extcon.git/log/?h=extcon-test
>>
>> Chanwoo Choi (4):
>>    extcon: Add the extcon_type to group each connector into five category
>>    extcon: Add the support for extcon property according to type of
>> connector
>>    extcon: Rename the extcon_set/get_state() to maintain the function
>> naming pattern
>>    extcon: Add the sync APIs to support the  notification for extcon
>> property
>>
>> Chris Zhong (1):
>>    extcon: Add EXTCON_DISP_DP and the property for USB Type-C
>>
>> Regards,
>> Chanwoo Choi
>>
>> On 2016? 07? 21? 22:13, Chris Zhong wrote:
>>>
>>> Add EXTCON_DISP_DP for the Display external connector. For Type-C
>>> connector the DisplayPort can work as an Alternate Mode(VESA DisplayPort
>>> Alt Mode on USB Type-C Standard). The Type-C support both normal and
>>> flipped orientation, so add a property to extcon.
>>>
>>> Signe-off-by: Chris Zhong <zyw at rock-chips.com>
>>>
>>> Signed-off-by: Chris Zhong <zyw at rock-chips.com>
>>> ---
>>>
>>> Changes in v6:
>>> - move the EXTCON_PROP_TYPEC_POLARITY to EXTCON_TYPE_USB in _supported
>>> Series-changes: 5
>>> - support get property
>>>
>>> Changes in v5: None
>>> Changes in v4: None
>>> Changes in v3: None
>>> Changes in v2: None
>>> Changes in v1: None
>>>
>>>   drivers/extcon/extcon.c | 26 ++++++++++++++++++++++++++
>>>   include/linux/extcon.h  | 13 +++++++++++++
>>>   2 files changed, 39 insertions(+)
>>>
>>> diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c
>>> index a1117db..f79b510 100644
>>> --- a/drivers/extcon/extcon.c
>>> +++ b/drivers/extcon/extcon.c
>>> @@ -157,6 +157,11 @@ struct __extcon_info {
>>>                 .id = EXTCON_DISP_VGA,
>>>                 .name = "VGA",
>>>         },
>>> +       [EXTCON_DISP_DP] = {
>>> +               .type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB,
>>> +               .id = EXTCON_DISP_DP,
>>> +               .name = "DP",
>>> +       },
>>>         /* Miscellaneous external connector */
>>>         [EXTCON_DOCK] = {
>>> @@ -270,6 +275,7 @@ static bool is_extcon_property_supported(unsigned int
>>> id,
>>>                 switch (prop) {
>>>                 case EXTCON_PROP_USB_ID:
>>>                 case EXTCON_PROP_USB_VBUS:
>>> +               case EXTCON_PROP_TYPEC_POLARITY:
>>>                         return true;
>>>                 default:
>>>                         break;
>>> @@ -547,6 +553,26 @@ int extcon_get_cable_property(struct extcon_dev
>>> *edev, unsigned int id,
>>>                                 enum extcon_property prop,
>>>                                 union extcon_property_value *val)
>>>   {
>>> +       struct extcon_cable *cable;
>>> +       int index;
>>> +
>>> +       if (!edev)
>>> +               return -EINVAL;
>>> +
>>> +       /* Check the property whether is supported or not */
>>> +       if (!is_extcon_property_supported(id, prop))
>>> +               return -EINVAL;
>>> +
>>> +       /* Find the cable index of external connector by using id */
>>> +       index = find_cable_index_by_id(edev, id);
>>> +       if (index < 0)
>>> +               return index;
>>> +
>>> +       /* Store the property value */
>>> +       cable = &edev->cables[index];
>>> +
>>> +       val->intval = cable->propval[prop].intval;
>>> +
>>>         return 0;
>>>   }
>>>   diff --git a/include/linux/extcon.h b/include/linux/extcon.h
>>> index f6f0a8d..50ef87f 100644
>>> --- a/include/linux/extcon.h
>>> +++ b/include/linux/extcon.h
>>> @@ -77,6 +77,7 @@ enum extcon_type {
>>>   #define EXTCON_DISP_MHL               41      /* Mobile High-Definition
>>> Link */
>>>   #define EXTCON_DISP_DVI               42      /* Digital Visual
>>> Interface */
>>>   #define EXTCON_DISP_VGA               43      /* Video Graphics Array
>>> */
>>> +#define EXTCON_DISP_DP         44      /* DisplayPort */
>>>     /* Miscellaneous external connector */
>>>   #define EXTCON_DOCK           60
>>> @@ -108,9 +109,13 @@ enum extcon_property {
>>>          * - EXTCON_PROP_USB_USB
>>>          * @type:       integer (int value)
>>>          * @value:      0 (low) or 1 (high)
>>> +        * - EXTCON_PROP_TYPEC_POLARITY,
>>> +        * @type:       integer (int value)
>>> +        * @value:      0 (normal) or 1 (flip)
>>>          */
>>>         EXTCON_PROP_USB_ID = 0,
>>>         EXTCON_PROP_USB_VBUS,
>>> +       EXTCON_PROP_TYPEC_POLARITY,
>>>         /* Properties of EXTCON_TYPE_CHG. */
>>>         /* Properties of EXTCON_TYPE_JACK. */
>>> @@ -225,6 +230,14 @@ extern int extcon_get_cable_state_(struct extcon_dev
>>> *edev, unsigned int id);
>>>   extern int extcon_set_cable_state_(struct extcon_dev *edev, unsigned
>>> int id,
>>>                                    bool cable_state);
>>>   +extern int extcon_get_cable_property(struct extcon_dev *edev, unsigned
>>> int id,
>>> +                                    enum extcon_property prop,
>>> +                                    union extcon_property_value *val);
>>> +
>>> +extern int extcon_set_cable_property(struct extcon_dev *edev, unsigned
>>> int id,
>>> +                                    enum extcon_property prop,
>>> +                                    union extcon_property_value val);
>>> +
>>>   /*
>>>    * Following APIs are to monitor every action of a notifier.
>>>    * Registrar gets notified for every external port of a connection
>>> device.
>>>
>>
>>
>>
>
>


From groeck at google.com  Fri Jul 22 11:21:41 2016
From: groeck at google.com (Guenter Roeck)
Date: Fri, 22 Jul 2016 11:21:41 -0700
Subject: [v6 PATCH 1/6] extcon: Add Type-C and DP support
In-Reply-To: <5791E769.9060403@samsung.com>
References: <1469106808-28297-1-git-send-email-zyw@rock-chips.com>
 <CGME20160721131520epcas1p402684ff2569a23d7e5e011da9fb07667@epcas1p4.samsung.com>
 <1469106808-28297-2-git-send-email-zyw@rock-chips.com>
 <5791E769.9060403@samsung.com>
Message-ID: <CABXOdTe-xtfjBKOPE_EuqBai835V6NiMzUUoJZJZUEuP415xqQ@mail.gmail.com>

Hi,

On Fri, Jul 22, 2016 at 2:29 AM, Chanwoo Choi <cw00.choi at samsung.com> wrote:
> Hi Chris,
>
> I'm sorry for late reply. I finished the first draft to support the extcon property.
> You can check the patches[1]. But, I need more time to test it. After tested it,
> I'll send the patches.
>
> [1] https://git.kernel.org/cgit/linux/kernel/git/chanwoo/extcon.git/log/?h=extcon-test
>
> Chanwoo Choi (4):
>   extcon: Add the extcon_type to group each connector into five category
>   extcon: Add the support for extcon property according to type of connector
>   extcon: Rename the extcon_set/get_state() to maintain the function naming pattern
>   extcon: Add the sync APIs to support the  notification for extcon property
>
> Chris Zhong (1):
>   extcon: Add EXTCON_DISP_DP and the property for USB Type-C
>

Couple of comments:

- In extcon.c, line 560: 'state' is unused.

- extcon_set_property(edev, EXTCON_USB, EXTCON_USB_VBUS, 0);
  does not work. As written, it has to be something like:

  union extcon_property_value prop;
  ...
  prop.intval = vbus_state;
  extcon_set_property(edev, EXTCON_USB, EXTCON_USB_VBUS, prop);

- For USB, the state of EXTCON_USB, EXTCON_USB_HOST, and
EXTCON_DISP_DP tend to change at the same time, together with the
associated properties.
  It might be desirable to have an equivalent of extcon_sync_all(edev);

Thanks,
Guenter


From paul.gortmaker at windriver.com  Fri Jul 22 13:24:08 2016
From: paul.gortmaker at windriver.com (Paul Gortmaker)
Date: Fri, 22 Jul 2016 16:24:08 -0400
Subject: [PATCH v3 6/7] PM / devfreq: rockchip: add devfreq driver for
 rk3399 dmc
In-Reply-To: <1469178440-4668-7-git-send-email-hl@rock-chips.com>
References: <1469178440-4668-1-git-send-email-hl@rock-chips.com>
 <1469178440-4668-7-git-send-email-hl@rock-chips.com>
Message-ID: <CAP=VYLpBBXhMLdNyuYvtJ6XWn-yTof_-oEL7saB87KGY-84c9A@mail.gmail.com>

On Fri, Jul 22, 2016 at 5:07 AM, Lin Huang <hl at rock-chips.com> wrote:
> base on dfi result, we do ddr frequency scaling, register
> dmc driver to devfreq framework, and use simple-ondemand
> policy.
>
> Signed-off-by: Lin Huang <hl at rock-chips.com>
> ---
> Changes in v3:
> - operate dram setting through sip call
> - imporve set rate flow
>
> Changes in v2:
> - None
>
> Changes in v1:
> - move dfi controller to event
> - fix set voltage sequence when set rate fail
> - change Kconfig type from tristate to bool
> - move unuse EXPORT_SYMBOL_GPL()
>
>  drivers/devfreq/Kconfig                 |   1 +
>  drivers/devfreq/Makefile                |   1 +
>  drivers/devfreq/rockchip/Kconfig        |  15 +
>  drivers/devfreq/rockchip/Makefile       |   2 +
>  drivers/devfreq/rockchip/rk3399_dmc.c   | 482 ++++++++++++++++++++++++++++++++
>  drivers/devfreq/rockchip/rockchip_dmc.c | 143 ++++++++++
>  include/soc/rockchip/rockchip_dmc.h     |  45 +++
>  7 files changed, 689 insertions(+)
>  create mode 100644 drivers/devfreq/rockchip/Kconfig
>  create mode 100644 drivers/devfreq/rockchip/Makefile
>  create mode 100644 drivers/devfreq/rockchip/rk3399_dmc.c
>  create mode 100644 drivers/devfreq/rockchip/rockchip_dmc.c
>  create mode 100644 include/soc/rockchip/rockchip_dmc.h
>
> diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig
> index a5be56e..cb67246 100644
> --- a/drivers/devfreq/Kconfig
> +++ b/drivers/devfreq/Kconfig
> @@ -101,5 +101,6 @@ config ARM_TEGRA_DEVFREQ
>           operating frequencies and voltages with OPP support.
>
>  source "drivers/devfreq/event/Kconfig"
> +source "drivers/devfreq/rockchip/Kconfig"
>
>  endif # PM_DEVFREQ
> diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile
> index 09f11d9..48e2ae6 100644
> --- a/drivers/devfreq/Makefile
> +++ b/drivers/devfreq/Makefile
> @@ -9,6 +9,7 @@ obj-$(CONFIG_DEVFREQ_GOV_PASSIVE)       += governor_passive.o
>  # DEVFREQ Drivers
>  obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ)   += exynos-bus.o
>  obj-$(CONFIG_ARM_TEGRA_DEVFREQ)                += tegra-devfreq.o
> +obj-$(CONFIG_ARCH_ROCKCHIP)            += rockchip/
>
>  # DEVFREQ Event Drivers
>  obj-$(CONFIG_PM_DEVFREQ_EVENT)         += event/
> diff --git a/drivers/devfreq/rockchip/Kconfig b/drivers/devfreq/rockchip/Kconfig
> new file mode 100644
> index 0000000..7fb1cff
> --- /dev/null
> +++ b/drivers/devfreq/rockchip/Kconfig
> @@ -0,0 +1,15 @@
> +config ARM_ROCKCHIP_DMC_DEVFREQ
> +       bool "ARM ROCKCHIP DMC DEVFREQ Driver"
> +       depends on ARCH_ROCKCHIP
> +       help
> +         This adds the DEVFREQ driver framework for the rockchip dmc.
> +
> +config ARM_RK3399_DMC_DEVFREQ
> +       bool "ARM RK3399 DMC DEVFREQ Driver"

Since you are using bool Kconfigs for your driver, please do not use
module.h or MODULE_<xyz> tags in your driver, and use the builtin
register function.

Alternatively if there really is  a use case for it to be a modular driver
then use a tristate Kconfig.

THanks,
Paul.
--

> +       depends on ARM_ROCKCHIP_DMC_DEVFREQ
> +       select PM_OPP
> +       select DEVFREQ_GOV_SIMPLE_ONDEMAND
> +       help
> +          This adds the DEVFREQ driver for the RK3399 dmc. It sets the frequency
> +          for the memory controller and reads the usage counts from hardware.
> +


From paul.gortmaker at windriver.com  Fri Jul 22 13:28:53 2016
From: paul.gortmaker at windriver.com (Paul Gortmaker)
Date: Fri, 22 Jul 2016 16:28:53 -0400
Subject: [PATCH v3 5/7] PM / devfreq: event: support rockchip dfi
 controller
In-Reply-To: <1469178440-4668-6-git-send-email-hl@rock-chips.com>
References: <1469178440-4668-1-git-send-email-hl@rock-chips.com>
 <1469178440-4668-6-git-send-email-hl@rock-chips.com>
Message-ID: <CAP=VYLo6ZH+Vc+3N0Ed6XyLdy-rc9tKnjiACDnovcqv2CnE0Sg@mail.gmail.com>

On Fri, Jul 22, 2016 at 5:07 AM, Lin Huang <hl at rock-chips.com> wrote:
> on rk3399 platform, there is dfi conroller can monitor
> ddr load, base on this result, we can do ddr freqency
> scaling.
>
> Signed-off-by: Lin Huang <hl at rock-chips.com>
> Acked-by: Chanwoo Choi <cw00.choi at samsung.com>
> ---
> Changes in v3:
> - None
>
> Changes in v2:
> - use clk_disable_unprepare and clk_enable_prepare
> - remove clk_enable_prepare in probe
> - remove rockchip_dfi_remove function
>
> Changes in v1:
> - None
>
>  drivers/devfreq/event/Kconfig        |   7 +
>  drivers/devfreq/event/Makefile       |   1 +
>  drivers/devfreq/event/rockchip-dfi.c | 253 +++++++++++++++++++++++++++++++++++
>  3 files changed, 261 insertions(+)
>  create mode 100644 drivers/devfreq/event/rockchip-dfi.c
>
> diff --git a/drivers/devfreq/event/Kconfig b/drivers/devfreq/event/Kconfig
> index eb6f74a..0d32d68 100644
> --- a/drivers/devfreq/event/Kconfig
> +++ b/drivers/devfreq/event/Kconfig
> @@ -30,4 +30,11 @@ config DEVFREQ_EVENT_EXYNOS_PPMU
>           (Platform Performance Monitoring Unit) counters to estimate the
>           utilization of each module.
>
> +config DEVFREQ_EVENT_ROCKCHIP_DFI
> +       bool "ROCKCHIP DFI DEVFREQ event Driver"
> +       depends on ARCH_ROCKCHIP
> +       help
> +         This add the devfreq-event driver for Rockchip SoC. It provides DFI
> +         (DDR Monitor Module) driver to count ddr load.
> +
>  endif # PM_DEVFREQ_EVENT
> diff --git a/drivers/devfreq/event/Makefile b/drivers/devfreq/event/Makefile
> index 3d6afd3..dda7090 100644
> --- a/drivers/devfreq/event/Makefile
> +++ b/drivers/devfreq/event/Makefile
> @@ -2,3 +2,4 @@
>
>  obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_NOCP) += exynos-nocp.o
>  obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_PPMU) += exynos-ppmu.o
> +obj-$(CONFIG_DEVFREQ_EVENT_ROCKCHIP_DFI) += rockchip-dfi.o
> diff --git a/drivers/devfreq/event/rockchip-dfi.c b/drivers/devfreq/event/rockchip-dfi.c
> new file mode 100644
> index 0000000..96a0307
> --- /dev/null
> +++ b/drivers/devfreq/event/rockchip-dfi.c
> @@ -0,0 +1,253 @@
> +/*
> + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
> + * Author: Lin Huang <hl at rock-chips.com>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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/clk.h>
> +#include <linux/devfreq-event.h>
> +#include <linux/kernel.h>
> +#include <linux/err.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>

Same comment applies here.   Also might want to check any instances
of "IS_ENABLED" being used on bool items, as that is overkill too.

Thanks,
Paul.


From briannorris at chromium.org  Fri Jul 22 13:39:42 2016
From: briannorris at chromium.org (Brian Norris)
Date: Fri, 22 Jul 2016 13:39:42 -0700
Subject: [PATCH v7 1/2] Documentation: bindings: add dt doc for Rockchip
 PCIe controller
In-Reply-To: <1468802533-17699-1-git-send-email-shawn.lin@rock-chips.com>
References: <1468802533-17699-1-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <20160722203941.GA58807@google.com>

On Mon, Jul 18, 2016 at 08:42:12AM +0800, Shawn Lin wrote:
> This patch adds a binding that describes the Rockchip PCIe controller
> found on Rockchip SoCs PCIe interface.
> 
> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
> 
> Acked-by: Rob Herring <robh at kernel.org>

Reviewed-by: Brian Norris <briannorris at chromium.org>


From heiko at sntech.de  Fri Jul 22 13:50:51 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Fri, 22 Jul 2016 22:50:51 +0200
Subject: [PATCH v3 1/7] firmware: rockchip: sip: Add rockchip SIP runtime
 service
In-Reply-To: <1469178440-4668-2-git-send-email-hl@rock-chips.com>
References: <1469178440-4668-1-git-send-email-hl@rock-chips.com>
 <1469178440-4668-2-git-send-email-hl@rock-chips.com>
Message-ID: <1497407.KruMAzy5tb@diego>

Hi again,

one bigger thing I noticed only now.

Am Freitag, 22. Juli 2016, 17:07:14 schrieben Sie:
> diff --git a/drivers/firmware/rockchip_sip.c
> b/drivers/firmware/rockchip_sip.c new file mode 100644
> index 0000000..7756af9
> --- /dev/null
> +++ b/drivers/firmware/rockchip_sip.c
> @@ -0,0 +1,64 @@
> +/*
> + * 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.
> + *
> + * Copyright (C) 2016 ARM Limited
> + */
> +#include <linux/errno.h>
> +#include <linux/linkage.h>
> +#include <linux/of.h>
> +#include <linux/pm.h>
> +#include <linux/printk.h>
> +#include "rockchip_sip.h"
> +
> +typedef unsigned long (psci_fn)(unsigned long, unsigned long,
> +				unsigned long, unsigned long);
> +asmlinkage psci_fn __invoke_psci_fn_smc;
> +
> +#define CONFIG_DRAM_INIT	0x00
> +#define CONFIG_DRAM_SET_RATE	0x01
> +#define CONFIG_DRAM_ROUND_RATE	0x02
> +#define CONFIG_DRAM_SET_AT_SR	0x03
> +#define CONFIG_DRAM_GET_BW	0x04
> +#define CONFIG_DRAM_GET_RATE	0x05
> +#define CONFIG_DRAM_CLR_IRQ	0x06
> +#define CONFIG_DRAM_SET_PARAM   0x07
> +
> +uint64_t sip_smc_ddr_init(void)
> +{
> +	return __invoke_psci_fn_smc(SIP_DDR_FREQ, 0,
> +				    0, CONFIG_DRAM_INIT);

I don't think that is legal to use. For one this function itself is declared 
static in the psci code - most likely for a specific reason.

And also if anything invoke_psci_fn would hold the correct pointer depending 
on the calling method.

But as said above, accessing psci static stuff is most likely wrong. Maybe the 
two psci people I've included can tell us how this is to be accessed.


Heiko

> +}
> +
> +uint64_t sip_smc_set_ddr_param(uint64_t param)
> +{
> +	return __invoke_psci_fn_smc(SIP_DDR_FREQ, param,
> +				    0, CONFIG_DRAM_SET_PARAM);
> +}
> +
> +uint64_t sip_smc_set_ddr_rate(uint64_t rate)
> +{
> +	return __invoke_psci_fn_smc(SIP_DDR_FREQ, rate, 0,
> +				    CONFIG_DRAM_SET_RATE);
> +}
> +
> +uint64_t sip_smc_get_ddr_rate(void)
> +{
> +	return __invoke_psci_fn_smc(SIP_DDR_FREQ, 0, 0, CONFIG_DRAM_GET_RATE);
> +}
> +
> +uint64_t sip_smc_clr_ddr_irq(void)
> +{
> +	return __invoke_psci_fn_smc(SIP_DDR_FREQ, 0, 0, CONFIG_DRAM_CLR_IRQ);
> +}
> +
> +uint64_t sip_smc_get_call_count(void)
> +{
> +	return __invoke_psci_fn_smc(SIP_SVC_CALL_COUNT, 0, 0, 0);
> +}



From lkp at intel.com  Fri Jul 22 14:32:42 2016
From: lkp at intel.com (kbuild test robot)
Date: Sat, 23 Jul 2016 05:32:42 +0800
Subject: [PATCH v3 1/7] firmware: rockchip: sip: Add rockchip SIP runtime
 service
In-Reply-To: <1469178440-4668-2-git-send-email-hl@rock-chips.com>
Message-ID: <201607230542.z1P2eELa%fengguang.wu@intel.com>

Hi,

[auto build test ERROR on next-20160722]
[also build test ERROR on v4.7-rc7]
[cannot apply to rockchip/for-next stable/master linux/master v4.7-rc7 v4.7-rc6 v4.7-rc5]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Lin-Huang/rk3399-support-ddr-frequency-scaling/20160722-173343
config: arm64-allmodconfig (attached as .config)
compiler: aarch64-linux-gnu-gcc (Debian 5.4.0-6) 5.4.0 20160609
reproduce:
        wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=arm64 

All errors (new ones prefixed by >>):

   drivers/built-in.o: In function `sip_smc_ddr_init':
>> binder.c:(.text+0x279074): undefined reference to `__invoke_psci_fn_smc'
   drivers/built-in.o: In function `sip_smc_set_ddr_param':
   binder.c:(.text+0x2790ac): undefined reference to `__invoke_psci_fn_smc'
   drivers/built-in.o: In function `sip_smc_set_ddr_rate':
   binder.c:(.text+0x2790ec): undefined reference to `__invoke_psci_fn_smc'
   drivers/built-in.o: In function `sip_smc_get_ddr_rate':
   binder.c:(.text+0x279124): undefined reference to `__invoke_psci_fn_smc'
   drivers/built-in.o: In function `sip_smc_clr_ddr_irq':
   binder.c:(.text+0x279154): undefined reference to `__invoke_psci_fn_smc'
   drivers/built-in.o:binder.c:(.text+0x279184): more undefined references to `__invoke_psci_fn_smc' follow

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
-------------- next part --------------
A non-text attachment was scrubbed...
Name: .config.gz
Type: application/octet-stream
Size: 51332 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160723/7c7845a2/attachment-0001.obj>

From groeck at google.com  Fri Jul 22 14:40:59 2016
From: groeck at google.com (Guenter Roeck)
Date: Fri, 22 Jul 2016 14:40:59 -0700
Subject: [v6 PATCH 1/6] extcon: Add Type-C and DP support
In-Reply-To: <5791E769.9060403@samsung.com>
References: <1469106808-28297-1-git-send-email-zyw@rock-chips.com>
 <CGME20160721131520epcas1p402684ff2569a23d7e5e011da9fb07667@epcas1p4.samsung.com>
 <1469106808-28297-2-git-send-email-zyw@rock-chips.com>
 <5791E769.9060403@samsung.com>
Message-ID: <CABXOdTfcxQ0W8wqyNstoYUf3XS6ehZRnESX418aUymTx_6cEbw@mail.gmail.com>

On Fri, Jul 22, 2016 at 2:29 AM, Chanwoo Choi <cw00.choi at samsung.com> wrote:
> Hi Chris,
>
> I'm sorry for late reply. I finished the first draft to support the extcon property.
> You can check the patches[1]. But, I need more time to test it. After tested it,
> I'll send the patches.
>
> [1] https://git.kernel.org/cgit/linux/kernel/git/chanwoo/extcon.git/log/?h=extcon-test
>
> Chanwoo Choi (4):
>   extcon: Add the extcon_type to group each connector into five category
>   extcon: Add the support for extcon property according to type of connector
>   extcon: Rename the extcon_set/get_state() to maintain the function naming pattern
>   extcon: Add the sync APIs to support the  notification for extcon property
>

Some additional feedback:

- IS_PROPERTY(), is_extcon_property_supported():
   The logic should probably be
        if (EXTCON_TYPE_##name & type) {
   and
        if (!(extcon_info[id].type & type)) {

  (logical & instead of logical | )

- extcon_get_property():
  - ret is not initialized.
  - case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX:
    misses a break statement

Guenter

> Chris Zhong (1):
>   extcon: Add EXTCON_DISP_DP and the property for USB Type-C
>
> Regards,
> Chanwoo Choi
>
> On 2016? 07? 21? 22:13, Chris Zhong wrote:
>> Add EXTCON_DISP_DP for the Display external connector. For Type-C
>> connector the DisplayPort can work as an Alternate Mode(VESA DisplayPort
>> Alt Mode on USB Type-C Standard). The Type-C support both normal and
>> flipped orientation, so add a property to extcon.
>>
>> Signe-off-by: Chris Zhong <zyw at rock-chips.com>
>>
>> Signed-off-by: Chris Zhong <zyw at rock-chips.com>
>> ---
>>
>> Changes in v6:
>> - move the EXTCON_PROP_TYPEC_POLARITY to EXTCON_TYPE_USB in _supported
>> Series-changes: 5
>> - support get property
>>
>> Changes in v5: None
>> Changes in v4: None
>> Changes in v3: None
>> Changes in v2: None
>> Changes in v1: None
>>
>>  drivers/extcon/extcon.c | 26 ++++++++++++++++++++++++++
>>  include/linux/extcon.h  | 13 +++++++++++++
>>  2 files changed, 39 insertions(+)
>>
>> diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c
>> index a1117db..f79b510 100644
>> --- a/drivers/extcon/extcon.c
>> +++ b/drivers/extcon/extcon.c
>> @@ -157,6 +157,11 @@ struct __extcon_info {
>>               .id = EXTCON_DISP_VGA,
>>               .name = "VGA",
>>       },
>> +     [EXTCON_DISP_DP] = {
>> +             .type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB,
>> +             .id = EXTCON_DISP_DP,
>> +             .name = "DP",
>> +     },
>>
>>       /* Miscellaneous external connector */
>>       [EXTCON_DOCK] = {
>> @@ -270,6 +275,7 @@ static bool is_extcon_property_supported(unsigned int id,
>>               switch (prop) {
>>               case EXTCON_PROP_USB_ID:
>>               case EXTCON_PROP_USB_VBUS:
>> +             case EXTCON_PROP_TYPEC_POLARITY:
>>                       return true;
>>               default:
>>                       break;
>> @@ -547,6 +553,26 @@ int extcon_get_cable_property(struct extcon_dev *edev, unsigned int id,
>>                               enum extcon_property prop,
>>                               union extcon_property_value *val)
>>  {
>> +     struct extcon_cable *cable;
>> +     int index;
>> +
>> +     if (!edev)
>> +             return -EINVAL;
>> +
>> +     /* Check the property whether is supported or not */
>> +     if (!is_extcon_property_supported(id, prop))
>> +             return -EINVAL;
>> +
>> +     /* Find the cable index of external connector by using id */
>> +     index = find_cable_index_by_id(edev, id);
>> +     if (index < 0)
>> +             return index;
>> +
>> +     /* Store the property value */
>> +     cable = &edev->cables[index];
>> +
>> +     val->intval = cable->propval[prop].intval;
>> +
>>       return 0;
>>  }
>>
>> diff --git a/include/linux/extcon.h b/include/linux/extcon.h
>> index f6f0a8d..50ef87f 100644
>> --- a/include/linux/extcon.h
>> +++ b/include/linux/extcon.h
>> @@ -77,6 +77,7 @@ enum extcon_type {
>>  #define EXTCON_DISP_MHL              41      /* Mobile High-Definition Link */
>>  #define EXTCON_DISP_DVI              42      /* Digital Visual Interface */
>>  #define EXTCON_DISP_VGA              43      /* Video Graphics Array */
>> +#define EXTCON_DISP_DP               44      /* DisplayPort */
>>
>>  /* Miscellaneous external connector */
>>  #define EXTCON_DOCK          60
>> @@ -108,9 +109,13 @@ enum extcon_property {
>>        * - EXTCON_PROP_USB_USB
>>        * @type:       integer (int value)
>>        * @value:      0 (low) or 1 (high)
>> +      * - EXTCON_PROP_TYPEC_POLARITY,
>> +      * @type:       integer (int value)
>> +      * @value:      0 (normal) or 1 (flip)
>>        */
>>       EXTCON_PROP_USB_ID = 0,
>>       EXTCON_PROP_USB_VBUS,
>> +     EXTCON_PROP_TYPEC_POLARITY,
>>
>>       /* Properties of EXTCON_TYPE_CHG. */
>>       /* Properties of EXTCON_TYPE_JACK. */
>> @@ -225,6 +230,14 @@ extern int extcon_get_cable_state_(struct extcon_dev *edev, unsigned int id);
>>  extern int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id,
>>                                  bool cable_state);
>>
>> +extern int extcon_get_cable_property(struct extcon_dev *edev, unsigned int id,
>> +                                  enum extcon_property prop,
>> +                                  union extcon_property_value *val);
>> +
>> +extern int extcon_set_cable_property(struct extcon_dev *edev, unsigned int id,
>> +                                  enum extcon_property prop,
>> +                                  union extcon_property_value val);
>> +
>>  /*
>>   * Following APIs are to monitor every action of a notifier.
>>   * Registrar gets notified for every external port of a connection device.
>>
>


From briannorris at chromium.org  Fri Jul 22 15:29:03 2016
From: briannorris at chromium.org (Brian Norris)
Date: Fri, 22 Jul 2016 15:29:03 -0700
Subject: [PATCH v7 2/2] PCI: Rockchip: Add Rockchip PCIe controller support
In-Reply-To: <1468802533-17699-2-git-send-email-shawn.lin@rock-chips.com>
References: <1468802533-17699-1-git-send-email-shawn.lin@rock-chips.com>
 <1468802533-17699-2-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <20160722222902.GB58807@google.com>

Hi Shawn,

On Mon, Jul 18, 2016 at 08:42:13AM +0800, Shawn Lin wrote:
> This patch adds Rockchip PCIe controller support found
> on RK3399 Soc platform.
> 
> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
> 
> ---
> 
> Changes in v7:
> - make it as a build-in driver
> - improve gen1/2 training timeout checking
> - only clear known interrupt
> - fix INTx for 0-base index

Also:
  - Fix infinite loop in legacy interrupt handler

(not that the changelog is that critical to get exactly right)

> 
> Changes in v6:
> - use "depends on PCI_MSI_IRQ_DOMAIN" suggested by Arnd
> 
> Changes in v5:
> - handle multiple pending INTx at the same time
>   suggested by Marc
> 
> Changes in v4:
> - address the comments from Brain
> 
> Changes in v3:
> - remove header file
> - remove struct msi_controller and move most of variables
>   of rockchip_pcie_port to become the local ones.
> - allow legacy int even if enabling MSI
> - drop regulator set voltage operation suggested by Doug
> 
> Changes in v2:
> - remove phy related stuff and call phy API
> - add new head file and define lots of macro to make
>   the code more readable
> - remove lots msi related code suggested by Marc
> - add IO window address translation
> - init_port and parse_dt reconstruction suggested by Bharat
> - improve wr_own_conf suggested by Arnd
> - make pcie as an interrupt controller and fix wrong int handler
>   suggested by Marc
> - remove PCI_PROBE_ONLY suggested by Lorenzo
> 
>  drivers/pci/host/Kconfig         |   11 +
>  drivers/pci/host/Makefile        |    1 +
>  drivers/pci/host/pcie-rockchip.c | 1233 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 1245 insertions(+)
>  create mode 100644 drivers/pci/host/pcie-rockchip.c
> 
[...]

Looks better to me:

Reviewed-by: Brian Norris <briannorris at chromium.org>


From cwchoi00 at gmail.com  Fri Jul 22 19:19:21 2016
From: cwchoi00 at gmail.com (Chanwoo Choi)
Date: Sat, 23 Jul 2016 11:19:21 +0900
Subject: [v6 PATCH 1/6] extcon: Add Type-C and DP support
In-Reply-To: <CABXOdTe-xtfjBKOPE_EuqBai835V6NiMzUUoJZJZUEuP415xqQ@mail.gmail.com>
References: <1469106808-28297-1-git-send-email-zyw@rock-chips.com>
 <CGME20160721131520epcas1p402684ff2569a23d7e5e011da9fb07667@epcas1p4.samsung.com>
 <1469106808-28297-2-git-send-email-zyw@rock-chips.com>
 <5791E769.9060403@samsung.com>
 <CABXOdTe-xtfjBKOPE_EuqBai835V6NiMzUUoJZJZUEuP415xqQ@mail.gmail.com>
Message-ID: <CAGTfZH1FvSYfjDijOGY9DCWD6-_OP1mA+-q2n17EWHXRY2Pgeg@mail.gmail.com>

Hi,

2016-07-23 3:21 GMT+09:00 Guenter Roeck <groeck at google.com>:
> Hi,
>
> On Fri, Jul 22, 2016 at 2:29 AM, Chanwoo Choi <cw00.choi at samsung.com> wrote:
>> Hi Chris,
>>
>> I'm sorry for late reply. I finished the first draft to support the extcon property.
>> You can check the patches[1]. But, I need more time to test it. After tested it,
>> I'll send the patches.
>>
>> [1] https://git.kernel.org/cgit/linux/kernel/git/chanwoo/extcon.git/log/?h=extcon-test
>>
>> Chanwoo Choi (4):
>>   extcon: Add the extcon_type to group each connector into five category
>>   extcon: Add the support for extcon property according to type of connector
>>   extcon: Rename the extcon_set/get_state() to maintain the function naming pattern
>>   extcon: Add the sync APIs to support the  notification for extcon property
>>
>> Chris Zhong (1):
>>   extcon: Add EXTCON_DISP_DP and the property for USB Type-C
>>
>
> Couple of comments:
>
> - In extcon.c, line 560: 'state' is unused.
>
> - extcon_set_property(edev, EXTCON_USB, EXTCON_USB_VBUS, 0);
>   does not work. As written, it has to be something like:

you shoud use the 'union extcon_property_valu' as below.

>
>   union extcon_property_value prop;
>   ...
>   prop.intval = vbus_state;
>   extcon_set_property(edev, EXTCON_USB, EXTCON_USB_VBUS, prop);
>
> - For USB, the state of EXTCON_USB, EXTCON_USB_HOST, and
> EXTCON_DISP_DP tend to change at the same time, together with the
> associated properties.
>   It might be desirable to have an equivalent of extcon_sync_all(edev);

Each external connector has the separate notifier independently.
All of operation in extcon should be done by each external connector unit.

I think that extcon_sync_all() is not appropriate. If the some extcon device
support the many connectors, maybe extcon send the un-needed notifications
for all connectors.

Also, extcon_sync_all(edev) is same operation with follwoing three
function calls:
extcon_sync(edev, EXTCON_USB);
extcon_sync(edev, EXTCON_USB_HOST);
extcon_sync(edev, EXTCON_DISP_DP);

Also,
EXTCON_DISP_DP has the both EXTCON_TYPE_USB and EXTCON_TYPE_DISP.
So, the extcon client driver is able to set the vbus property with
EXTCON_DISP_DP as following:
- extcon_set_property(edev, EXTCON_DISP_DP, EXTCON_USB_VBUS, prop)

Thanks,
Chanwoo Choi


From cwchoi00 at gmail.com  Fri Jul 22 19:20:14 2016
From: cwchoi00 at gmail.com (Chanwoo Choi)
Date: Sat, 23 Jul 2016 11:20:14 +0900
Subject: [v6 PATCH 1/6] extcon: Add Type-C and DP support
In-Reply-To: <CABXOdTfcxQ0W8wqyNstoYUf3XS6ehZRnESX418aUymTx_6cEbw@mail.gmail.com>
References: <1469106808-28297-1-git-send-email-zyw@rock-chips.com>
 <CGME20160721131520epcas1p402684ff2569a23d7e5e011da9fb07667@epcas1p4.samsung.com>
 <1469106808-28297-2-git-send-email-zyw@rock-chips.com>
 <5791E769.9060403@samsung.com>
 <CABXOdTfcxQ0W8wqyNstoYUf3XS6ehZRnESX418aUymTx_6cEbw@mail.gmail.com>
Message-ID: <CAGTfZH1WR+RBzo20GnGgeABb6mp9oQcAYJ_y5+VOZ75hrNan=A@mail.gmail.com>

Hi,

2016-07-23 6:40 GMT+09:00 Guenter Roeck <groeck at google.com>:
> On Fri, Jul 22, 2016 at 2:29 AM, Chanwoo Choi <cw00.choi at samsung.com> wrote:
>> Hi Chris,
>>
>> I'm sorry for late reply. I finished the first draft to support the extcon property.
>> You can check the patches[1]. But, I need more time to test it. After tested it,
>> I'll send the patches.
>>
>> [1] https://git.kernel.org/cgit/linux/kernel/git/chanwoo/extcon.git/log/?h=extcon-test
>>
>> Chanwoo Choi (4):
>>   extcon: Add the extcon_type to group each connector into five category
>>   extcon: Add the support for extcon property according to type of connector
>>   extcon: Rename the extcon_set/get_state() to maintain the function naming pattern
>>   extcon: Add the sync APIs to support the  notification for extcon property
>>
>
> Some additional feedback:
>
> - IS_PROPERTY(), is_extcon_property_supported():
>    The logic should probably be
>         if (EXTCON_TYPE_##name & type) {
>    and
>         if (!(extcon_info[id].type & type)) {
>
>   (logical & instead of logical | )
>
> - extcon_get_property():
>   - ret is not initialized.
>   - case EXTCON_PROP_DISP_MIN ... EXTCON_PROP_DISP_MAX:
>     misses a break statement

Thanks for report. I'll check it.

Thanks,
Chanwoo Choi

>
> Guenter
>
>> Chris Zhong (1):
>>   extcon: Add EXTCON_DISP_DP and the property for USB Type-C
>>
>> Regards,
>> Chanwoo Choi
>>
>> On 2016? 07? 21? 22:13, Chris Zhong wrote:
>>> Add EXTCON_DISP_DP for the Display external connector. For Type-C
>>> connector the DisplayPort can work as an Alternate Mode(VESA DisplayPort
>>> Alt Mode on USB Type-C Standard). The Type-C support both normal and
>>> flipped orientation, so add a property to extcon.
>>>
>>> Signe-off-by: Chris Zhong <zyw at rock-chips.com>
>>>
>>> Signed-off-by: Chris Zhong <zyw at rock-chips.com>
>>> ---
>>>
>>> Changes in v6:
>>> - move the EXTCON_PROP_TYPEC_POLARITY to EXTCON_TYPE_USB in _supported
>>> Series-changes: 5
>>> - support get property
>>>
>>> Changes in v5: None
>>> Changes in v4: None
>>> Changes in v3: None
>>> Changes in v2: None
>>> Changes in v1: None
>>>
>>>  drivers/extcon/extcon.c | 26 ++++++++++++++++++++++++++
>>>  include/linux/extcon.h  | 13 +++++++++++++
>>>  2 files changed, 39 insertions(+)
>>>
>>> diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c
>>> index a1117db..f79b510 100644
>>> --- a/drivers/extcon/extcon.c
>>> +++ b/drivers/extcon/extcon.c
>>> @@ -157,6 +157,11 @@ struct __extcon_info {
>>>               .id = EXTCON_DISP_VGA,
>>>               .name = "VGA",
>>>       },
>>> +     [EXTCON_DISP_DP] = {
>>> +             .type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB,
>>> +             .id = EXTCON_DISP_DP,
>>> +             .name = "DP",
>>> +     },
>>>
>>>       /* Miscellaneous external connector */
>>>       [EXTCON_DOCK] = {
>>> @@ -270,6 +275,7 @@ static bool is_extcon_property_supported(unsigned int id,
>>>               switch (prop) {
>>>               case EXTCON_PROP_USB_ID:
>>>               case EXTCON_PROP_USB_VBUS:
>>> +             case EXTCON_PROP_TYPEC_POLARITY:
>>>                       return true;
>>>               default:
>>>                       break;
>>> @@ -547,6 +553,26 @@ int extcon_get_cable_property(struct extcon_dev *edev, unsigned int id,
>>>                               enum extcon_property prop,
>>>                               union extcon_property_value *val)
>>>  {
>>> +     struct extcon_cable *cable;
>>> +     int index;
>>> +
>>> +     if (!edev)
>>> +             return -EINVAL;
>>> +
>>> +     /* Check the property whether is supported or not */
>>> +     if (!is_extcon_property_supported(id, prop))
>>> +             return -EINVAL;
>>> +
>>> +     /* Find the cable index of external connector by using id */
>>> +     index = find_cable_index_by_id(edev, id);
>>> +     if (index < 0)
>>> +             return index;
>>> +
>>> +     /* Store the property value */
>>> +     cable = &edev->cables[index];
>>> +
>>> +     val->intval = cable->propval[prop].intval;
>>> +
>>>       return 0;
>>>  }
>>>
>>> diff --git a/include/linux/extcon.h b/include/linux/extcon.h
>>> index f6f0a8d..50ef87f 100644
>>> --- a/include/linux/extcon.h
>>> +++ b/include/linux/extcon.h
>>> @@ -77,6 +77,7 @@ enum extcon_type {
>>>  #define EXTCON_DISP_MHL              41      /* Mobile High-Definition Link */
>>>  #define EXTCON_DISP_DVI              42      /* Digital Visual Interface */
>>>  #define EXTCON_DISP_VGA              43      /* Video Graphics Array */
>>> +#define EXTCON_DISP_DP               44      /* DisplayPort */
>>>
>>>  /* Miscellaneous external connector */
>>>  #define EXTCON_DOCK          60
>>> @@ -108,9 +109,13 @@ enum extcon_property {
>>>        * - EXTCON_PROP_USB_USB
>>>        * @type:       integer (int value)
>>>        * @value:      0 (low) or 1 (high)
>>> +      * - EXTCON_PROP_TYPEC_POLARITY,
>>> +      * @type:       integer (int value)
>>> +      * @value:      0 (normal) or 1 (flip)
>>>        */
>>>       EXTCON_PROP_USB_ID = 0,
>>>       EXTCON_PROP_USB_VBUS,
>>> +     EXTCON_PROP_TYPEC_POLARITY,
>>>
>>>       /* Properties of EXTCON_TYPE_CHG. */
>>>       /* Properties of EXTCON_TYPE_JACK. */
>>> @@ -225,6 +230,14 @@ extern int extcon_get_cable_state_(struct extcon_dev *edev, unsigned int id);
>>>  extern int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id,
>>>                                  bool cable_state);
>>>
>>> +extern int extcon_get_cable_property(struct extcon_dev *edev, unsigned int id,
>>> +                                  enum extcon_property prop,
>>> +                                  union extcon_property_value *val);
>>> +
>>> +extern int extcon_set_cable_property(struct extcon_dev *edev, unsigned int id,
>>> +                                  enum extcon_property prop,
>>> +                                  union extcon_property_value val);
>>> +
>>>  /*
>>>   * Following APIs are to monitor every action of a notifier.
>>>   * Registrar gets notified for every external port of a connection device.
>>>
>>


From dianders at chromium.org  Fri Jul 22 21:04:41 2016
From: dianders at chromium.org (Doug Anderson)
Date: Fri, 22 Jul 2016 21:04:41 -0700
Subject: [PATCH v4 2/4] drm/rockchip: add an common abstracted PSR driver
In-Reply-To: <1468469749-12636-1-git-send-email-ykk@rock-chips.com>
References: <1468469704-12440-1-git-send-email-ykk@rock-chips.com>
 <1468469749-12636-1-git-send-email-ykk@rock-chips.com>
Message-ID: <CAD=FV=U-9WoyZTyj0weDxVkZjQ7ESc+pwJXX=19JuGrOCrsKcQ@mail.gmail.com>

Yakir,

On Wed, Jul 13, 2016 at 9:15 PM, Yakir Yang <ykk at rock-chips.com> wrote:
> +static void psr_set_state(struct psr_drv *psr, enum psr_state state)
> +{
> +       mutex_lock(&psr->state_mutex);
> +
> +       if (psr->state == state) {
> +               mutex_unlock(&psr->state_mutex);
> +               return;
> +       }
> +
> +       psr->state = state;
> +       switch (state) {
> +       case PSR_ENABLE:
> +               psr->set(psr->encoder, true);
> +               break;
> +
> +       case PSR_DISABLE:
> +       case PSR_FLUSH:
> +               psr->set(psr->encoder, false);
> +               break;
> +       };
> +
> +       mutex_unlock(&psr->state_mutex);
> +}
> +
> +static void psr_flush_handler(unsigned long data)
> +{
> +       struct psr_drv *psr = (struct psr_drv *)data;
> +
> +       if (!psr || psr->state != PSR_FLUSH)
> +               return;
> +
> +       psr_set_state(psr, PSR_ENABLE);

As mentioned in a separate thread, this is probably not OK.
psr_set_state() grabs a mutex and that might sleep.  ...but
psr_flush_handler() is a timer.  I'm nearly certain that timers can't
sleep.

I believe this is the source of "sleeping function called from invalid
context" that I've seen at times.

-Doug


From ulf.hansson at linaro.org  Sat Jul 23 02:39:06 2016
From: ulf.hansson at linaro.org (Ulf Hansson)
Date: Sat, 23 Jul 2016 11:39:06 +0200
Subject: [PATCH 2/3] phy: rockchip-emmc: Be tolerant to card clock of 0 in
 power on
In-Reply-To: <CAD=FV=U_-8HJRjWa8ktT=7tmM5RB4OVnDce-Bv2teDibYfbCeA@mail.gmail.com>
References: <1467049167-14628-1-git-send-email-dianders@chromium.org>
 <1467049167-14628-3-git-send-email-dianders@chromium.org>
 <5773D1FD.8020601@ti.com>
 <CAD=FV=U_-8HJRjWa8ktT=7tmM5RB4OVnDce-Bv2teDibYfbCeA@mail.gmail.com>
Message-ID: <CAPDyKFqPHB2O=-hDxVda-h04WS9xYpJ3MyzH8bk5Fc4ogTfB5A@mail.gmail.com>

On 29 June 2016 at 17:18, Doug Anderson <dianders at chromium.org> wrote:
> Kishon,
>
> On Wed, Jun 29, 2016 at 6:49 AM, Kishon Vijay Abraham I <kishon at ti.com> wrote:
>> Hi,
>>
>> On Monday 27 June 2016 11:09 PM, Douglas Anderson wrote:
>>> It's possible that there are some reasons to turn the PHY on while the
>>> clock is 0.  In this case we just won't wait for the DLL to lock.
>>>
>>> This is a bit of a stopgap until we figure out exactly when we're
>>> supposed to wait for the DLL to lock and when we're supposed to power
>>> cycle the PHY.
>>>
>>> Note: this patch should help with suspend/resume where the system will
>>> try to turn the PHY back on when the clock is 0.
>>>
>>> Signed-off-by: Douglas Anderson <dianders at chromium.org>
>>> ---
>>>  drivers/phy/phy-rockchip-emmc.c | 59 ++++++++++++++++++++++++++---------------
>>>  1 file changed, 37 insertions(+), 22 deletions(-)
>>>
>>> diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c
>>> index 9dce958233a0..a2aa6aca7dec 100644
>>> --- a/drivers/phy/phy-rockchip-emmc.c
>>> +++ b/drivers/phy/phy-rockchip-emmc.c
>>> @@ -88,15 +88,36 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off)
>>>       unsigned int caldone;
>>>       unsigned int dllrdy;
>>>       unsigned int freqsel = PHYCTRL_FREQSEL_200M;
>>> +     unsigned long rate;
>>>       unsigned long timeout;
>>>
>>> -     if (rk_phy->emmcclk != NULL) {
>>> -             unsigned long rate = clk_get_rate(rk_phy->emmcclk);
>>> +     /*
>>> +      * Keep phyctrl_pdb and phyctrl_endll low to allow
>>> +      * initialization of CALIO state M/C DFFs
>>> +      */
>>> +     regmap_write(rk_phy->reg_base,
>>> +                  rk_phy->reg_offset + GRF_EMMCPHY_CON6,
>>> +                  HIWORD_UPDATE(PHYCTRL_PDB_PWR_OFF,
>>> +                                PHYCTRL_PDB_MASK,
>>> +                                PHYCTRL_PDB_SHIFT));
>>> +     regmap_write(rk_phy->reg_base,
>>> +                  rk_phy->reg_offset + GRF_EMMCPHY_CON6,
>>> +                  HIWORD_UPDATE(PHYCTRL_ENDLL_DISABLE,
>>> +                                PHYCTRL_ENDLL_MASK,
>>> +                                PHYCTRL_ENDLL_SHIFT));
>>> +
>>> +     /* Already finish power_off above */
>>> +     if (on_off == PHYCTRL_PDB_PWR_OFF)
>>> +             return 0;
>>> +
>>> +     rate = clk_get_rate(rk_phy->emmcclk);
>>> +
>>> +     if (rate != 0) {
>>>               unsigned long ideal_rate;
>>>               unsigned long diff;
>>>
>>>               switch (rate) {
>>> -             case 0 ... 74999999:
>>> +             case 1 ... 74999999:
>>>                       ideal_rate = 50000000;
>>>                       freqsel = PHYCTRL_FREQSEL_50M;
>>>                       break;
>>> @@ -127,25 +148,6 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off)
>>>       }
>>>
>>>       /*
>>> -      * Keep phyctrl_pdb and phyctrl_endll low to allow
>>> -      * initialization of CALIO state M/C DFFs
>>> -      */
>>> -     regmap_write(rk_phy->reg_base,
>>> -                  rk_phy->reg_offset + GRF_EMMCPHY_CON6,
>>> -                  HIWORD_UPDATE(PHYCTRL_PDB_PWR_OFF,
>>> -                                PHYCTRL_PDB_MASK,
>>> -                                PHYCTRL_PDB_SHIFT));
>>> -     regmap_write(rk_phy->reg_base,
>>> -                  rk_phy->reg_offset + GRF_EMMCPHY_CON6,
>>> -                  HIWORD_UPDATE(PHYCTRL_ENDLL_DISABLE,
>>> -                                PHYCTRL_ENDLL_MASK,
>>> -                                PHYCTRL_ENDLL_SHIFT));
>>> -
>>> -     /* Already finish power_off above */
>>> -     if (on_off == PHYCTRL_PDB_PWR_OFF)
>>> -             return 0;
>>> -
>>> -     /*
>>>        * According to the user manual, calpad calibration
>>>        * cycle takes more than 2us without the minimal recommended
>>>        * value, so we may need a little margin here
>>> @@ -183,6 +185,19 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off)
>>>                    HIWORD_UPDATE(PHYCTRL_ENDLL_ENABLE,
>>>                                  PHYCTRL_ENDLL_MASK,
>>>                                  PHYCTRL_ENDLL_SHIFT));
>>> +
>>> +     /*
>>> +      * We turned on the DLL even though the rate was 0 because we the
>>> +      * clock might be turned on later.  ...but we can't wait for the DLL
>>> +      * to lock when the rate is 0 because it will never lock with no
>>> +      * input clock.
>>> +      *
>>> +      * Technically we should be checking the lock later when the clock
>>> +      * is turned on, but for now we won't.
>>> +      */
>>> +     if (rate == 0)
>>> +             return 0;
>>
>> Why not return initially from rockchip_emmc_phy_power if the clock rate is '0'.
>> Are there other functions to lock the DLL apart from phy_power?
>
> Yeah, it's a big ugly right now.  This ugliness is really needed
> because of <https://patchwork.kernel.org/patch/9201035/> because:
>
> 1. We power on the PHY at probe time and the card clock is in an
> unknown state at that time.  It will be reported as 0 right now, but
> it may or may not actually be 0.
>
> 2. We don't have an easy way to call back into the PHY when we
> actually set the clock to a low rate (like 400kHz) for ID mode.
> Before this series I tried to power the PHY off and on for every clock
> change, but apparently that was causing problems.
>
>
> As talked about in <https://patchwork.kernel.org/patch/9201035/>, I
> think the right answer is to figure out how to get the common clock
> framework notifications to happen for the card clock and then remove
> the wholesale PHY power off / power on for every clock change.  The
> PHY itself can register for the clock change notifications and figure
> out how much or how little to do on every clock change.
>
> Unfortunately, as also discussed in the other patch, it's not trivial
> to do this because I think it requires surgery on the main SDHCI
> driver to change the way it deals with the card clock.  I'm not sure I
> have time for this delicate surgery right now and I'm hoping that
> perhaps Shawn will be able to help figure something out (maybe?) or I
> can try coming back to it later.
>
>
> In any case, I think a wholesale revert of my previous 150 MHz series
> probably puts us in a worse state than we started with, so I was just
> proposing reverting the one patch.  Once we do that, this PHY patch
> helps keep us in a sane state (keeps suspend/resume working).
>
>
> -Doug

Doug, Kishon,

Did you agree on how to move forward with this change?

Kind regards
Uffe


From ykk at rock-chips.com  Sat Jul 23 23:57:48 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Sun, 24 Jul 2016 14:57:48 +0800
Subject: [PATCH v5 3/4] drm/bridge: analogix_dp: add the PSR function support
In-Reply-To: <1469343437-27443-1-git-send-email-ykk@rock-chips.com>
References: <1469343437-27443-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1469343468-27740-1-git-send-email-ykk@rock-chips.com>

The full name of PSR is Panel Self Refresh, panel device could refresh
itself with the hardware framebuffer in panel, this would make lots of
sense to save the power consumption.

This patch have exported two symbols for platform driver to implement
the PSR function in hardware side:
- analogix_dp_active_psr()
- analogix_dp_inactive_psr()

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
Reviewed-by: Sean Paul <seanpaul at chromium.org>
---
Changes in v5:
- Add reviewed flag from Sean.

Changes in v4.1:
- Take use of existing edp_psr_vsc struct to swap HBx and DBx setting. (Sean)
- Remove PSR_VID_CRC_FLUSH setting analogix_dp_enable_psr_crc().
- Add comment about PBx magic numbers. (Sean)

Changes in v4:
- Downgrade the PSR version print message to debug level. (Sean)
- Return 'void' instead of 'int' in analogix_dp_enable_sink_psr(). (Sean)
- Delete the unused read dpcd operations in analogix_dp_enable_sink_psr(). (Sean)
- Delete the arbitrary usleep_range in analogix_dp_enable_psr_crc. (Sean).
- Clean up the hardcoded values in analogix_dp_send_psr_spd(). (Sean)
- Rename "active/inactive" to "enable/disable". (Sean, Dominik)
- Keep set the PSR_VID_CRC_FLUSH gate in analogix_dp_enable_psr_crc().

Changes in v3:
- split analogix_dp_enable_psr(), make it more clearly
    analogix_dp_detect_sink_psr()
    analogix_dp_enable_sink_psr()
- remove some nosie register setting comments

Changes in v2:
- introduce in v2, splite the common Analogix DP changes out

 drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 81 ++++++++++++++++++++++
 drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |  5 ++
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  | 51 ++++++++++++++
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h  | 34 +++++++++
 include/drm/bridge/analogix_dp.h                   |  3 +
 5 files changed, 174 insertions(+)

diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index 32715da..381b25e 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -97,6 +97,83 @@ static int analogix_dp_detect_hpd(struct analogix_dp_device *dp)
 	return 0;
 }
 
+int analogix_dp_enable_psr(struct device *dev)
+{
+	struct analogix_dp_device *dp = dev_get_drvdata(dev);
+	struct edp_vsc_psr psr_vsc;
+
+	if (!dp->psr_support)
+		return -EINVAL;
+
+	/* Prepare VSC packet as per EDP 1.4 spec, Table 6.9 */
+	memset(&psr_vsc, 0, sizeof(psr_vsc));
+	psr_vsc.sdp_header.HB0 = 0;
+	psr_vsc.sdp_header.HB1 = 0x7;
+	psr_vsc.sdp_header.HB2 = 0x2;
+	psr_vsc.sdp_header.HB3 = 0x8;
+
+	psr_vsc.DB0 = 0;
+	psr_vsc.DB1 = EDP_VSC_PSR_STATE_ACTIVE | EDP_VSC_PSR_CRC_VALUES_VALID;
+
+	analogix_dp_send_psr_spd(dp, &psr_vsc);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(analogix_dp_enable_psr);
+
+int analogix_dp_disable_psr(struct device *dev)
+{
+	struct analogix_dp_device *dp = dev_get_drvdata(dev);
+	struct edp_vsc_psr psr_vsc;
+
+	if (!dp->psr_support)
+		return -EINVAL;
+
+	/* Prepare VSC packet as per EDP 1.4 spec, Table 6.9 */
+	memset(&psr_vsc, 0, sizeof(psr_vsc));
+	psr_vsc.sdp_header.HB0 = 0;
+	psr_vsc.sdp_header.HB1 = 0x7;
+	psr_vsc.sdp_header.HB2 = 0x2;
+	psr_vsc.sdp_header.HB3 = 0x8;
+
+	psr_vsc.DB0 = 0;
+	psr_vsc.DB1 = 0;
+
+	analogix_dp_send_psr_spd(dp, &psr_vsc);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(analogix_dp_disable_psr);
+
+static bool analogix_dp_detect_sink_psr(struct analogix_dp_device *dp)
+{
+	unsigned char psr_version;
+
+	analogix_dp_read_byte_from_dpcd(dp, DP_PSR_SUPPORT, &psr_version);
+	dev_dbg(dp->dev, "Panel PSR version : %x\n", psr_version);
+
+	return (psr_version & DP_PSR_IS_SUPPORTED) ? true : false;
+}
+
+static void analogix_dp_enable_sink_psr(struct analogix_dp_device *dp)
+{
+	unsigned char psr_en;
+
+	/* Disable psr function */
+	analogix_dp_read_byte_from_dpcd(dp, DP_PSR_EN_CFG, &psr_en);
+	psr_en &= ~DP_PSR_ENABLE;
+	analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
+
+	/* Main-Link transmitter remains active during PSR active states */
+	psr_en = DP_PSR_MAIN_LINK_ACTIVE | DP_PSR_CRC_VERIFICATION;
+	analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
+
+	/* Enable psr function */
+	psr_en = DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE |
+		 DP_PSR_CRC_VERIFICATION;
+	analogix_dp_write_byte_to_dpcd(dp, DP_PSR_EN_CFG, psr_en);
+
+	analogix_dp_enable_psr_crc(dp);
+}
+
 static unsigned char analogix_dp_calc_edid_check_sum(unsigned char *edid_data)
 {
 	int i;
@@ -921,6 +998,10 @@ static void analogix_dp_commit(struct analogix_dp_device *dp)
 
 	/* Enable video */
 	analogix_dp_start_video(dp);
+
+	dp->psr_support = analogix_dp_detect_sink_psr(dp);
+	if (dp->psr_support)
+		analogix_dp_enable_sink_psr(dp);
 }
 
 int analogix_dp_get_modes(struct drm_connector *connector)
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
index b456380..17fbcee 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
@@ -177,6 +177,7 @@ struct analogix_dp_device {
 	int			hpd_gpio;
 	bool                    force_hpd;
 	unsigned char           edid[EDID_BLOCK_LENGTH * 2];
+	bool			psr_support;
 
 	struct analogix_dp_plat_data *plat_data;
 };
@@ -278,4 +279,8 @@ int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp);
 void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp);
 void analogix_dp_enable_scrambling(struct analogix_dp_device *dp);
 void analogix_dp_disable_scrambling(struct analogix_dp_device *dp);
+void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp);
+void analogix_dp_send_psr_spd(struct analogix_dp_device *dp,
+			      struct edp_vsc_psr *vsc);
+
 #endif /* _ANALOGIX_DP_CORE_H */
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
index 48030f0..52c1b6b 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
@@ -1322,3 +1322,54 @@ void analogix_dp_disable_scrambling(struct analogix_dp_device *dp)
 	reg |= SCRAMBLING_DISABLE;
 	writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
 }
+
+void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp)
+{
+	writel(PSR_VID_CRC_ENABLE, dp->reg_base + ANALOGIX_DP_CRC_CON);
+}
+
+void analogix_dp_send_psr_spd(struct analogix_dp_device *dp,
+			      struct edp_vsc_psr *vsc)
+{
+	unsigned int val;
+
+	/* don't send info frame */
+	val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+	val &= ~IF_EN;
+	writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+
+	/* configure single frame update mode */
+	writel(PSR_FRAME_UP_TYPE_BURST | PSR_CRC_SEL_HARDWARE,
+	       dp->reg_base + ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL);
+
+	/* configure VSC HB0~HB3 */
+	writel(vsc->sdp_header.HB0, dp->reg_base + ANALOGIX_DP_SPD_HB0);
+	writel(vsc->sdp_header.HB1, dp->reg_base + ANALOGIX_DP_SPD_HB1);
+	writel(vsc->sdp_header.HB2, dp->reg_base + ANALOGIX_DP_SPD_HB2);
+	writel(vsc->sdp_header.HB3, dp->reg_base + ANALOGIX_DP_SPD_HB3);
+
+	/* configure reused VSC PB0~PB3, magic number from vendor */
+	writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_PB0);
+	writel(0x16, dp->reg_base + ANALOGIX_DP_SPD_PB1);
+	writel(0xCE, dp->reg_base + ANALOGIX_DP_SPD_PB2);
+	writel(0x5D, dp->reg_base + ANALOGIX_DP_SPD_PB3);
+
+	/* configure DB0 / DB1 values */
+	writel(vsc->DB0, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB0);
+	writel(vsc->DB1, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB1);
+
+	/* set reuse spd inforframe */
+	val = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
+	val |= REUSE_SPD_EN;
+	writel(val, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
+
+	/* mark info frame update */
+	val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+	val = (val | IF_UP) & ~IF_EN;
+	writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+
+	/* send info frame */
+	val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+	val |= IF_EN;
+	writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+}
diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
index cdcc6c5..40200c6 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
@@ -22,6 +22,8 @@
 #define ANALOGIX_DP_VIDEO_CTL_8			0x3C
 #define ANALOGIX_DP_VIDEO_CTL_10		0x44
 
+#define ANALOGIX_DP_SPDIF_AUDIO_CTL_0		0xD8
+
 #define ANALOGIX_DP_PLL_REG_1			0xfc
 #define ANALOGIX_DP_PLL_REG_2			0x9e4
 #define ANALOGIX_DP_PLL_REG_3			0x9e8
@@ -30,6 +32,21 @@
 
 #define ANALOGIX_DP_PD				0x12c
 
+#define ANALOGIX_DP_IF_TYPE			0x244
+#define ANALOGIX_DP_IF_PKT_DB1			0x254
+#define ANALOGIX_DP_IF_PKT_DB2			0x258
+#define ANALOGIX_DP_SPD_HB0			0x2F8
+#define ANALOGIX_DP_SPD_HB1			0x2FC
+#define ANALOGIX_DP_SPD_HB2			0x300
+#define ANALOGIX_DP_SPD_HB3			0x304
+#define ANALOGIX_DP_SPD_PB0			0x308
+#define ANALOGIX_DP_SPD_PB1			0x30C
+#define ANALOGIX_DP_SPD_PB2			0x310
+#define ANALOGIX_DP_SPD_PB3			0x314
+#define ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL	0x318
+#define ANALOGIX_DP_VSC_SHADOW_DB0		0x31C
+#define ANALOGIX_DP_VSC_SHADOW_DB1		0x320
+
 #define ANALOGIX_DP_LANE_MAP			0x35C
 
 #define ANALOGIX_DP_ANALOG_CTL_1		0x370
@@ -103,6 +120,8 @@
 
 #define ANALOGIX_DP_SOC_GENERAL_CTL		0x800
 
+#define ANALOGIX_DP_CRC_CON			0x890
+
 /* ANALOGIX_DP_TX_SW_RESET */
 #define RESET_DP_TX				(0x1 << 0)
 
@@ -151,6 +170,7 @@
 #define VID_CHK_UPDATE_TYPE_SHIFT		(4)
 #define VID_CHK_UPDATE_TYPE_1			(0x1 << 4)
 #define VID_CHK_UPDATE_TYPE_0			(0x0 << 4)
+#define REUSE_SPD_EN				(0x1 << 3)
 
 /* ANALOGIX_DP_VIDEO_CTL_8 */
 #define VID_HRES_TH(x)				(((x) & 0xf) << 4)
@@ -167,6 +187,12 @@
 #define REF_CLK_27M				(0x0 << 0)
 #define REF_CLK_MASK				(0x1 << 0)
 
+/* ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL */
+#define PSR_FRAME_UP_TYPE_BURST			(0x1 << 0)
+#define PSR_FRAME_UP_TYPE_SINGLE		(0x0 << 0)
+#define PSR_CRC_SEL_HARDWARE			(0x1 << 1)
+#define PSR_CRC_SEL_MANUALLY			(0x0 << 1)
+
 /* ANALOGIX_DP_LANE_MAP */
 #define LANE3_MAP_LOGIC_LANE_0			(0x0 << 6)
 #define LANE3_MAP_LOGIC_LANE_1			(0x1 << 6)
@@ -376,4 +402,12 @@
 #define VIDEO_MODE_SLAVE_MODE			(0x1 << 0)
 #define VIDEO_MODE_MASTER_MODE			(0x0 << 0)
 
+/* ANALOGIX_DP_PKT_SEND_CTL */
+#define IF_UP					(0x1 << 4)
+#define IF_EN					(0x1 << 0)
+
+/* ANALOGIX_DP_CRC_CON */
+#define PSR_VID_CRC_FLUSH			(0x1 << 2)
+#define PSR_VID_CRC_ENABLE			(0x1 << 0)
+
 #endif /* _ANALOGIX_DP_REG_H */
diff --git a/include/drm/bridge/analogix_dp.h b/include/drm/bridge/analogix_dp.h
index 261b86d..9cd8838 100644
--- a/include/drm/bridge/analogix_dp.h
+++ b/include/drm/bridge/analogix_dp.h
@@ -38,6 +38,9 @@ struct analogix_dp_plat_data {
 			 struct drm_connector *);
 };
 
+int analogix_dp_enable_psr(struct device *dev);
+int analogix_dp_disable_psr(struct device *dev);
+
 int analogix_dp_resume(struct device *dev);
 int analogix_dp_suspend(struct device *dev);
 
-- 
1.9.1




From ykk at rock-chips.com  Sat Jul 23 23:57:44 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Sun, 24 Jul 2016 14:57:44 +0800
Subject: [PATCH v5 2/4] drm/rockchip: add an common abstracted PSR driver
In-Reply-To: <1469343437-27443-1-git-send-email-ykk@rock-chips.com>
References: <1469343437-27443-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1469343464-27580-1-git-send-email-ykk@rock-chips.com>

The PSR driver have exported four symbols for specific device driver, and
it's safe to call them in interrupt context:
- rockchip_drm_psr_register()
- rockchip_drm_psr_unregister()
- rockchip_drm_psr_enable()
- rockchip_drm_psr_disable()
- rockchip_drm_psr_flush()

Encoder driver should call the register/unregister interfaces to hook
itself into common PSR driver, encoder have implement the 'psr_set'
callback which use the set PSR state in hardware side.

Crtc driver would call the enable/disable interfaces when vblank is
enable/disable, after that the common PSR driver would call the encoder
registered callback to set the PSR state.

Fb driver would call the flush interface in 'fb->dirty' callback, this
helper function would force all PSR enabled encoders to exit from PSR
for 3 seconds.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
---
Changes in v5:
- Only keep 'psr_list_mutex' at register/unregister time.
- Remove unused global variables.
- Make list traversal safely at rockchip_drm_psr_unregister().
- Make psr_set_state() to a delay work, safe to call exported symbols at
  interrupt context.
- Remove the repeated psr delay work in vop driver.

Changes in v4:
- Tuck the global "psr_list" & "psr_list_mutex" in struct rockchip_drm_private. (Sean)
- Move the access of "psr->state" under "psr->state_mutex"'s protect. (Sean)
- Let "psr->state = PSR_FLUSH" under "psr->state_mutex"'s protect. (Sean)
- Collect psr_enable() and psr_disable() into psr_set_state()
- s/5\ second/PSR_FLUSH_TIMEOUT/ (Sean)
- Flush the psr callback in vop_crtc_disable(). (St?phane, reviewed in Google gerrit)
    [https://chromium-review.googlesource.com/#/c/349084/6/drivers/gpu/drm/rockchip/rockchip_drm_vop.c at 475]
- Add the missing file head with license. (St?phane, reviewed in Google gerrit)
    [https://chromium-review.googlesource.com/#/c/357563/1/drivers/gpu/drm/rockchip/rockchip_drm_psr.h at 3]

Changes in v3:
- split the psr flow into an common abstracted PSR driver
- implement the 'fb->dirty' callback function (Daniel)
- avoid to use notify to acqiure for vact event (Daniel)
- remove psr_active() callback which introduce in v2

Changes in v2: None

 drivers/gpu/drm/rockchip/Makefile           |   2 +-
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c |   3 +
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h |   3 +
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c  |  12 ++
 drivers/gpu/drm/rockchip/rockchip_drm_psr.c | 249 ++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_psr.h |  26 +++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c |   9 +
 7 files changed, 303 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.h

diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index 05d0713..9746365 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -3,7 +3,7 @@
 # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
 
 rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
-		rockchip_drm_gem.o rockchip_drm_vop.o
+		rockchip_drm_gem.o rockchip_drm_psr.o rockchip_drm_vop.o
 rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
 
 obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index a822d49..b43fe5d 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -156,6 +156,9 @@ static int rockchip_drm_bind(struct device *dev)
 
 	drm_dev->dev_private = private;
 
+	INIT_LIST_HEAD(&private->psr_list);
+	mutex_init(&private->psr_list_mutex);
+
 	drm_mode_config_init(drm_dev);
 
 	rockchip_drm_mode_config_init(drm_dev);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index 239b830..9c34c9e 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -61,6 +61,9 @@ struct rockchip_drm_private {
 	struct drm_gem_object *fbdev_bo;
 	const struct rockchip_crtc_funcs *crtc_funcs[ROCKCHIP_MAX_CRTC];
 	struct drm_atomic_state *state;
+
+	struct list_head psr_list;
+	struct mutex psr_list_mutex;
 };
 
 int rockchip_register_crtc_funcs(struct drm_crtc *crtc,
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
index 55c5273..ba45d9d 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
@@ -22,6 +22,7 @@
 #include "rockchip_drm_drv.h"
 #include "rockchip_drm_fb.h"
 #include "rockchip_drm_gem.h"
+#include "rockchip_drm_psr.h"
 
 #define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
 
@@ -63,9 +64,20 @@ static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
 				     rockchip_fb->obj[0], handle);
 }
 
+static int rockchip_drm_fb_dirty(struct drm_framebuffer *fb,
+				 struct drm_file *file,
+				 unsigned int flags, unsigned int color,
+				 struct drm_clip_rect *clips,
+				 unsigned int num_clips)
+{
+	rockchip_drm_psr_flush(fb->dev);
+	return 0;
+}
+
 static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
 	.destroy	= rockchip_drm_fb_destroy,
 	.create_handle	= rockchip_drm_fb_create_handle,
+	.dirty		= rockchip_drm_fb_dirty,
 };
 
 static struct rockchip_drm_fb *
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
new file mode 100644
index 0000000..a6d3bd25
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Yakir Yang <ykk at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_psr.h"
+
+#define PSR_FLUSH_TIMEOUT	msecs_to_jiffies(3000) /* 3 seconds */
+#define PSR_SET_DELAY_TIME	msecs_to_jiffies(10)
+
+enum psr_state {
+	PSR_FLUSH,
+	PSR_ENABLE,
+	PSR_DISABLE,
+};
+
+struct psr_drv {
+	struct list_head	list;
+	struct drm_encoder	*encoder;
+
+	enum psr_state		request_state;
+	enum psr_state		state;
+
+	struct delayed_work	state_work;
+
+	struct timer_list	flush_timer;
+
+	void (*set)(struct drm_encoder *encoder, bool enable);
+};
+
+static struct psr_drv *find_psr_by_crtc(struct drm_crtc *crtc)
+{
+	struct rockchip_drm_private *drm_drv = crtc->dev->dev_private;
+	struct psr_drv *psr;
+
+	list_for_each_entry(psr, &drm_drv->psr_list, list)
+		if (psr->encoder->crtc == crtc)
+			return psr;
+
+	return ERR_PTR(-ENODEV);
+}
+
+static void psr_state_work(struct work_struct *work)
+{
+	struct psr_drv *psr = container_of(work, typeof(*psr), state_work.work);
+	enum psr_state request_state = psr->request_state;
+
+	/*
+	 * Allowed finite state machine:
+	 *
+	 *   PSR_ENABLE  < = = = = = >  PSR_FLUSH
+	  *      | ^                        |
+	  *      | |                        |
+	  *      v |                        |
+	 *   PSR_DISABLE < - - - - - - - - -
+	 */
+
+	/* Forbid no state change */
+	if (request_state == psr->state)
+		return;
+
+	/* Forbid DISABLE change to FLUSH */
+	if (request_state == PSR_FLUSH && psr->state == PSR_DISABLE)
+		return;
+
+	/* Allow but no need hardware change, just need assign the state */
+	if (request_state == PSR_DISABLE && psr->state == PSR_FLUSH) {
+		psr->state = request_state;
+		return;
+	}
+
+	/* Only wrote in this work, no need lock protection */
+	psr->state = request_state;
+
+	/* Refact to hardware state change */
+	switch (request_state) {
+	case PSR_ENABLE:
+		psr->set(psr->encoder, true);
+		break;
+
+	case PSR_DISABLE:
+	case PSR_FLUSH:
+		psr->set(psr->encoder, false);
+		break;
+	}
+}
+
+static void psr_set_state(struct psr_drv *psr, enum psr_state state)
+{
+	psr->request_state = state;
+
+	schedule_delayed_work(&psr->state_work, PSR_SET_DELAY_TIME);
+}
+
+static void psr_flush_handler(unsigned long data)
+{
+	struct psr_drv *psr = (struct psr_drv *)data;
+
+	if (!psr)
+		return;
+
+	/* State changed between flush time, then keep it */
+	if (psr->request_state != PSR_FLUSH)
+		return;
+
+	psr_set_state(psr, PSR_ENABLE);
+}
+
+/**
+ * rockchip_drm_psr_enable - enable the encoder PSR which bind to given CRTC
+ * @crtc: CRTC to obtain the PSR encoder
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int rockchip_drm_psr_enable(struct drm_crtc *crtc)
+{
+	struct psr_drv *psr = find_psr_by_crtc(crtc);
+
+	if (IS_ERR(psr))
+		return PTR_ERR(psr);
+
+	psr_set_state(psr, PSR_ENABLE);
+	return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_psr_enable);
+
+/**
+ * rockchip_drm_psr_disable - disable the encoder PSR which bind to given CRTC
+ * @crtc: CRTC to obtain the PSR encoder
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int rockchip_drm_psr_disable(struct drm_crtc *crtc)
+{
+	struct psr_drv *psr = find_psr_by_crtc(crtc);
+
+	if (IS_ERR(psr))
+		return PTR_ERR(psr);
+
+	psr_set_state(psr, PSR_DISABLE);
+	return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_psr_disable);
+
+/**
+ * rockchip_drm_psr_flush - force to flush all registered PSR encoders
+ * @dev: drm device
+ *
+ * Disable the PSR function for all registered encoders, and then enable the
+ * PSR function back after PSR_FLUSH_TIMEOUT. If encoder PSR state have been
+ * changed during flush time, then keep the state no change after flush
+ * timeout.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+void rockchip_drm_psr_flush(struct drm_device *dev)
+{
+	struct rockchip_drm_private *drm_drv = dev->dev_private;
+	struct psr_drv *psr;
+
+	list_for_each_entry(psr, &drm_drv->psr_list, list) {
+		if (psr->request_state == PSR_DISABLE)
+			continue;
+
+		mod_timer(&psr->flush_timer,
+			  round_jiffies_up(jiffies + PSR_FLUSH_TIMEOUT));
+
+		psr_set_state(psr, PSR_FLUSH);
+	}
+}
+EXPORT_SYMBOL(rockchip_drm_psr_flush);
+
+/**
+ * rockchip_drm_psr_register - register encoder to psr driver
+ * @encoder: encoder that obtain the PSR function
+ * @psr_set: call back to set PSR state
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int rockchip_drm_psr_register(struct drm_encoder *encoder,
+			void (*psr_set)(struct drm_encoder *, bool enable))
+{
+	struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
+	struct psr_drv *psr;
+
+	if (!encoder || !psr_set)
+		return -EINVAL;
+
+	psr = kzalloc(sizeof(struct psr_drv), GFP_KERNEL);
+	if (!psr)
+		return -ENOMEM;
+
+	setup_timer(&psr->flush_timer, psr_flush_handler, (unsigned long)psr);
+
+	INIT_DELAYED_WORK(&psr->state_work, psr_state_work);
+
+	psr->state = PSR_DISABLE;
+	psr->encoder = encoder;
+	psr->set = psr_set;
+
+	mutex_lock(&drm_drv->psr_list_mutex);
+	list_add_tail(&psr->list, &drm_drv->psr_list);
+	mutex_unlock(&drm_drv->psr_list_mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_psr_register);
+
+/**
+ * rockchip_drm_psr_unregister - unregister encoder to psr driver
+ * @encoder: encoder that obtain the PSR function
+ * @psr_set: call back to set PSR state
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
+{
+	struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
+	struct psr_drv *psr, *n;
+
+	mutex_lock(&drm_drv->psr_list_mutex);
+	list_for_each_entry_safe(psr, n, &drm_drv->psr_list, list) {
+		if (psr->encoder == encoder) {
+			del_timer(&psr->flush_timer);
+			list_del(&psr->list);
+			kfree(psr);
+		}
+	}
+	mutex_unlock(&drm_drv->psr_list_mutex);
+}
+EXPORT_SYMBOL(rockchip_drm_psr_unregister);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.h b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
new file mode 100644
index 0000000..c35b688
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Yakir Yang <ykk at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef __ROCKCHIP_DRM_PSR___
+#define __ROCKCHIP_DRM_PSR___
+
+void rockchip_drm_psr_flush(struct drm_device *dev);
+int rockchip_drm_psr_enable(struct drm_crtc *crtc);
+int rockchip_drm_psr_disable(struct drm_crtc *crtc);
+
+int rockchip_drm_psr_register(struct drm_encoder *encoder,
+			void (*psr_set)(struct drm_encoder *, bool enable));
+void rockchip_drm_psr_unregister(struct drm_encoder *encoder);
+
+#endif /* __ROCKCHIP_DRM_PSR__ */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 90a7e62..ebfcb93 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -34,6 +34,7 @@
 #include "rockchip_drm_drv.h"
 #include "rockchip_drm_gem.h"
 #include "rockchip_drm_fb.h"
+#include "rockchip_drm_psr.h"
 #include "rockchip_drm_vop.h"
 
 #define __REG_SET_RELAXED(x, off, mask, shift, v, write_mask) \
@@ -629,6 +630,9 @@ static void vop_crtc_disable(struct drm_crtc *crtc)
 
 		crtc->state->event = NULL;
 	}
+
+	vop->psr_enabled = false;
+	INIT_DELAYED_WORK(&vop->psr_work, vop_psr_work);
 }
 
 static void vop_plane_destroy(struct drm_plane *plane)
@@ -937,6 +941,8 @@ static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
 
 	spin_unlock_irqrestore(&vop->irq_lock, flags);
 
+	rockchip_drm_psr_disable(&vop->crtc);
+
 	return 0;
 }
 
@@ -953,6 +959,8 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc)
 	VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0);
 
 	spin_unlock_irqrestore(&vop->irq_lock, flags);
+
+	rockchip_drm_psr_enable(&vop->crtc);
 }
 
 static void vop_crtc_wait_for_update(struct drm_crtc *crtc)
@@ -1607,6 +1615,7 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
 		return ret;
 
 	pm_runtime_enable(&pdev->dev);
+
 	return 0;
 }
 
-- 
1.9.1




From ykk at rock-chips.com  Sat Jul 23 23:57:52 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Sun, 24 Jul 2016 14:57:52 +0800
Subject: [PATCH v5 4/4] drm/rockchip: analogix_dp: implement PSR function
In-Reply-To: <1469343437-27443-1-git-send-email-ykk@rock-chips.com>
References: <1469343437-27443-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1469343472-27810-1-git-send-email-ykk@rock-chips.com>

Alway enable the PSR function for Rockchip analogix_dp driver. If panel
don't support PSR, then the core analogix_dp would ignore this setting.

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
Reviewed-by: Sean Paul <seanpaul at chromium.org>
---
Changes in v5:
- Add reviewed flag from Sean.

Changes in v4:
- Return 'void' instead of 'int' in analogix_dp_psr_set(). (Sean)
- Pull the 10ms delay time out into a #define. (Sean)
- Improved the code of analogix_dp_psr_work(). (Sean)
- Indented with spaces for new numbers in rockchip_dp_device struct. (St?phane, reviewed at Google gerrit)
    [https://chromium-review.googlesource.com/#/c/349085/33/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c at 83]

Changes in v3:
- split the common psr logic into a seperate driver, make this to a
  simple sub-psr device driver.

Changes in v2:
- remove vblank notify out (Daniel)
- create a psr_active() callback in vop data struct.

 drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 57 +++++++++++++++++++++++++
 1 file changed, 57 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
index e81e19a..aa916f4 100644
--- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
@@ -32,6 +32,7 @@
 #include <drm/bridge/analogix_dp.h>
 
 #include "rockchip_drm_drv.h"
+#include "rockchip_drm_psr.h"
 #include "rockchip_drm_vop.h"
 
 #define RK3288_GRF_SOC_CON6		0x25c
@@ -41,6 +42,9 @@
 
 #define HIWORD_UPDATE(val, mask)	(val | (mask) << 16)
 
+#define PSR_SET_DELAY_TIME		msecs_to_jiffies(10)
+#define PSR_WAIT_LINE_FLAG_TIMEOUT_MS	100
+
 #define to_dp(nm)	container_of(nm, struct rockchip_dp_device, nm)
 
 /**
@@ -68,11 +72,55 @@ struct rockchip_dp_device {
 	struct regmap            *grf;
 	struct reset_control     *rst;
 
+	struct delayed_work      psr_work;
+	unsigned int             psr_state;
+
 	const struct rockchip_dp_chip_data *data;
 
 	struct analogix_dp_plat_data plat_data;
 };
 
+static void analogix_dp_psr_set(struct drm_encoder *encoder, bool enabled)
+{
+	struct rockchip_dp_device *dp = to_dp(encoder);
+
+	dev_dbg(dp->dev, "%s PSR...\n", enabled ? "Entry" : "Exit");
+
+	if (enabled)
+		dp->psr_state = EDP_VSC_PSR_STATE_ACTIVE;
+	else
+		dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE;
+
+	schedule_delayed_work(&dp->psr_work, PSR_SET_DELAY_TIME);
+}
+
+static void analogix_dp_psr_work(struct work_struct *work)
+{
+	struct rockchip_dp_device *dp =
+				container_of(work, typeof(*dp), psr_work.work);
+	struct drm_crtc *crtc = dp->encoder.crtc;
+	int psr_state = dp->psr_state;
+	int vact_end;
+	int ret;
+
+	if (!crtc)
+		return;
+
+	vact_end = crtc->mode.vtotal - crtc->mode.vsync_start + crtc->mode.vdisplay;
+
+	ret = rockchip_drm_wait_line_flag(dp->encoder.crtc, vact_end,
+					  PSR_WAIT_LINE_FLAG_TIMEOUT_MS);
+	if (ret) {
+		dev_err(dp->dev, "line flag interrupt did not arrive\n");
+		return;
+	}
+
+	if (psr_state == EDP_VSC_PSR_STATE_ACTIVE)
+		analogix_dp_enable_psr(dp->dev);
+	else
+		analogix_dp_disable_psr(dp->dev);
+}
+
 static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
 {
 	reset_control_assert(dp->rst);
@@ -340,12 +388,21 @@ static int rockchip_dp_bind(struct device *dev, struct device *master,
 	dp->plat_data.power_off = rockchip_dp_powerdown;
 	dp->plat_data.get_modes = rockchip_dp_get_modes;
 
+	dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE;
+	INIT_DELAYED_WORK(&dp->psr_work, analogix_dp_psr_work);
+
+	rockchip_drm_psr_register(&dp->encoder, analogix_dp_psr_set);
+
 	return analogix_dp_bind(dev, dp->drm_dev, &dp->plat_data);
 }
 
 static void rockchip_dp_unbind(struct device *dev, struct device *master,
 			       void *data)
 {
+	struct rockchip_dp_device *dp = dev_get_drvdata(dev);
+
+	rockchip_drm_psr_unregister(&dp->encoder);
+
 	return analogix_dp_unbind(dev, master, data);
 }
 
-- 
1.9.1




From ykk at rock-chips.com  Sat Jul 23 23:57:17 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Sun, 24 Jul 2016 14:57:17 +0800
Subject: [PATCH v5 0/4] Add PSR function support for Analogix/Rockchip DP
Message-ID: <1469343437-27443-1-git-send-email-ykk@rock-chips.com>


The full name of PSR is Panel Self Refresh, panel device could refresh
itself with the hardware framebuffer in panel, this would make a lots
of sense to save the power consumption.

The v5 version have made the psr_set_state() to a delay work, which is
the biggest changes from v4.

The v3 version have splited an common PSR driver for Rockchip, which is
biggest changes from v2.

This thread is based on Mark's RK3399 VOP thread[0].

[0]: https://patchwork.kernel.org/patch/8886041/


Changes in v5:
- Add reviewed flag from Sean.
- Only keep 'psr_list_mutex' at register/unregister time.
- Remove unused global variables.
- Make list traversal safely at rockchip_drm_psr_unregister().
- Make psr_set_state() to a delay work, safe to call exported symbols at
  interrupt context.
- Remove the repeated psr delay work in vop driver.
- Add reviewed flag from Sean.
- Add reviewed flag from Sean.

Changes in v4.1:
- Remove the completion_done() check in irq handler (Sean)
- Take use of existing edp_psr_vsc struct to swap HBx and DBx setting. (Sean)
- Remove PSR_VID_CRC_FLUSH setting analogix_dp_enable_psr_crc().
- Add comment about PBx magic numbers. (Sean)

Changes in v4:
- Avoid the weird behavior in rockchip_drm_wait_line_flag(). (Sean)
- Make line_flag_num_x to an array. (Sean)
- Remove the unused vop_cfg_done() in vop_line_flag_irq_enable(). (Stephane, reviewed in Google gerrit)
    [https://chromium-review.googlesource.com/#/c/349084/33/drivers/gpu/drm/rockchip/rockchip_drm_vop.c at 466]
- Tuck the global "psr_list" & "psr_list_mutex" in struct rockchip_drm_private. (Sean)
- Move the access of "psr->state" under "psr->state_mutex"'s protect. (Sean)
- Let "psr->state = PSR_FLUSH" under "psr->state_mutex"'s protect. (Sean)
- Collect psr_enable() and psr_disable() into psr_set_state()
- s/5\ second/PSR_FLUSH_TIMEOUT/ (Sean)
- Flush the psr callback in vop_crtc_disable(). (St?phane, reviewed in Google gerrit)
    [https://chromium-review.googlesource.com/#/c/349084/6/drivers/gpu/drm/rockchip/rockchip_drm_vop.c at 475]
- Add the missing file head with license. (St?phane, reviewed in Google gerrit)
    [https://chromium-review.googlesource.com/#/c/357563/1/drivers/gpu/drm/rockchip/rockchip_drm_psr.h at 3]
- Downgrade the PSR version print message to debug level. (Sean)
- Return 'void' instead of 'int' in analogix_dp_enable_sink_psr(). (Sean)
- Delete the unused read dpcd operations in analogix_dp_enable_sink_psr(). (Sean)
- Delete the arbitrary usleep_range in analogix_dp_enable_psr_crc. (Sean).
- Clean up the hardcoded values in analogix_dp_send_psr_spd(). (Sean)
- Rename "active/inactive" to "enable/disable". (Sean, Dominik)
- Keep set the PSR_VID_CRC_FLUSH gate in analogix_dp_enable_psr_crc().
- Return 'void' instead of 'int' in analogix_dp_psr_set(). (Sean)
- Pull the 10ms delay time out into a #define. (Sean)
- Improved the code of analogix_dp_psr_work(). (Sean)
- Indented with spaces for new numbers in rockchip_dp_device struct. (St?phane, reviewed at Google gerrit)
    [https://chromium-review.googlesource.com/#/c/349085/33/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c at 83]

Changes in v3:
- Export the 'rockchip_drm_wait_line_flag' symbol, and document it.
- Add 'line_flag_num_0' for RK3288/RK3036
- Remove the notify for waiting line_flag event (Daniel)
- split the psr flow into an common abstracted PSR driver
- implement the 'fb->dirty' callback function (Daniel)
- avoid to use notify to acqiure for vact event (Daniel)
- remove psr_active() callback which introduce in v2
- split analogix_dp_enable_psr(), make it more clearly
    analogix_dp_detect_sink_psr()
    analogix_dp_enable_sink_psr()
- remove some nosie register setting comments
- split the common psr logic into a seperate driver, make this to a
  simple sub-psr device driver.

Changes in v2:
- Introduce in v2, split VOP line flag changes out
- introduce in v2, splite the common Analogix DP changes out
- remove vblank notify out (Daniel)
- create a psr_active() callback in vop data struct.

Yakir Yang (4):
  drm/rockchip: vop: export line flag function
  drm/rockchip: add an common abstracted PSR driver
  drm/bridge: analogix_dp: add the PSR function support
  drm/rockchip: analogix_dp: implement PSR function

 drivers/gpu/drm/bridge/analogix/analogix_dp_core.c |  81 +++++++
 drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |   5 +
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  |  51 +++++
 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h  |  34 +++
 drivers/gpu/drm/rockchip/Makefile                  |   2 +-
 drivers/gpu/drm/rockchip/analogix_dp-rockchip.c    |  57 +++++
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c        |   3 +
 drivers/gpu/drm/rockchip/rockchip_drm_drv.h        |   6 +
 drivers/gpu/drm/rockchip/rockchip_drm_fb.c         |  12 +
 drivers/gpu/drm/rockchip/rockchip_drm_psr.c        | 249 +++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_psr.h        |  26 +++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c        | 126 +++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h        |   2 +
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c        |   4 +
 include/drm/bridge/analogix_dp.h                   |   3 +
 15 files changed, 660 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.c
 create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.h

-- 
1.9.1




From ykk at rock-chips.com  Sat Jul 23 23:57:40 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Sun, 24 Jul 2016 14:57:40 +0800
Subject: [PATCH v5 1/4] drm/rockchip: vop: export line flag function
In-Reply-To: <1469343437-27443-1-git-send-email-ykk@rock-chips.com>
References: <1469343437-27443-1-git-send-email-ykk@rock-chips.com>
Message-ID: <1469343460-27521-1-git-send-email-ykk@rock-chips.com>

VOP have integrated a hardware counter which indicate the exact display
line that vop is scanning. And if we're interested in a specific line,
we can set the line number to vop line_flag register, and then vop would
generate a line_flag interrupt for it.

For example eDP PSR function is interested in the vertical blanking
period, then driver could set the line number to zero.

This patch have exported a symbol that allow other driver to listen the
line flag event with given timeout limit:
-  rockchip_drm_wait_line_flag()

Signed-off-by: Yakir Yang <ykk at rock-chips.com>
Reviewed-by: Sean Paul <seanpaul at chromium.org>
---
Changes in v5:
- Add reviewed flag from Sean.

Changes in v4.1:
- Remove the completion_done() check in irq handler (Sean)

Changes in v4:
- Avoid the weird behavior in rockchip_drm_wait_line_flag(). (Sean)
- Make line_flag_num_x to an array. (Sean)
- Remove the unused vop_cfg_done() in vop_line_flag_irq_enable(). (Stephane, reviewed in Google gerrit)
    [https://chromium-review.googlesource.com/#/c/349084/33/drivers/gpu/drm/rockchip/rockchip_drm_vop.c at 466]

Changes in v3:
- Export the 'rockchip_drm_wait_line_flag' symbol, and document it.
- Add 'line_flag_num_0' for RK3288/RK3036
- Remove the notify for waiting line_flag event (Daniel)

Changes in v2:
- Introduce in v2, split VOP line flag changes out

 drivers/gpu/drm/rockchip/rockchip_drm_drv.h |   3 +
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 117 ++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   2 +
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c |   4 +
 4 files changed, 126 insertions(+)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index ea39329..239b830 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -70,4 +70,7 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
 				   struct device *dev);
 void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
 				    struct device *dev);
+int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
+				unsigned int mstimeout);
+
 #endif /* _ROCKCHIP_DRM_DRV_H_ */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index bc08b50..90a7e62 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -121,6 +121,8 @@ struct vop {
 	/* protected by dev->event_lock */
 	struct drm_pending_vblank_event *event;
 
+	struct completion line_flag_completion;
+
 	const struct vop_data *data;
 
 	uint32_t *regsbak;
@@ -431,6 +433,71 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop)
 	spin_unlock_irqrestore(&vop->irq_lock, flags);
 }
 
+/*
+ * (1) each frame starts at the start of the Vsync pulse which is signaled by
+ *     the "FRAME_SYNC" interrupt.
+ * (2) the active data region of each frame ends at dsp_vact_end
+ * (3) we should program this same number (dsp_vact_end) into dsp_line_frag_num,
+ *      to get "LINE_FLAG" interrupt at the end of the active on screen data.
+ *
+ * VOP_INTR_CTRL0.dsp_line_frag_num = VOP_DSP_VACT_ST_END.dsp_vact_end
+ * Interrupts
+ * LINE_FLAG -------------------------------+
+ * FRAME_SYNC ----+                         |
+ *                |                         |
+ *                v                         v
+ *                | Vsync | Vbp |  Vactive  | Vfp |
+ *                        ^     ^           ^     ^
+ *                        |     |           |     |
+ *                        |     |           |     |
+ * dsp_vs_end ------------+     |           |     |   VOP_DSP_VTOTAL_VS_END
+ * dsp_vact_start --------------+           |     |   VOP_DSP_VACT_ST_END
+ * dsp_vact_end ----------------------------+     |   VOP_DSP_VACT_ST_END
+ * dsp_total -------------------------------------+   VOP_DSP_VTOTAL_VS_END
+ */
+static bool vop_line_flag_irq_is_enabled(struct vop *vop)
+{
+	uint32_t line_flag_irq;
+	unsigned long flags;
+
+	spin_lock_irqsave(&vop->irq_lock, flags);
+
+	line_flag_irq = VOP_INTR_GET_TYPE(vop, enable, LINE_FLAG_INTR);
+
+	spin_unlock_irqrestore(&vop->irq_lock, flags);
+
+	return !!line_flag_irq;
+}
+
+static void vop_line_flag_irq_enable(struct vop *vop, int line_num)
+{
+	unsigned long flags;
+
+	if (WARN_ON(!vop->is_enabled))
+		return;
+
+	spin_lock_irqsave(&vop->irq_lock, flags);
+
+	VOP_CTRL_SET(vop, line_flag_num[0], line_num);
+	VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1);
+
+	spin_unlock_irqrestore(&vop->irq_lock, flags);
+}
+
+static void vop_line_flag_irq_disable(struct vop *vop)
+{
+	unsigned long flags;
+
+	if (WARN_ON(!vop->is_enabled))
+		return;
+
+	spin_lock_irqsave(&vop->irq_lock, flags);
+
+	VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 0);
+
+	spin_unlock_irqrestore(&vop->irq_lock, flags);
+}
+
 static void vop_enable(struct drm_crtc *crtc)
 {
 	struct vop *vop = to_vop(crtc);
@@ -1168,6 +1235,12 @@ static irqreturn_t vop_isr(int irq, void *data)
 		ret = IRQ_HANDLED;
 	}
 
+	if (active_irqs & LINE_FLAG_INTR) {
+		complete(&vop->line_flag_completion);
+		active_irqs &= ~LINE_FLAG_INTR;
+		ret = IRQ_HANDLED;
+	}
+
 	if (active_irqs & FS_INTR) {
 		drm_crtc_handle_vblank(crtc);
 		vop_handle_vblank(vop);
@@ -1266,6 +1339,7 @@ static int vop_create_crtc(struct vop *vop)
 
 	init_completion(&vop->dsp_hold_completion);
 	init_completion(&vop->wait_update_complete);
+	init_completion(&vop->line_flag_completion);
 	crtc->port = port;
 	rockchip_register_crtc_funcs(crtc, &private_crtc_funcs);
 
@@ -1422,6 +1496,49 @@ static void vop_win_init(struct vop *vop)
 	}
 }
 
+/**
+ * rockchip_drm_wait_line_flag - acqiure the give line flag event
+ * @crtc: CRTC to enable line flag
+ * @line_num: interested line number
+ * @mstimeout: millisecond for timeout
+ *
+ * Driver would hold here until the interested line flag interrupt have
+ * happened or timeout to wait.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
+				unsigned int mstimeout)
+{
+	struct vop *vop = to_vop(crtc);
+	unsigned long jiffies_left;
+
+	if (!crtc || !vop->is_enabled)
+		return -ENODEV;
+
+	if (line_num > crtc->mode.vtotal || mstimeout <= 0)
+		return -EINVAL;
+
+	if (vop_line_flag_irq_is_enabled(vop))
+		return -EBUSY;
+
+	reinit_completion(&vop->line_flag_completion);
+	vop_line_flag_irq_enable(vop, line_num);
+
+	jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion,
+						   msecs_to_jiffies(mstimeout));
+	vop_line_flag_irq_disable(vop);
+
+	if (jiffies_left == 0) {
+		dev_err(vop->dev, "Timeout waiting for IRQ\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_wait_line_flag);
+
 static int vop_bind(struct device *dev, struct device *master, void *data)
 {
 	struct platform_device *pdev = to_platform_device(dev);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index ff4f52e..1dbc526 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -61,6 +61,8 @@ struct vop_ctrl {
 	struct vop_reg hpost_st_end;
 	struct vop_reg vpost_st_end;
 
+	struct vop_reg line_flag_num[2];
+
 	struct vop_reg cfg_done;
 };
 
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index a21b13b..e932b1b 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -123,6 +123,7 @@ static const struct vop_ctrl rk3036_ctrl_data = {
 	.hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0),
 	.vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
 	.vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0),
+	.line_flag_num[0] = VOP_REG(RK3036_INT_STATUS, 0xfff, 12),
 	.cfg_done = VOP_REG(RK3036_REG_CFG_DONE, 0x1, 0),
 };
 
@@ -222,6 +223,7 @@ static const struct vop_ctrl rk3288_ctrl_data = {
 	.vact_st_end = VOP_REG(RK3288_DSP_VACT_ST_END, 0x1fff1fff, 0),
 	.hpost_st_end = VOP_REG(RK3288_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
 	.vpost_st_end = VOP_REG(RK3288_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
+	.line_flag_num[0] = VOP_REG(RK3288_INTR_CTRL0, 0x1fff, 12),
 	.cfg_done = VOP_REG(RK3288_REG_CFG_DONE, 0x1, 0),
 };
 
@@ -300,6 +302,8 @@ static const struct vop_ctrl rk3399_ctrl_data = {
 	.vact_st_end = VOP_REG(RK3399_DSP_VACT_ST_END, 0x1fff1fff, 0),
 	.hpost_st_end = VOP_REG(RK3399_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
 	.vpost_st_end = VOP_REG(RK3399_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
+	.line_flag_num[0] = VOP_REG(RK3399_LINE_FLAG, 0xffff, 0),
+	.line_flag_num[1] = VOP_REG(RK3399_LINE_FLAG, 0xffff, 16),
 	.cfg_done = VOP_REG_MASK(RK3399_REG_CFG_DONE, 0x1, 0),
 };
 
-- 
1.9.1




From ykk at rock-chips.com  Sun Jul 24 00:08:50 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Sun, 24 Jul 2016 15:08:50 +0800
Subject: [PATCH v4 2/4] drm/rockchip: add an common abstracted PSR driver
In-Reply-To: <CAD=FV=U-9WoyZTyj0weDxVkZjQ7ESc+pwJXX=19JuGrOCrsKcQ@mail.gmail.com>
References: <1468469704-12440-1-git-send-email-ykk@rock-chips.com>
 <1468469749-12636-1-git-send-email-ykk@rock-chips.com>
 <CAD=FV=U-9WoyZTyj0weDxVkZjQ7ESc+pwJXX=19JuGrOCrsKcQ@mail.gmail.com>
Message-ID: <57946982.7050209@rock-chips.com>

Doug,

On 07/23/2016 12:04 PM, Doug Anderson wrote:
> Yakir,
>
> On Wed, Jul 13, 2016 at 9:15 PM, Yakir Yang <ykk at rock-chips.com> wrote:
>> +static void psr_set_state(struct psr_drv *psr, enum psr_state state)
>> +{
>> +       mutex_lock(&psr->state_mutex);
>> +
>> +       if (psr->state == state) {
>> +               mutex_unlock(&psr->state_mutex);
>> +               return;
>> +       }
>> +
>> +       psr->state = state;
>> +       switch (state) {
>> +       case PSR_ENABLE:
>> +               psr->set(psr->encoder, true);
>> +               break;
>> +
>> +       case PSR_DISABLE:
>> +       case PSR_FLUSH:
>> +               psr->set(psr->encoder, false);
>> +               break;
>> +       };
>> +
>> +       mutex_unlock(&psr->state_mutex);
>> +}
>> +
>> +static void psr_flush_handler(unsigned long data)
>> +{
>> +       struct psr_drv *psr = (struct psr_drv *)data;
>> +
>> +       if (!psr || psr->state != PSR_FLUSH)
>> +               return;
>> +
>> +       psr_set_state(psr, PSR_ENABLE);
> As mentioned in a separate thread, this is probably not OK.
> psr_set_state() grabs a mutex and that might sleep.  ...but
> psr_flush_handler() is a timer.  I'm nearly certain that timers can't
> sleep.
>
> I believe this is the source of "sleeping function called from invalid
> context" that I've seen at times.

Thanks for your reported, i have wrote a patch[0] to fix this problem in 
my v5. If you're happy to review, that would be great ;)

[0]: https://patchwork.kernel.org/patch/9244805/

- Yakir

>
> -Doug
>
>
>




From ykk at rock-chips.com  Sun Jul 24 00:14:05 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Sun, 24 Jul 2016 15:14:05 +0800
Subject: [PATCH v4 2/4] drm/rockchip: add an common abstracted PSR driver
In-Reply-To: <20160714151433.GB13857@seanpaul0.roam.corp.google.com>
References: <1468469704-12440-1-git-send-email-ykk@rock-chips.com>
 <1468469749-12636-1-git-send-email-ykk@rock-chips.com>
 <20160714151433.GB13857@seanpaul0.roam.corp.google.com>
Message-ID: <57946ABD.8030901@rock-chips.com>

Sean,

On 07/14/2016 11:14 PM, Sean Paul wrote:
> On Thu, Jul 14, 2016 at 12:15:49PM +0800, Yakir Yang wrote:
>> The PSR driver have exported four symbols for specific device driver:
>> - rockchip_drm_psr_register()
>> - rockchip_drm_psr_unregister()
>> - rockchip_drm_psr_enable()
>> - rockchip_drm_psr_disable()
>> - rockchip_drm_psr_flush()
>>
>> Encoder driver should call the register/unregister interfaces to hook
>> itself into common PSR driver, encoder have implement the 'psr_set'
>> callback which use the set PSR state in hardware side.
>>
>> Crtc driver would call the enable/disable interfaces when vblank is
>> enable/disable, after that the common PSR driver would call the encoder
>> registered callback to set the PSR state.
>>
>> Fb driver would call the flush interface in 'fb->dirty' callback, this
>> helper function would force all PSR enabled encoders to exit from PSR
>> for 3 seconds.
>>
>> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
>
> I still don't think it's a good idea to pull this out into a separate PSR
> driver, but I suppose we can integrate it at a later time if it becomes
> cumbersome.
>
> Reviewed-by: Sean Paul <seanpaul at chromium.org>

In order to make it safely to call those symbols at interrupt context, i 
have made lots of changes about this patch. It's not suitable to take 
your reviewed flag at the v5, if you're happy to review the new one [0], 
that would be very nice  :-D

[0]: https://patchwork.kernel.org/patch/9244805/

Thanks,
- Yakir


>
>> ---
>> Changes in v4:
>> - Tuck the global "psr_list" & "psr_list_mutex" in struct rockchip_drm_private. (Sean)
>> - Move the access of "psr->state" under "psr->state_mutex"'s protect. (Sean)
>> - Let "psr->state = PSR_FLUSH" under "psr->state_mutex"'s protect. (Sean)
>> - Collect psr_enable() and psr_disable() into psr_set_state()
>> - s/5\ second/PSR_FLUSH_TIMEOUT/ (Sean)
>> - Flush the psr callback in vop_crtc_disable(). (St?phane, reviewed in Google gerrit)
>>      [https://chromium-review.googlesource.com/#/c/349084/6/drivers/gpu/drm/rockchip/rockchip_drm_vop.c at 475]
>> - Add the missing file head with license. (St?phane, reviewed in Google gerrit)
>>      [https://chromium-review.googlesource.com/#/c/357563/1/drivers/gpu/drm/rockchip/rockchip_drm_psr.h at 3]
>>
>> Changes in v3:
>> - split the psr flow into an common abstracted PSR driver
>> - implement the 'fb->dirty' callback function (Daniel)
>> - avoid to use notify to acqiure for vact event (Daniel)
>> - remove psr_active() callback which introduce in v2
>>
>> Changes in v2: None
>>
>>   drivers/gpu/drm/rockchip/Makefile           |   2 +-
>>   drivers/gpu/drm/rockchip/rockchip_drm_drv.c |   4 +
>>   drivers/gpu/drm/rockchip/rockchip_drm_drv.h |   3 +
>>   drivers/gpu/drm/rockchip/rockchip_drm_fb.c  |  12 ++
>>   drivers/gpu/drm/rockchip/rockchip_drm_psr.c | 223 ++++++++++++++++++++++++++++
>>   drivers/gpu/drm/rockchip/rockchip_drm_psr.h |  26 ++++
>>   drivers/gpu/drm/rockchip/rockchip_drm_vop.c |  29 ++++
>>   7 files changed, 298 insertions(+), 1 deletion(-)
>>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.c
>>   create mode 100644 drivers/gpu/drm/rockchip/rockchip_drm_psr.h
>>
>> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
>> index 05d0713..9746365 100644
>> --- a/drivers/gpu/drm/rockchip/Makefile
>> +++ b/drivers/gpu/drm/rockchip/Makefile
>> @@ -3,7 +3,7 @@
>>   # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
>>   
>>   rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
>> -		rockchip_drm_gem.o rockchip_drm_vop.o
>> +		rockchip_drm_gem.o rockchip_drm_psr.o rockchip_drm_vop.o
>>   rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
>>   
>>   obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
>> index d665fb0..26c12b3 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
>> @@ -156,6 +156,9 @@ static int rockchip_drm_bind(struct device *dev)
>>   
>>   	drm_dev->dev_private = private;
>>   
>> +	INIT_LIST_HEAD(&private->psr_list);
>> +	mutex_init(&private->psr_list_mutex);
>> +
>>   	drm_mode_config_init(drm_dev);
>>   
>>   	rockchip_drm_mode_config_init(drm_dev);
>> @@ -218,6 +221,7 @@ static int rockchip_drm_bind(struct device *dev)
>>   
>>   	if (is_support_iommu)
>>   		arm_iommu_release_mapping(mapping);
>> +
>>   	return 0;
>>   err_fbdev_fini:
>>   	rockchip_drm_fbdev_fini(drm_dev);
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>> index 239b830..9c34c9e 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>> @@ -61,6 +61,9 @@ struct rockchip_drm_private {
>>   	struct drm_gem_object *fbdev_bo;
>>   	const struct rockchip_crtc_funcs *crtc_funcs[ROCKCHIP_MAX_CRTC];
>>   	struct drm_atomic_state *state;
>> +
>> +	struct list_head psr_list;
>> +	struct mutex psr_list_mutex;
>>   };
>>   
>>   int rockchip_register_crtc_funcs(struct drm_crtc *crtc,
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>> index 20f12bc..36afd9c 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
>> @@ -21,6 +21,7 @@
>>   
>>   #include "rockchip_drm_drv.h"
>>   #include "rockchip_drm_gem.h"
>> +#include "rockchip_drm_psr.h"
>>   
>>   #define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
>>   
>> @@ -66,9 +67,20 @@ static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
>>   				     rockchip_fb->obj[0], handle);
>>   }
>>   
>> +static int rockchip_drm_fb_dirty(struct drm_framebuffer *fb,
>> +				 struct drm_file *file,
>> +				 unsigned int flags, unsigned int color,
>> +				 struct drm_clip_rect *clips,
>> +				 unsigned int num_clips)
>> +{
>> +	rockchip_drm_psr_flush(fb->dev);
>> +	return 0;
>> +}
>> +
>>   static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
>>   	.destroy	= rockchip_drm_fb_destroy,
>>   	.create_handle	= rockchip_drm_fb_create_handle,
>> +	.dirty		= rockchip_drm_fb_dirty,
>>   };
>>   
>>   static struct rockchip_drm_fb *
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
>> new file mode 100644
>> index 0000000..e79ea18
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
>> @@ -0,0 +1,223 @@
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + * Author: Yakir Yang <ykk at rock-chips.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * 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 <drm/drmP.h>
>> +#include <drm/drm_crtc_helper.h>
>> +
>> +#include "rockchip_drm_drv.h"
>> +#include "rockchip_drm_psr.h"
>> +
>> +#define PSR_FLUSH_TIMEOUT	msecs_to_jiffies(3000) /* 3 seconds */
>> +
>> +static LIST_HEAD(psr_list);
>> +static DEFINE_MUTEX(psr_list_mutex);
>> +
>> +enum psr_state {
>> +	PSR_FLUSH,
>> +	PSR_ENABLE,
>> +	PSR_DISABLE,
>> +};
>> +
>> +struct psr_drv {
>> +	struct list_head list;
>> +	enum psr_state state;
>> +	struct mutex state_mutex;
>> +
>> +	struct timer_list flush_timer;
>> +
>> +	struct drm_encoder *encoder;
>> +	void (*set)(struct drm_encoder *encoder, bool enable);
>> +};
>> +
>> +static struct psr_drv *find_psr_by_crtc(struct drm_crtc *crtc)
>> +{
>> +	struct rockchip_drm_private *drm_drv = crtc->dev->dev_private;
>> +	struct psr_drv *psr;
>> +
>> +	mutex_lock(&drm_drv->psr_list_mutex);
>> +	list_for_each_entry(psr, &drm_drv->psr_list, list) {
>> +		if (psr->encoder->crtc == crtc) {
>> +			mutex_unlock(&drm_drv->psr_list_mutex);
>> +			return psr;
>> +		}
>> +	}
>> +	mutex_unlock(&drm_drv->psr_list_mutex);
>> +
>> +	return ERR_PTR(-ENODEV);
>> +}
>> +
>> +static void psr_set_state(struct psr_drv *psr, enum psr_state state)
>> +{
>> +	mutex_lock(&psr->state_mutex);
>> +
>> +	if (psr->state == state) {
>> +		mutex_unlock(&psr->state_mutex);
>> +		return;
>> +	}
>> +
>> +	psr->state = state;
>> +	switch (state) {
>> +	case PSR_ENABLE:
>> +		psr->set(psr->encoder, true);
>> +		break;
>> +
>> +	case PSR_DISABLE:
>> +	case PSR_FLUSH:
>> +		psr->set(psr->encoder, false);
>> +		break;
>> +	};
>> +
>> +	mutex_unlock(&psr->state_mutex);
>> +}
>> +
>> +static void psr_flush_handler(unsigned long data)
>> +{
>> +	struct psr_drv *psr = (struct psr_drv *)data;
>> +
>> +	if (!psr || psr->state != PSR_FLUSH)
>> +		return;
>> +
>> +	psr_set_state(psr, PSR_ENABLE);
>> +}
>> +
>> +/**
>> + * rockchip_drm_psr_enable - enable the encoder PSR which bind to given CRTC
>> + * @crtc: CRTC to obtain the PSR encoder
>> + *
>> + * Returns:
>> + * Zero on success, negative errno on failure.
>> + */
>> +int rockchip_drm_psr_enable(struct drm_crtc *crtc)
>> +{
>> +	struct psr_drv *psr = find_psr_by_crtc(crtc);
>> +
>> +	if (IS_ERR(psr))
>> +		return PTR_ERR(psr);
>> +
>> +	psr_set_state(psr, PSR_ENABLE);
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL(rockchip_drm_psr_enable);
>> +
>> +/**
>> + * rockchip_drm_psr_disable - disable the encoder PSR which bind to given CRTC
>> + * @crtc: CRTC to obtain the PSR encoder
>> + *
>> + * Returns:
>> + * Zero on success, negative errno on failure.
>> + */
>> +int rockchip_drm_psr_disable(struct drm_crtc *crtc)
>> +{
>> +	struct psr_drv *psr = find_psr_by_crtc(crtc);
>> +
>> +	if (IS_ERR(psr))
>> +		return PTR_ERR(psr);
>> +
>> +	psr_set_state(psr, PSR_DISABLE);
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL(rockchip_drm_psr_disable);
>> +
>> +/**
>> + * rockchip_drm_psr_flush - force to flush all registered PSR encoders
>> + * @dev: drm device
>> + *
>> + * Disable the PSR function for all registered encoders, and then enable the
>> + * PSR function back after PSR_FLUSH_TIMEOUT. If encoder PSR state have been
>> + * changed during flush time, then keep the state no change after flush
>> + * timeout.
>> + *
>> + * Returns:
>> + * Zero on success, negative errno on failure.
>> + */
>> +void rockchip_drm_psr_flush(struct drm_device *dev)
>> +{
>> +	struct rockchip_drm_private *drm_drv = dev->dev_private;
>> +	struct psr_drv *psr;
>> +
>> +	mutex_lock(&drm_drv->psr_list_mutex);
>> +	list_for_each_entry(psr, &drm_drv->psr_list, list) {
>> +		if (psr->state == PSR_DISABLE)
>> +			continue;
>> +
>> +		mod_timer(&psr->flush_timer,
>> +			  round_jiffies_up(jiffies + PSR_FLUSH_TIMEOUT));
>> +
>> +		psr_set_state(psr, PSR_FLUSH);
>> +	}
>> +	mutex_unlock(&drm_drv->psr_list_mutex);
>> +}
>> +EXPORT_SYMBOL(rockchip_drm_psr_flush);
>> +
>> +/**
>> + * rockchip_drm_psr_register - register encoder to psr driver
>> + * @encoder: encoder that obtain the PSR function
>> + * @psr_set: call back to set PSR state
>> + *
>> + * Returns:
>> + * Zero on success, negative errno on failure.
>> + */
>> +int rockchip_drm_psr_register(struct drm_encoder *encoder,
>> +			void (*psr_set)(struct drm_encoder *, bool enable))
>> +{
>> +	struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
>> +	struct psr_drv *psr;
>> +
>> +	if (!encoder || !psr_set)
>> +		return -EINVAL;
>> +
>> +	psr = kzalloc(sizeof(struct psr_drv), GFP_KERNEL);
>> +	if (!psr)
>> +		return -ENOMEM;
>> +
>> +	setup_timer(&psr->flush_timer, psr_flush_handler, (unsigned long)psr);
>> +
>> +	mutex_init(&psr->state_mutex);
>> +
>> +	psr->state = PSR_DISABLE;
>> +	psr->encoder = encoder;
>> +	psr->set = psr_set;
>> +
>> +	mutex_lock(&drm_drv->psr_list_mutex);
>> +	list_add_tail(&psr->list, &drm_drv->psr_list);
>> +	mutex_unlock(&drm_drv->psr_list_mutex);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL(rockchip_drm_psr_register);
>> +
>> +/**
>> + * rockchip_drm_psr_unregister - unregister encoder to psr driver
>> + * @encoder: encoder that obtain the PSR function
>> + * @psr_set: call back to set PSR state
>> + *
>> + * Returns:
>> + * Zero on success, negative errno on failure.
>> + */
>> +void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
>> +{
>> +	struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
>> +	struct psr_drv *psr;
>> +
>> +	mutex_lock(&drm_drv->psr_list_mutex);
>> +	list_for_each_entry(psr, &drm_drv->psr_list, list) {
>> +		if (psr->encoder == encoder) {
>> +			del_timer(&psr->flush_timer);
>> +			list_del(&psr->list);
>> +			kfree(psr);
>> +		}
>> +	}
>> +	mutex_unlock(&drm_drv->psr_list_mutex);
>> +}
>> +EXPORT_SYMBOL(rockchip_drm_psr_unregister);
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.h b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
>> new file mode 100644
>> index 0000000..c35b688
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
>> @@ -0,0 +1,26 @@
>> +/*
>> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
>> + * Author: Yakir Yang <ykk at rock-chips.com>
>> + *
>> + * This software is licensed under the terms of the GNU General Public
>> + * License version 2, as published by the Free Software Foundation, and
>> + * may be copied, distributed, and modified under those terms.
>> + *
>> + * 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.
>> + */
>> +
>> +#ifndef __ROCKCHIP_DRM_PSR___
>> +#define __ROCKCHIP_DRM_PSR___
>> +
>> +void rockchip_drm_psr_flush(struct drm_device *dev);
>> +int rockchip_drm_psr_enable(struct drm_crtc *crtc);
>> +int rockchip_drm_psr_disable(struct drm_crtc *crtc);
>> +
>> +int rockchip_drm_psr_register(struct drm_encoder *encoder,
>> +			void (*psr_set)(struct drm_encoder *, bool enable));
>> +void rockchip_drm_psr_unregister(struct drm_encoder *encoder);
>> +
>> +#endif /* __ROCKCHIP_DRM_PSR__ */
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> index 69d32f1..e702fb3 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
>> @@ -34,6 +34,7 @@
>>   #include "rockchip_drm_drv.h"
>>   #include "rockchip_drm_gem.h"
>>   #include "rockchip_drm_fb.h"
>> +#include "rockchip_drm_psr.h"
>>   #include "rockchip_drm_vop.h"
>>   
>>   #define __REG_SET_RELAXED(x, off, mask, shift, v, write_mask) \
>> @@ -87,6 +88,8 @@
>>   #define to_vop_win(x) container_of(x, struct vop_win, base)
>>   #define to_vop_plane_state(x) container_of(x, struct vop_plane_state, base)
>>   
>> +#define VOP_PSR_SET_DELAY_TIME		msecs_to_jiffies(10)
>> +
>>   struct vop_plane_state {
>>   	struct drm_plane_state base;
>>   	int format;
>> @@ -121,6 +124,9 @@ struct vop {
>>   	/* protected by dev->event_lock */
>>   	struct drm_pending_vblank_event *event;
>>   
>> +	bool psr_enabled;
>> +	struct delayed_work psr_work;
>> +
>>   	struct completion line_flag_completion;
>>   
>>   	const struct vop_data *data;
>> @@ -629,6 +635,9 @@ static void vop_crtc_disable(struct drm_crtc *crtc)
>>   
>>   		crtc->state->event = NULL;
>>   	}
>> +
>> +	vop->psr_enabled = false;
>> +	INIT_DELAYED_WORK(&vop->psr_work, vop_psr_work);
>>   }
>>   
>>   static void vop_plane_destroy(struct drm_plane *plane)
>> @@ -923,6 +932,16 @@ static const struct drm_plane_funcs vop_plane_funcs = {
>>   	.atomic_destroy_state = vop_atomic_plane_destroy_state,
>>   };
>>   
>> +static void vop_psr_work(struct work_struct *work)
>> +{
>> +	struct vop *vop = container_of(work, typeof(*vop), psr_work.work);
>> +
>> +	if (vop->psr_enabled)
>> +		rockchip_drm_psr_enable(&vop->crtc);
>> +	else
>> +		rockchip_drm_psr_disable(&vop->crtc);
>> +}
>> +
>>   static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
>>   {
>>   	struct vop *vop = to_vop(crtc);
>> @@ -937,6 +956,9 @@ static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
>>   
>>   	spin_unlock_irqrestore(&vop->irq_lock, flags);
>>   
>> +	vop->psr_enabled = false;
>> +	schedule_delayed_work(&vop->psr_work, VOP_PSR_SET_DELAY_TIME);
>> +
>>   	return 0;
>>   }
>>   
>> @@ -953,6 +975,9 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc)
>>   	VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 0);
>>   
>>   	spin_unlock_irqrestore(&vop->irq_lock, flags);
>> +
>> +	vop->psr_enabled = true;
>> +	schedule_delayed_work(&vop->psr_work, VOP_PSR_SET_DELAY_TIME);
>>   }
>>   
>>   static void vop_crtc_wait_for_update(struct drm_crtc *crtc)
>> @@ -1597,6 +1622,10 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
>>   		return ret;
>>   
>>   	pm_runtime_enable(&pdev->dev);
>> +
>> +	vop->psr_enabled = false;
>> +	INIT_DELAYED_WORK(&vop->psr_work, vop_psr_work);
>> +
>>   	return 0;
>>   }
>>   
>> -- 
>> 1.9.1
>>
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel at lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>
>




From hl at rock-chips.com  Sun Jul 24 00:54:09 2016
From: hl at rock-chips.com (hl)
Date: Sun, 24 Jul 2016 15:54:09 +0800
Subject: [PATCH v3 6/7] PM / devfreq: rockchip: add devfreq driver for
 rk3399 dmc
In-Reply-To: <CAP=VYLpBBXhMLdNyuYvtJ6XWn-yTof_-oEL7saB87KGY-84c9A@mail.gmail.com>
References: <1469178440-4668-1-git-send-email-hl@rock-chips.com>
 <1469178440-4668-7-git-send-email-hl@rock-chips.com>
 <CAP=VYLpBBXhMLdNyuYvtJ6XWn-yTof_-oEL7saB87KGY-84c9A@mail.gmail.com>
Message-ID: <57947421.7050006@rock-chips.com>

Hi Paul,

On 2016?07?23? 04:24, Paul Gortmaker wrote:
> On Fri, Jul 22, 2016 at 5:07 AM, Lin Huang <hl at rock-chips.com> wrote:
>> base on dfi result, we do ddr frequency scaling, register
>> dmc driver to devfreq framework, and use simple-ondemand
>> policy.
>>
>> Signed-off-by: Lin Huang <hl at rock-chips.com>
>> ---
>> Changes in v3:
>> - operate dram setting through sip call
>> - imporve set rate flow
>>
>> Changes in v2:
>> - None
>>
>> Changes in v1:
>> - move dfi controller to event
>> - fix set voltage sequence when set rate fail
>> - change Kconfig type from tristate to bool
>> - move unuse EXPORT_SYMBOL_GPL()
>>
>>   drivers/devfreq/Kconfig                 |   1 +
>>   drivers/devfreq/Makefile                |   1 +
>>   drivers/devfreq/rockchip/Kconfig        |  15 +
>>   drivers/devfreq/rockchip/Makefile       |   2 +
>>   drivers/devfreq/rockchip/rk3399_dmc.c   | 482 ++++++++++++++++++++++++++++++++
>>   drivers/devfreq/rockchip/rockchip_dmc.c | 143 ++++++++++
>>   include/soc/rockchip/rockchip_dmc.h     |  45 +++
>>   7 files changed, 689 insertions(+)
>>   create mode 100644 drivers/devfreq/rockchip/Kconfig
>>   create mode 100644 drivers/devfreq/rockchip/Makefile
>>   create mode 100644 drivers/devfreq/rockchip/rk3399_dmc.c
>>   create mode 100644 drivers/devfreq/rockchip/rockchip_dmc.c
>>   create mode 100644 include/soc/rockchip/rockchip_dmc.h
>>
>> diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig
>> index a5be56e..cb67246 100644
>> --- a/drivers/devfreq/Kconfig
>> +++ b/drivers/devfreq/Kconfig
>> @@ -101,5 +101,6 @@ config ARM_TEGRA_DEVFREQ
>>            operating frequencies and voltages with OPP support.
>>
>>   source "drivers/devfreq/event/Kconfig"
>> +source "drivers/devfreq/rockchip/Kconfig"
>>
>>   endif # PM_DEVFREQ
>> diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile
>> index 09f11d9..48e2ae6 100644
>> --- a/drivers/devfreq/Makefile
>> +++ b/drivers/devfreq/Makefile
>> @@ -9,6 +9,7 @@ obj-$(CONFIG_DEVFREQ_GOV_PASSIVE)       += governor_passive.o
>>   # DEVFREQ Drivers
>>   obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ)   += exynos-bus.o
>>   obj-$(CONFIG_ARM_TEGRA_DEVFREQ)                += tegra-devfreq.o
>> +obj-$(CONFIG_ARCH_ROCKCHIP)            += rockchip/
>>
>>   # DEVFREQ Event Drivers
>>   obj-$(CONFIG_PM_DEVFREQ_EVENT)         += event/
>> diff --git a/drivers/devfreq/rockchip/Kconfig b/drivers/devfreq/rockchip/Kconfig
>> new file mode 100644
>> index 0000000..7fb1cff
>> --- /dev/null
>> +++ b/drivers/devfreq/rockchip/Kconfig
>> @@ -0,0 +1,15 @@
>> +config ARM_ROCKCHIP_DMC_DEVFREQ
>> +       bool "ARM ROCKCHIP DMC DEVFREQ Driver"
>> +       depends on ARCH_ROCKCHIP
>> +       help
>> +         This adds the DEVFREQ driver framework for the rockchip dmc.
>> +
>> +config ARM_RK3399_DMC_DEVFREQ
>> +       bool "ARM RK3399 DMC DEVFREQ Driver"
> Since you are using bool Kconfigs for your driver, please do not use
> module.h or MODULE_<xyz> tags in your driver, and use the builtin
> register function.
>
> Alternatively if there really is  a use case for it to be a modular driver
> then use a tristate Kconfig.
Thanks for pointing it, will fix it next version.
> THanks,
> Paul.
> --
>
>> +       depends on ARM_ROCKCHIP_DMC_DEVFREQ
>> +       select PM_OPP
>> +       select DEVFREQ_GOV_SIMPLE_ONDEMAND
>> +       help
>> +          This adds the DEVFREQ driver for the RK3399 dmc. It sets the frequency
>> +          for the memory controller and reads the usage counts from hardware.
>> +
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip
>
>
>

-- 
Lin Huang




From hl at rock-chips.com  Sun Jul 24 01:00:35 2016
From: hl at rock-chips.com (hl)
Date: Sun, 24 Jul 2016 16:00:35 +0800
Subject: [PATCH v3 1/7] firmware: rockchip: sip: Add rockchip SIP runtime
 service
In-Reply-To: <1497407.KruMAzy5tb@diego>
References: <1469178440-4668-1-git-send-email-hl@rock-chips.com>
 <1469178440-4668-2-git-send-email-hl@rock-chips.com>
 <1497407.KruMAzy5tb@diego>
Message-ID: <579475A3.2040506@rock-chips.com>

Hi Heiko,

On 2016?07?23? 04:50, Heiko St?bner wrote:
> Hi again,
>
> one bigger thing I noticed only now.
>
> Am Freitag, 22. Juli 2016, 17:07:14 schrieben Sie:
>> diff --git a/drivers/firmware/rockchip_sip.c
>> b/drivers/firmware/rockchip_sip.c new file mode 100644
>> index 0000000..7756af9
>> --- /dev/null
>> +++ b/drivers/firmware/rockchip_sip.c
>> @@ -0,0 +1,64 @@
>> +/*
>> + * 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.
>> + *
>> + * Copyright (C) 2016 ARM Limited
>> + */
>> +#include <linux/errno.h>
>> +#include <linux/linkage.h>
>> +#include <linux/of.h>
>> +#include <linux/pm.h>
>> +#include <linux/printk.h>
>> +#include "rockchip_sip.h"
>> +
>> +typedef unsigned long (psci_fn)(unsigned long, unsigned long,
>> +				unsigned long, unsigned long);
>> +asmlinkage psci_fn __invoke_psci_fn_smc;
>> +
>> +#define CONFIG_DRAM_INIT	0x00
>> +#define CONFIG_DRAM_SET_RATE	0x01
>> +#define CONFIG_DRAM_ROUND_RATE	0x02
>> +#define CONFIG_DRAM_SET_AT_SR	0x03
>> +#define CONFIG_DRAM_GET_BW	0x04
>> +#define CONFIG_DRAM_GET_RATE	0x05
>> +#define CONFIG_DRAM_CLR_IRQ	0x06
>> +#define CONFIG_DRAM_SET_PARAM   0x07
>> +
>> +uint64_t sip_smc_ddr_init(void)
>> +{
>> +	return __invoke_psci_fn_smc(SIP_DDR_FREQ, 0,
>> +				    0, CONFIG_DRAM_INIT);
> I don't think that is legal to use. For one this function itself is declared
> static in the psci code - most likely for a specific reason.
>
> And also if anything invoke_psci_fn would hold the correct pointer depending
> on the calling method.
>
> But as said above, accessing psci static stuff is most likely wrong. Maybe the
> two psci people I've included can tell us how this is to be accessed.
>
   Thanks Heiko. Hope Sudeep Holla and Lorenzo Pieralisi can give me advice.
> Heiko
>
>> +}
>> +
>> +uint64_t sip_smc_set_ddr_param(uint64_t param)
>> +{
>> +	return __invoke_psci_fn_smc(SIP_DDR_FREQ, param,
>> +				    0, CONFIG_DRAM_SET_PARAM);
>> +}
>> +
>> +uint64_t sip_smc_set_ddr_rate(uint64_t rate)
>> +{
>> +	return __invoke_psci_fn_smc(SIP_DDR_FREQ, rate, 0,
>> +				    CONFIG_DRAM_SET_RATE);
>> +}
>> +
>> +uint64_t sip_smc_get_ddr_rate(void)
>> +{
>> +	return __invoke_psci_fn_smc(SIP_DDR_FREQ, 0, 0, CONFIG_DRAM_GET_RATE);
>> +}
>> +
>> +uint64_t sip_smc_clr_ddr_irq(void)
>> +{
>> +	return __invoke_psci_fn_smc(SIP_DDR_FREQ, 0, 0, CONFIG_DRAM_CLR_IRQ);
>> +}
>> +
>> +uint64_t sip_smc_get_call_count(void)
>> +{
>> +	return __invoke_psci_fn_smc(SIP_SVC_CALL_COUNT, 0, 0, 0);
>> +}
>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip
>
>
>

-- 
Lin Huang




From heiko at sntech.de  Sun Jul 24 02:09:02 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Sun, 24 Jul 2016 11:09:02 +0200
Subject: [PATCH v3 2/7] clk: rockchip: add new clock-type for the ddrclk
In-Reply-To: <1469178440-4668-3-git-send-email-hl@rock-chips.com>
References: <1469178440-4668-1-git-send-email-hl@rock-chips.com>
 <1469178440-4668-3-git-send-email-hl@rock-chips.com>
Message-ID: <1619904.zXvqDRdkgD@diego>

Am Freitag, 22. Juli 2016, 17:07:15 schrieb Lin Huang:
> @@ -488,6 +496,25 @@ struct rockchip_clk_branch {
>  		.child		= ch,				\
>  	}
> 
> +#define COMPOSITE_DDRC(_id, cname, pnames, f, mo, ms, mw, mf,	\
> +			 ds, dw, df)				\
> +	{							\
> +		.id		= _id,				\
> +		.branch_type	= branch_ddrc,			\
> +		.name		= cname,			\
> +		.parent_names	= pnames,			\
> +		.num_parents	= ARRAY_SIZE(pnames),		\
> +		.flags		= f,				\
> +		.muxdiv_offset	= mo,				\
> +		.mux_shift	= ms,				\
> +		.mux_width	= mw,				\
> +		.mux_flags	= mf,				\
> +		.div_shift	= ds,				\
> +		.div_width	= dw,				\
> +		.div_flags	= df,				\
> +		.gate_offset	= -1,				\
> +	}
> +

the mux and div-flags are not used in your clock type. Instead I think we could 
go a similar way as the inverter-type, having one flag type, which could for 
example encode the interface type ROCKCHIP_DDRCLK_SIP, etc.

Because right now there exist 3 types on how to set the ddr clk, SIP, an SCPI-
variant and through code in SRAM (rk3288), while the core clock type on the 
side of the CRU is pretty much the same.

And while it is definitly not necessary to do that now, I think the ddrclk-type 
can hold the other interfaces as well in the future.


Heiko


From William.wu at rock-chips.com  Sun Jul 24 09:05:15 2016
From: William.wu at rock-chips.com (William.wu)
Date: Mon, 25 Jul 2016 00:05:15 +0800
Subject: [PATCH v7 3/5] usb: dwc3: make usb2 phy utmi interface
 configurable in DT
In-Reply-To: <20160716225715.GA27724@rob-hp-laptop>
References: <1468486762-21960-1-git-send-email-william.wu@rock-chips.com>
 <1468486762-21960-4-git-send-email-william.wu@rock-chips.com>
 <20160716225715.GA27724@rob-hp-laptop>
Message-ID: <f9a11ec3-8a69-83d6-39c3-7c50439298d6@rock-chips.com>

Dear Rob,


On 2016/7/17 6:57, Rob Herring wrote:
> On Thu, Jul 14, 2016 at 04:59:20PM +0800, William Wu wrote:
>> Add snps,phyif-utmi-width devicetree property to configure
>> the UTMI+ PHY with an 8- or 16-bit interface. UTMI+ PHY
>> interface is a hardware property, and it's platform dependent.
>> Normally,the PHYIF can be configured during coreconsultant.
>             ^
> space
I'll fix it next patch, thanks:-)
>
>> But for some specific USB cores(e.g. rk3399 SoC DWC3), the
>> default PHYIF configuration value is fault, so we need to
>> reconfigure it by software.
>>
>> And refer to the DWC3 databook, the GUSB2PHYCFG.USBTRDTIM
>> must be set to the corresponding value according to the
>> UTMI+ PHY interface.
>>
>> Signed-off-by: William Wu <william.wu at rock-chips.com>
>> ---
>> Changes in v7:
>> - remove quirk and use only one property to configure utmi (Heiko, Rob Herring)
>>
>> Changes in v6:
>> - use '-' instead of '_' in dts (Rob Herring)
>>
>> Changes in v5:
>> - None
>>
>> Changes in v4:
>> - rebase on top of balbi testing/next, remove pdata (balbi)
>>
>> Changes in v3:
>> - None
>>
>> Changes in v2:
>> - add a quirk for phyif_utmi (balbi)
>>
>>   Documentation/devicetree/bindings/usb/dwc3.txt |  3 +++
>>   drivers/usb/dwc3/core.c                        | 25 +++++++++++++++++++++++++
>>   drivers/usb/dwc3/core.h                        | 10 ++++++++++
>>   3 files changed, 38 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt
>> index 020b0e9..00cc541 100644
>> --- a/Documentation/devicetree/bindings/usb/dwc3.txt
>> +++ b/Documentation/devicetree/bindings/usb/dwc3.txt
>> @@ -47,6 +47,9 @@ Optional properties:
>>    - snps,hird-threshold: HIRD threshold
>>    - snps,hsphy_interface: High-Speed PHY interface selection between "utmi" for
>>      UTMI+ and "ulpi" for ULPI when the DWC_USB3_HSPHY_INTERFACE has value 3.
>> + - snps,phyif-utmi-width: the value to configure the core to support a UTMI+ PHY
>> +			with an 8- or 16-bit interface. Value 8 select 8-bit
>> +			interface, value 16 select 16-bit interface.
> Is 'phy_type = "utmi_wide"' not the same as 16-bit width?
>
> Again, I think this should be common.
Yes? I agree with you. ?phy_type = "utmi_wide" really means 16-bit UTMI 
width.
Thanks very much for your rigorous check.

And according to Heiko's helpful suggestion?I double check the kernel 
code and
look over UTMI/UTMI+ spec?I confirm that there is already generic code in
drivers/usb/phy/of.c about utmi interface, 'phy_type = "utmi"' means 
8-bit interface,
and 'phy_type = "utmi_wide"' means 16-bit interface.

So I think I don't need to add a new dts property 
'snps,phyif-utmi-width' here?
but just use the?phy_type? property to confirm UTMI+ interface for dwc3.

>
> Rob
>
>
>





From William.wu at rock-chips.com  Sun Jul 24 09:24:26 2016
From: William.wu at rock-chips.com (William.wu)
Date: Mon, 25 Jul 2016 00:24:26 +0800
Subject: [PATCH v7 3/5] usb: dwc3: make usb2 phy utmi interface
 configurable in DT
In-Reply-To: <1738306.cydkc9Pb90@diego>
References: <1468486762-21960-1-git-send-email-william.wu@rock-chips.com>
 <1468486762-21960-4-git-send-email-william.wu@rock-chips.com>
 <20160716225715.GA27724@rob-hp-laptop> <1738306.cydkc9Pb90@diego>
Message-ID: <f3e38a34-3d7b-8187-0262-d41b23a3c4a9@rock-chips.com>

Dear Heiko,


On 2016/7/17 18:28, Heiko St?bner wrote:
> Am Samstag, 16. Juli 2016, 17:57:15 schrieb Rob Herring:
>> On Thu, Jul 14, 2016 at 04:59:20PM +0800, William Wu wrote:
>>> Add snps,phyif-utmi-width devicetree property to configure
>>> the UTMI+ PHY with an 8- or 16-bit interface. UTMI+ PHY
>>> interface is a hardware property, and it's platform dependent.
>>> Normally,the PHYIF can be configured during coreconsultant.
>>             ^
>> space
>>
>>> But for some specific USB cores(e.g. rk3399 SoC DWC3), the
>>> default PHYIF configuration value is fault, so we need to
>>> reconfigure it by software.
>>>
>>> And refer to the DWC3 databook, the GUSB2PHYCFG.USBTRDTIM
>>> must be set to the corresponding value according to the
>>> UTMI+ PHY interface.
>>>
>>> Signed-off-by: William Wu <william.wu at rock-chips.com>
>>> ---
>>> Changes in v7:
>>> - remove quirk and use only one property to configure utmi (Heiko, Rob
>>> Herring)
>>>
>>> Changes in v6:
>>> - use '-' instead of '_' in dts (Rob Herring)
>>>
>>> Changes in v5:
>>> - None
>>>
>>> Changes in v4:
>>> - rebase on top of balbi testing/next, remove pdata (balbi)
>>>
>>> Changes in v3:
>>> - None
>>>
>>> Changes in v2:
>>> - add a quirk for phyif_utmi (balbi)
>>>
>>>   Documentation/devicetree/bindings/usb/dwc3.txt |  3 +++
>>>   drivers/usb/dwc3/core.c                        | 25
>>>   +++++++++++++++++++++++++ drivers/usb/dwc3/core.h
>>>   | 10 ++++++++++
>>>   3 files changed, 38 insertions(+)
>>>
>>> diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt
>>> b/Documentation/devicetree/bindings/usb/dwc3.txt index 020b0e9..00cc541
>>> 100644
>>> --- a/Documentation/devicetree/bindings/usb/dwc3.txt
>>> +++ b/Documentation/devicetree/bindings/usb/dwc3.txt
>>>
>>> @@ -47,6 +47,9 @@ Optional properties:
>>>    - snps,hird-threshold: HIRD threshold
>>>    - snps,hsphy_interface: High-Speed PHY interface selection between
>>>    "utmi" for>
>>>      UTMI+ and "ulpi" for ULPI when the DWC_USB3_HSPHY_INTERFACE has value
>>>      3.
>>>
>>> + - snps,phyif-utmi-width: the value to configure the core to support a
>>> UTMI+ PHY +			with an 8- or 16-bit interface. Value 8 select 8-bit
>>> +			interface, value 16 select 16-bit interface.
>> Is 'phy_type = "utmi_wide"' not the same as 16-bit width?
>>
>> Again, I think this should be common.
> after knowing that I need to look for that "utmi_wide", I think I'd agree.
>
> I found mention of that in usb/ci-hdrc-usb2.txt and usb/fsl-usb.txt and from
> the coresponding code, I can see that they really mean the 16bit interface,
> the Rockchip TRM as well as the spec [0] seems to call it UTMI+ but really
> looks the same as utmi_wide.
>
> Interestingly, there is already generic code in drivers/usb/phy/of.c so that
> property should probably move to devicetree/bindings/usb/generic.txt
> as well.
>
>
> Heiko
>
> [0] http://cache.nxp.com/files/corporate/doc/support_info/UTMI-PLUS-SPECIFICATION.pdf

Thank you very much for your kindly help and helpful suggestion.:-D
I quite agree with you about use ?phy_type? to config UTMI+ interface 
for DWC3 controller, and add the ?phy_type? property to 
devicetree/bindings/usb/generic.txt.

So I shouldn't add a new dts property ?snps,phyif-utmi-width? here? and 
just use ?phy_type?is enough.

BTW, I think rk3399 UTMI+ isn't the same as ?utmi_wide?. Because UTMI+ 
support both 8-bit data interface and 16-bit data interface, but refer 
to code in drivers/usb/phy/of.c and related driver??phy_type = 
"utmi_wide"?means 16-bits, 'phy_type = utmi' means 8-bits.

Dear Felipe? how about your opinion about add ?phy_type? to config UTMI+ 
interface?

Best Regards
        William wu
>
>
>
>





From kishon at ti.com  Sun Jul 24 22:57:21 2016
From: kishon at ti.com (Kishon Vijay Abraham I)
Date: Mon, 25 Jul 2016 11:27:21 +0530
Subject: [PATCH 2/3] phy: rockchip-emmc: Be tolerant to card clock of 0 in
 power on
In-Reply-To: <CAPDyKFqPHB2O=-hDxVda-h04WS9xYpJ3MyzH8bk5Fc4ogTfB5A@mail.gmail.com>
References: <1467049167-14628-1-git-send-email-dianders@chromium.org>
 <1467049167-14628-3-git-send-email-dianders@chromium.org>
 <5773D1FD.8020601@ti.com>
 <CAD=FV=U_-8HJRjWa8ktT=7tmM5RB4OVnDce-Bv2teDibYfbCeA@mail.gmail.com>
 <CAPDyKFqPHB2O=-hDxVda-h04WS9xYpJ3MyzH8bk5Fc4ogTfB5A@mail.gmail.com>
Message-ID: <5795AA41.7070604@ti.com>

Ulf,

On Saturday 23 July 2016 03:09 PM, Ulf Hansson wrote:
> On 29 June 2016 at 17:18, Doug Anderson <dianders at chromium.org> wrote:
>> Kishon,
>>
>> On Wed, Jun 29, 2016 at 6:49 AM, Kishon Vijay Abraham I <kishon at ti.com> wrote:
>>> Hi,
>>>
>>> On Monday 27 June 2016 11:09 PM, Douglas Anderson wrote:
>>>> It's possible that there are some reasons to turn the PHY on while the
>>>> clock is 0.  In this case we just won't wait for the DLL to lock.
>>>>
>>>> This is a bit of a stopgap until we figure out exactly when we're
>>>> supposed to wait for the DLL to lock and when we're supposed to power
>>>> cycle the PHY.
>>>>
>>>> Note: this patch should help with suspend/resume where the system will
>>>> try to turn the PHY back on when the clock is 0.
>>>>
>>>> Signed-off-by: Douglas Anderson <dianders at chromium.org>
>>>> ---
>>>>  drivers/phy/phy-rockchip-emmc.c | 59 ++++++++++++++++++++++++++---------------
>>>>  1 file changed, 37 insertions(+), 22 deletions(-)
>>>>
>>>> diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c
>>>> index 9dce958233a0..a2aa6aca7dec 100644
>>>> --- a/drivers/phy/phy-rockchip-emmc.c
>>>> +++ b/drivers/phy/phy-rockchip-emmc.c
>>>> @@ -88,15 +88,36 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off)
>>>>       unsigned int caldone;
>>>>       unsigned int dllrdy;
>>>>       unsigned int freqsel = PHYCTRL_FREQSEL_200M;
>>>> +     unsigned long rate;
>>>>       unsigned long timeout;
>>>>
>>>> -     if (rk_phy->emmcclk != NULL) {
>>>> -             unsigned long rate = clk_get_rate(rk_phy->emmcclk);
>>>> +     /*
>>>> +      * Keep phyctrl_pdb and phyctrl_endll low to allow
>>>> +      * initialization of CALIO state M/C DFFs
>>>> +      */
>>>> +     regmap_write(rk_phy->reg_base,
>>>> +                  rk_phy->reg_offset + GRF_EMMCPHY_CON6,
>>>> +                  HIWORD_UPDATE(PHYCTRL_PDB_PWR_OFF,
>>>> +                                PHYCTRL_PDB_MASK,
>>>> +                                PHYCTRL_PDB_SHIFT));
>>>> +     regmap_write(rk_phy->reg_base,
>>>> +                  rk_phy->reg_offset + GRF_EMMCPHY_CON6,
>>>> +                  HIWORD_UPDATE(PHYCTRL_ENDLL_DISABLE,
>>>> +                                PHYCTRL_ENDLL_MASK,
>>>> +                                PHYCTRL_ENDLL_SHIFT));
>>>> +
>>>> +     /* Already finish power_off above */
>>>> +     if (on_off == PHYCTRL_PDB_PWR_OFF)
>>>> +             return 0;
>>>> +
>>>> +     rate = clk_get_rate(rk_phy->emmcclk);
>>>> +
>>>> +     if (rate != 0) {
>>>>               unsigned long ideal_rate;
>>>>               unsigned long diff;
>>>>
>>>>               switch (rate) {
>>>> -             case 0 ... 74999999:
>>>> +             case 1 ... 74999999:
>>>>                       ideal_rate = 50000000;
>>>>                       freqsel = PHYCTRL_FREQSEL_50M;
>>>>                       break;
>>>> @@ -127,25 +148,6 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off)
>>>>       }
>>>>
>>>>       /*
>>>> -      * Keep phyctrl_pdb and phyctrl_endll low to allow
>>>> -      * initialization of CALIO state M/C DFFs
>>>> -      */
>>>> -     regmap_write(rk_phy->reg_base,
>>>> -                  rk_phy->reg_offset + GRF_EMMCPHY_CON6,
>>>> -                  HIWORD_UPDATE(PHYCTRL_PDB_PWR_OFF,
>>>> -                                PHYCTRL_PDB_MASK,
>>>> -                                PHYCTRL_PDB_SHIFT));
>>>> -     regmap_write(rk_phy->reg_base,
>>>> -                  rk_phy->reg_offset + GRF_EMMCPHY_CON6,
>>>> -                  HIWORD_UPDATE(PHYCTRL_ENDLL_DISABLE,
>>>> -                                PHYCTRL_ENDLL_MASK,
>>>> -                                PHYCTRL_ENDLL_SHIFT));
>>>> -
>>>> -     /* Already finish power_off above */
>>>> -     if (on_off == PHYCTRL_PDB_PWR_OFF)
>>>> -             return 0;
>>>> -
>>>> -     /*
>>>>        * According to the user manual, calpad calibration
>>>>        * cycle takes more than 2us without the minimal recommended
>>>>        * value, so we may need a little margin here
>>>> @@ -183,6 +185,19 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off)
>>>>                    HIWORD_UPDATE(PHYCTRL_ENDLL_ENABLE,
>>>>                                  PHYCTRL_ENDLL_MASK,
>>>>                                  PHYCTRL_ENDLL_SHIFT));
>>>> +
>>>> +     /*
>>>> +      * We turned on the DLL even though the rate was 0 because we the
>>>> +      * clock might be turned on later.  ...but we can't wait for the DLL
>>>> +      * to lock when the rate is 0 because it will never lock with no
>>>> +      * input clock.
>>>> +      *
>>>> +      * Technically we should be checking the lock later when the clock
>>>> +      * is turned on, but for now we won't.
>>>> +      */
>>>> +     if (rate == 0)
>>>> +             return 0;
>>>
>>> Why not return initially from rockchip_emmc_phy_power if the clock rate is '0'.
>>> Are there other functions to lock the DLL apart from phy_power?
>>
>> Yeah, it's a big ugly right now.  This ugliness is really needed
>> because of <https://patchwork.kernel.org/patch/9201035/> because:
>>
>> 1. We power on the PHY at probe time and the card clock is in an
>> unknown state at that time.  It will be reported as 0 right now, but
>> it may or may not actually be 0.
>>
>> 2. We don't have an easy way to call back into the PHY when we
>> actually set the clock to a low rate (like 400kHz) for ID mode.
>> Before this series I tried to power the PHY off and on for every clock
>> change, but apparently that was causing problems.
>>
>>
>> As talked about in <https://patchwork.kernel.org/patch/9201035/>, I
>> think the right answer is to figure out how to get the common clock
>> framework notifications to happen for the card clock and then remove
>> the wholesale PHY power off / power on for every clock change.  The
>> PHY itself can register for the clock change notifications and figure
>> out how much or how little to do on every clock change.
>>
>> Unfortunately, as also discussed in the other patch, it's not trivial
>> to do this because I think it requires surgery on the main SDHCI
>> driver to change the way it deals with the card clock.  I'm not sure I
>> have time for this delicate surgery right now and I'm hoping that
>> perhaps Shawn will be able to help figure something out (maybe?) or I
>> can try coming back to it later.
>>
>>
>> In any case, I think a wholesale revert of my previous 150 MHz series
>> probably puts us in a worse state than we started with, so I was just
>> proposing reverting the one patch.  Once we do that, this PHY patch
>> helps keep us in a sane state (keeps suspend/resume working).
>>
>>
>> -Doug
> 
> Doug, Kishon,
> 
> Did you agree on how to move forward with this change?

I think still few things are not very clear especially on what should be
handled in PHY when the clock rate changes or if any new PHY APIs are required
to handle any clock changes etc..
But since this actually gets MMC working in rockchip, I'd be okay to merge this
now. Though I'd expect this to be refined in the future release cycles.

FWIW:
Reviewed-by: Kishon Vijay Abraham I <kishon at ti.com>

Thanks
Kishon


From cw00.choi at samsung.com  Sun Jul 24 23:01:50 2016
From: cw00.choi at samsung.com (Chanwoo Choi)
Date: Mon, 25 Jul 2016 15:01:50 +0900
Subject: [PATCH v3 6/7] PM / devfreq: rockchip: add devfreq driver for
 rk3399 dmc
In-Reply-To: <1469178440-4668-7-git-send-email-hl@rock-chips.com>
References: <1469178440-4668-1-git-send-email-hl@rock-chips.com>
 <CGME20160722090916epcas1p28b9a4db33945627f5e4c1157cd6786b7@epcas1p2.samsung.com>
 <1469178440-4668-7-git-send-email-hl@rock-chips.com>
Message-ID: <5795AB4E.4010600@samsung.com>

Hi Lin,

I'm glad to support the for dmc ddr clock scaling with devfreq/devfreq-event.

But, I think that you have to use the standard interface.
As I already mentioned[1] on previous mail, devfreq fwk support
the standard DEVFREQ_TRANSITION_NOTIFIER notifier which has the two notifications.
- DEVFREQ_PRECHANGE
- DEVFREQ_POSTCHANGE
[1] https://patchwork.kernel.org/patch/9194305/

You can use the DEVFREQ_PRECHANGE instead of DMCFREQ_ADJUST
and use the DEVFREQ_POSTCHANGE instead of DMCFREQ_FINISH.

On 2016? 07? 22? 18:07, Lin Huang wrote:
> base on dfi result, we do ddr frequency scaling, register
> dmc driver to devfreq framework, and use simple-ondemand
> policy.
> 
> Signed-off-by: Lin Huang <hl at rock-chips.com>
> ---
> Changes in v3:
> - operate dram setting through sip call
> - imporve set rate flow
> 
> Changes in v2:
> - None
>  
> Changes in v1:
> - move dfi controller to event
> - fix set voltage sequence when set rate fail
> - change Kconfig type from tristate to bool
> - move unuse EXPORT_SYMBOL_GPL()
> 
>  drivers/devfreq/Kconfig                 |   1 +
>  drivers/devfreq/Makefile                |   1 +
>  drivers/devfreq/rockchip/Kconfig        |  15 +
>  drivers/devfreq/rockchip/Makefile       |   2 +
>  drivers/devfreq/rockchip/rk3399_dmc.c   | 482 ++++++++++++++++++++++++++++++++
>  drivers/devfreq/rockchip/rockchip_dmc.c | 143 ++++++++++
>  include/soc/rockchip/rockchip_dmc.h     |  45 +++
>  7 files changed, 689 insertions(+)
>  create mode 100644 drivers/devfreq/rockchip/Kconfig
>  create mode 100644 drivers/devfreq/rockchip/Makefile
>  create mode 100644 drivers/devfreq/rockchip/rk3399_dmc.c
>  create mode 100644 drivers/devfreq/rockchip/rockchip_dmc.c
>  create mode 100644 include/soc/rockchip/rockchip_dmc.h
> 
> diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig
> index a5be56e..cb67246 100644
> --- a/drivers/devfreq/Kconfig
> +++ b/drivers/devfreq/Kconfig
> @@ -101,5 +101,6 @@ config ARM_TEGRA_DEVFREQ
>           operating frequencies and voltages with OPP support.
>  
>  source "drivers/devfreq/event/Kconfig"
> +source "drivers/devfreq/rockchip/Kconfig"
>  
>  endif # PM_DEVFREQ
> diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile
> index 09f11d9..48e2ae6 100644
> --- a/drivers/devfreq/Makefile
> +++ b/drivers/devfreq/Makefile
> @@ -9,6 +9,7 @@ obj-$(CONFIG_DEVFREQ_GOV_PASSIVE)	+= governor_passive.o
>  # DEVFREQ Drivers
>  obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ)	+= exynos-bus.o
>  obj-$(CONFIG_ARM_TEGRA_DEVFREQ)		+= tegra-devfreq.o
> +obj-$(CONFIG_ARCH_ROCKCHIP)		+= rockchip/
>  
>  # DEVFREQ Event Drivers
>  obj-$(CONFIG_PM_DEVFREQ_EVENT)		+= event/
> diff --git a/drivers/devfreq/rockchip/Kconfig b/drivers/devfreq/rockchip/Kconfig
> new file mode 100644
> index 0000000..7fb1cff
> --- /dev/null
> +++ b/drivers/devfreq/rockchip/Kconfig
> @@ -0,0 +1,15 @@
> +config ARM_ROCKCHIP_DMC_DEVFREQ
> +	bool "ARM ROCKCHIP DMC DEVFREQ Driver"
> +	depends on ARCH_ROCKCHIP
> +	help
> +	  This adds the DEVFREQ driver framework for the rockchip dmc.
> +
> +config ARM_RK3399_DMC_DEVFREQ
> +	bool "ARM RK3399 DMC DEVFREQ Driver"
> +	depends on ARM_ROCKCHIP_DMC_DEVFREQ
> +	select PM_OPP
> +	select DEVFREQ_GOV_SIMPLE_ONDEMAND
> +	help
> +          This adds the DEVFREQ driver for the RK3399 dmc. It sets the frequency
> +          for the memory controller and reads the usage counts from hardware.
> +
> diff --git a/drivers/devfreq/rockchip/Makefile b/drivers/devfreq/rockchip/Makefile
> new file mode 100644
> index 0000000..caca525
> --- /dev/null
> +++ b/drivers/devfreq/rockchip/Makefile
> @@ -0,0 +1,2 @@
> +obj-$(CONFIG_ARM_ROCKCHIP_DMC_DEVFREQ)	+= rockchip_dmc.o
> +obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ)	+= rk3399_dmc.o
> diff --git a/drivers/devfreq/rockchip/rk3399_dmc.c b/drivers/devfreq/rockchip/rk3399_dmc.c
> new file mode 100644
> index 0000000..f1d6120
> --- /dev/null
> +++ b/drivers/devfreq/rockchip/rk3399_dmc.c
> @@ -0,0 +1,482 @@
> +/*
> + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
> + * Author: Lin Huang <hl at rock-chips.com>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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/clk.h>
> +#include <linux/completion.h>
> +#include <linux/delay.h>
> +#include <linux/devfreq.h>
> +#include <linux/devfreq-event.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_opp.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/rwsem.h>
> +#include <linux/suspend.h>
> +#include <linux/syscore_ops.h>
> +
> +#include <soc/rockchip/rockchip_dmc.h>
> +#include "../../firmware/rockchip_sip.h"
> +
> +struct dram_timing {
> +	unsigned int ddr3_speed_bin;
> +	unsigned int pd_idle;
> +	unsigned int sr_idle;
> +	unsigned int sr_mc_gate_idle;
> +	unsigned int srpd_lite_idle;
> +	unsigned int standby_idle;
> +	unsigned int dram_dll_dis_freq;
> +	unsigned int phy_dll_dis_freq;
> +	unsigned int ddr3_odt_dis_freq;
> +	unsigned int ddr3_drv;
> +	unsigned int ddr3_odt;
> +	unsigned int phy_ddr3_ca_drv;
> +	unsigned int phy_ddr3_dq_drv;
> +	unsigned int phy_ddr3_odt;
> +	unsigned int lpddr3_odt_dis_freq;
> +	unsigned int lpddr3_drv;
> +	unsigned int lpddr3_odt;
> +	unsigned int phy_lpddr3_ca_drv;
> +	unsigned int phy_lpddr3_dq_drv;
> +	unsigned int phy_lpddr3_odt;
> +	unsigned int lpddr4_odt_dis_freq;
> +	unsigned int lpddr4_drv;
> +	unsigned int lpddr4_dq_odt;
> +	unsigned int lpddr4_ca_odt;
> +	unsigned int phy_lpddr4_ca_drv;
> +	unsigned int phy_lpddr4_ck_cs_drv;
> +	unsigned int phy_lpddr4_dq_drv;
> +	unsigned int phy_lpddr4_odt;
> +};
> +
> +struct rk3399_dmcfreq {
> +	struct device *dev;
> +	struct devfreq *devfreq;
> +	struct devfreq_simple_ondemand_data ondemand_data;
> +	struct clk *dmc_clk;
> +	struct devfreq_event_dev *edev;
> +	struct mutex lock;
> +	struct notifier_block dmc_nb;
> +	struct dram_timing *timing;
> +	wait_queue_head_t	wait_dcf_queue;
> +	int irq;
> +	int wait_dcf_flag;
> +	struct regulator *vdd_center;
> +	unsigned long rate, target_rate;
> +	unsigned long volt, target_volt;
> +};
> +
> +static int rk3399_dmcfreq_target(struct device *dev, unsigned long *freq,
> +				 u32 flags)
> +{
> +	struct platform_device *pdev = container_of(dev, struct platform_device,
> +						    dev);
> +	struct rk3399_dmcfreq *dmcfreq = platform_get_drvdata(pdev);
> +	struct dev_pm_opp *opp;
> +	unsigned long old_clk_rate = dmcfreq->rate;
> +	unsigned long target_volt, target_rate;
> +	int err;
> +
> +	rcu_read_lock();
> +	opp = devfreq_recommended_opp(dev, freq, flags);
> +	if (IS_ERR(opp)) {
> +		rcu_read_unlock();
> +		return PTR_ERR(opp);
> +	}
> +	target_rate = dev_pm_opp_get_freq(opp);
> +	target_volt = dev_pm_opp_get_voltage(opp);
> +	opp = devfreq_recommended_opp(dev, &dmcfreq->rate, flags);
> +	if (IS_ERR(opp)) {
> +		rcu_read_unlock();
> +		return PTR_ERR(opp);
> +	}
> +	dmcfreq->volt = dev_pm_opp_get_voltage(opp);
> +	rcu_read_unlock();
> +
> +	if (dmcfreq->rate == target_rate)
> +		return 0;
> +
> +	mutex_lock(&dmcfreq->lock);
> +	/*
> +	 * if frequency scaling from low to high, adjust voltage first;
> +	 * if frequency scaling from high to low, adjuset frequency first;
> +	 */
> +	if (old_clk_rate < target_rate) {
> +		err = regulator_set_voltage(dmcfreq->vdd_center, target_volt,
> +					    target_volt);
> +		if (err) {
> +			dev_err(dev, "Unable to set vol %lu\n", target_volt);
> +			goto out;
> +		}
> +	}
> +
> +	dmc_event(DMCFREQ_ADJUST);

You don't need to send the notification in target function.
When you use the DEVFREQ_TRANSITION_NOTIFIER notifier,
devfreq send the notification before calling the target() and after calling the target().

> +	dmcfreq->wait_dcf_flag = 1;
> +
> +	err = clk_set_rate(dmcfreq->dmc_clk, target_rate);
> +	if (err) {
> +		dev_err(dev,
> +			"Unable to set freq %lu. Current freq %lu. Error %d\n",
> +			target_rate, old_clk_rate, err);
> +		regulator_set_voltage(dmcfreq->vdd_center, dmcfreq->volt,
> +				      dmcfreq->volt);
> +		dmc_event(DMCFREQ_FINISH);

ditto.

> +		goto out;
> +	}
> +
> +	/*
> +	 * wait until bcf irq happen, it means freq scaling finish in bl31,
> +	 * use 100ms as timeout time
> +	 */
> +	wait_event_timeout(dmcfreq->wait_dcf_queue,
> +			   !dmcfreq->wait_dcf_flag, HZ / 10);
> +
> +	dmc_event(DMCFREQ_FINISH);

ditto.

> +
> +	/*
> +	 * check the dpll rate
> +	 * there only two result we will get,
> +	 * 1. ddr frequency scaling fail, we still get the old rate
> +	 * 2, ddr frequency scaling sucessful, we get the rate we set
> +	 */
> +	dmcfreq->rate = clk_get_rate(dmcfreq->dmc_clk);
> +
> +	/* if get the incorrect rate, set voltage to old value */
> +	if (dmcfreq->rate != target_rate) {
> +		dev_err(dev, "get wrong ddr frequency, Request freq %lu,\
> +			Current freq %lu\n", target_rate, dmcfreq->rate);
> +		regulator_set_voltage(dmcfreq->vdd_center, dmcfreq->volt,
> +				      dmcfreq->volt);
> +	} else if (old_clk_rate > target_rate)
> +		err = regulator_set_voltage(dmcfreq->vdd_center, target_volt,
> +					    target_volt);
> +	if (err)
> +		dev_err(dev, "Unable to set vol %lu\n", target_volt);
> +
> +out:
> +	mutex_unlock(&dmcfreq->lock);
> +	return err;
> +}
> +
> +static int rk3399_dmcfreq_get_dev_status(struct device *dev,
> +					 struct devfreq_dev_status *stat)
> +{
> +	struct platform_device *pdev = container_of(dev, struct platform_device,
> +						    dev);
> +	struct rk3399_dmcfreq *dmcfreq = platform_get_drvdata(pdev);
> +	struct devfreq_event_data edata;
> +
> +	devfreq_event_get_event(dmcfreq->edev, &edata);
> +
> +	stat->current_frequency = dmcfreq->rate;
> +	stat->busy_time = edata.load_count;
> +	stat->total_time = edata.total_count;
> +
> +	return 0;
> +}
> +
> +static int rk3399_dmcfreq_get_cur_freq(struct device *dev, unsigned long *freq)
> +{
> +	struct platform_device *pdev = container_of(dev, struct platform_device,
> +						    dev);
> +	struct rk3399_dmcfreq *dmcfreq = platform_get_drvdata(pdev);
> +
> +	*freq = dmcfreq->rate;
> +
> +	return 0;
> +}
> +
> +static void rk3399_dmcfreq_exit(struct device *dev)
> +{
> +	struct platform_device *pdev = container_of(dev, struct platform_device,
> +						    dev);
> +	struct rk3399_dmcfreq *dmcfreq = platform_get_drvdata(pdev);
> +
> +	devfreq_unregister_opp_notifier(dev, dmcfreq->devfreq);
> +}
> +
> +static struct devfreq_dev_profile rk3399_devfreq_dmc_profile = {
> +	.polling_ms	= 200,
> +	.target		= rk3399_dmcfreq_target,
> +	.get_dev_status	= rk3399_dmcfreq_get_dev_status,
> +	.get_cur_freq	= rk3399_dmcfreq_get_cur_freq,
> +	.exit		= rk3399_dmcfreq_exit,
> +};
> +
> +static __maybe_unused int rk3399_dmcfreq_suspend(struct device *dev)
> +{
> +	rockchip_dmc_disable();
> +	return 0;
> +}
> +
> +static __maybe_unused int rk3399_dmcfreq_resume(struct device *dev)
> +{
> +	rockchip_dmc_enable();
> +	return 0;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(rk3399_dmcfreq_pm, rk3399_dmcfreq_suspend,
> +			 rk3399_dmcfreq_resume);
> +
> +static int rk3399_dmc_enable_notify(struct notifier_block *nb,
> +				    unsigned long event, void *data)
> +{
> +	struct rk3399_dmcfreq *dmcfreq =
> +			      container_of(nb, struct rk3399_dmcfreq, dmc_nb);
> +	unsigned long freq = ULONG_MAX;
> +
> +	if (event == DMC_ENABLE) {
> +		devfreq_event_enable_edev(dmcfreq->edev);
> +		devfreq_resume_device(dmcfreq->devfreq);
> +		return NOTIFY_OK;
> +	} else if (event == DMC_DISABLE) {
> +		devfreq_event_disable_edev(dmcfreq->edev);
> +		devfreq_suspend_device(dmcfreq->devfreq);
> +
> +		/* when disable dmc, set sdram to max frequency */
> +		rk3399_dmcfreq_target(dmcfreq->dev, &freq, 0);
> +		return NOTIFY_OK;
> +	}
> +
> +	return NOTIFY_DONE;
> +}
> +
> +static irqreturn_t rk3399_dmc_irq(int irq, void *dev_id)
> +{
> +	struct rk3399_dmcfreq *dmcfreq = dev_id;
> +
> +	dmcfreq->wait_dcf_flag = 0;
> +	wake_up(&dmcfreq->wait_dcf_queue);
> +
> +	/* clr dcf irq */
> +	sip_smc_clr_ddr_irq();
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int of_do_get_timing(struct device_node *np,
> +		struct dram_timing *timing)
> +{
> +	int ret;
> +
> +	ret = of_property_read_u32(np, "ddr3_speed_bin",
> +				   &timing->ddr3_speed_bin);
> +	ret |= of_property_read_u32(np, "pd_idle", &timing->pd_idle);
> +	ret |= of_property_read_u32(np, "sr_idle", &timing->sr_idle);
> +	ret |= of_property_read_u32(np, "sr_mc_gate_idle",
> +				    &timing->sr_mc_gate_idle);
> +	ret |= of_property_read_u32(np, "srpd_lite_idle",
> +				    &timing->srpd_lite_idle);
> +	ret |= of_property_read_u32(np, "standby_idle", &timing->standby_idle);
> +	ret |= of_property_read_u32(np, "dram_dll_dis_freq",
> +				    &timing->dram_dll_dis_freq);
> +	ret |= of_property_read_u32(np, "phy_dll_dis_freq",
> +				    &timing->phy_dll_dis_freq);
> +	ret |= of_property_read_u32(np, "ddr3_odt_dis_freq",
> +				    &timing->ddr3_odt_dis_freq);
> +	ret |= of_property_read_u32(np, "ddr3_drv", &timing->ddr3_drv);
> +	ret |= of_property_read_u32(np, "ddr3_odt", &timing->ddr3_odt);
> +	ret |= of_property_read_u32(np, "phy_ddr3_ca_drv",
> +				    &timing->phy_ddr3_ca_drv);
> +	ret |= of_property_read_u32(np, "phy_ddr3_dq_drv",
> +				    &timing->phy_ddr3_dq_drv);
> +	ret |= of_property_read_u32(np, "phy_ddr3_odt", &timing->phy_ddr3_odt);
> +	ret |= of_property_read_u32(np, "lpddr3_odt_dis_freq",
> +				    &timing->lpddr3_odt_dis_freq);
> +	ret |= of_property_read_u32(np, "lpddr3_drv", &timing->lpddr3_drv);
> +	ret |= of_property_read_u32(np, "lpddr3_odt", &timing->lpddr3_odt);
> +	ret |= of_property_read_u32(np, "phy_lpddr3_ca_drv",
> +				    &timing->phy_lpddr3_ca_drv);
> +	ret |= of_property_read_u32(np, "phy_lpddr3_dq_drv",
> +				    &timing->phy_lpddr3_dq_drv);
> +	ret |= of_property_read_u32(np, "phy_lpddr3_odt",
> +				    &timing->phy_lpddr3_odt);
> +	ret |= of_property_read_u32(np, "lpddr4_odt_dis_freq",
> +				    &timing->lpddr4_odt_dis_freq);
> +	ret |= of_property_read_u32(np, "lpddr4_drv",
> +				    &timing->lpddr4_drv);
> +	ret |= of_property_read_u32(np, "lpddr4_dq_odt",
> +				    &timing->lpddr4_dq_odt);
> +	ret |= of_property_read_u32(np, "lpddr4_ca_odt",
> +				    &timing->lpddr4_ca_odt);
> +	ret |= of_property_read_u32(np, "phy_lpddr4_ca_drv",
> +				    &timing->phy_lpddr4_ca_drv);
> +	ret |= of_property_read_u32(np, "phy_lpddr4_ck_cs_drv",
> +				    &timing->phy_lpddr4_ck_cs_drv);
> +	ret |= of_property_read_u32(np, "phy_lpddr4_dq_drv",
> +				    &timing->phy_lpddr4_dq_drv);
> +	ret |= of_property_read_u32(np, "phy_lpddr4_odt",
> +				    &timing->phy_lpddr4_odt);
> +
> +	return ret;
> +}
> +
> +static struct dram_timing *of_get_ddr_timings(struct device *dev,
> +					      struct device_node *np)
> +{
> +	struct dram_timing	*timing = NULL;
> +	struct device_node	*np_tim;
> +
> +	np_tim = of_parse_phandle(np, "ddr_timing", 0);
> +	if (np_tim) {
> +		timing = devm_kzalloc(dev, sizeof(*timing), GFP_KERNEL);
> +		if (!timing)
> +			goto err;
> +		if (of_do_get_timing(np_tim, timing)) {
> +			devm_kfree(dev, timing);
> +			goto err;
> +		}
> +		return timing;
> +	}
> +
> +err:
> +	if (timing) {
> +		devm_kfree(dev, timing);
> +		timing = NULL;
> +	}
> +
> +	return timing;
> +}
> +
> +static int rk3399_dmcfreq_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct rk3399_dmcfreq *data;
> +	int ret, irq, index;
> +	uint64_t param = 0;
> +	uint32_t *timing;
> +	struct device_node *np = pdev->dev.of_node;
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0) {
> +		dev_err(&pdev->dev, "no dmc irq resource\n");
> +		return -EINVAL;
> +	}
> +
> +	data = devm_kzalloc(dev, sizeof(struct rk3399_dmcfreq), GFP_KERNEL);
> +	if (!data)
> +		return -ENOMEM;
> +
> +	mutex_init(&data->lock);
> +
> +	data->vdd_center = devm_regulator_get(dev, "center");
> +	if (IS_ERR(data->vdd_center)) {
> +		dev_err(dev, "Cannot get the regulator \"center\"\n");
> +		return PTR_ERR(data->vdd_center);
> +	}
> +
> +	data->dmc_clk = devm_clk_get(dev, "dmc_clk");
> +	if (IS_ERR(data->dmc_clk)) {
> +		dev_err(dev, "Cannot get the clk dmc_clk\n");
> +		return PTR_ERR(data->dmc_clk);
> +	};
> +
> +	data->edev = devfreq_event_get_edev_by_phandle(dev, 0);
> +	if (IS_ERR(data->edev))
> +		return -EPROBE_DEFER;
> +
> +	ret = devfreq_event_enable_edev(data->edev);
> +	if (ret < 0) {
> +		dev_err(dev, "failed to enable devfreq-event devices\n");
> +		return ret;
> +	}
> +
> +	/*
> +	 * We add a devfreq driver to our parent since it has a device tree node
> +	 * with operating points.
> +	 */
> +	if (dev_pm_opp_of_add_table(dev)) {
> +		dev_err(dev, "Invalid operating-points in device tree.\n");
> +		return -EINVAL;
> +	}
> +
> +	of_property_read_u32(np, "upthreshold",
> +			     &data->ondemand_data.upthreshold);
> +
> +	of_property_read_u32(np, "downdifferential",
> +			     &data->ondemand_data.downdifferential);
> +
> +	data->rate = clk_get_rate(data->dmc_clk);
> +	rk3399_devfreq_dmc_profile.initial_freq = data->rate;
> +	data->devfreq = devfreq_add_device(dev,
> +					   &rk3399_devfreq_dmc_profile,
> +					   "simple_ondemand",
> +					   &data->ondemand_data);
> +	if (IS_ERR(data->devfreq))
> +		return PTR_ERR(data->devfreq);
> +
> +	devfreq_register_opp_notifier(dev, data->devfreq);
> +
> +	data->dmc_nb.notifier_call = rk3399_dmc_enable_notify;
> +	dmc_register_notifier(&data->dmc_nb);
> +	data->irq = irq;
> +	ret = devm_request_irq(dev, irq, rk3399_dmc_irq, 0,
> +			       dev_name(dev), data);
> +	if (ret) {
> +		dev_err(dev, "failed to request dmc irq: %d\n", ret);
> +		return ret;
> +	}
> +
> +	/* get dram timing and pass it to bl31 */
> +	data->timing = of_get_ddr_timings(dev, np);
> +	if (data->timing) {
> +		timing = (uint32_t *)data->timing;
> +		for (index = 0; index < (sizeof(struct dram_timing) / 4);
> +		     index++) {
> +			param = index;
> +			param = param << 32 | *timing++;
> +			sip_smc_set_ddr_param(param);
> +			param = 0;
> +		}
> +	}
> +	sip_smc_ddr_init();
> +
> +	init_waitqueue_head(&data->wait_dcf_queue);
> +	data->wait_dcf_flag = 0;
> +
> +	data->dev = dev;
> +	platform_set_drvdata(pdev, data);
> +
> +	return 0;
> +}
> +
> +static int rk3399_dmcfreq_remove(struct platform_device *pdev)
> +{
> +	struct rk3399_dmcfreq *dmcfreq = platform_get_drvdata(pdev);
> +
> +	devfreq_remove_device(dmcfreq->devfreq);
> +	regulator_put(dmcfreq->vdd_center);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id rk3399dmc_devfreq_of_match[] = {
> +	{ .compatible = "rockchip,rk3399-dmc" },
> +	{ },
> +};
> +
> +static struct platform_driver rk3399_dmcfreq_driver = {
> +	.probe	= rk3399_dmcfreq_probe,
> +	.remove	= rk3399_dmcfreq_remove,
> +	.driver = {
> +		.name	= "rk3399-dmc-freq",
> +		.pm	= &rk3399_dmcfreq_pm,
> +		.of_match_table = rk3399dmc_devfreq_of_match,
> +	},
> +};
> +module_platform_driver(rk3399_dmcfreq_driver);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("RK3399 dmcfreq driver with devfreq framework");
> diff --git a/drivers/devfreq/rockchip/rockchip_dmc.c b/drivers/devfreq/rockchip/rockchip_dmc.c
> new file mode 100644
> index 0000000..78baee5
> --- /dev/null
> +++ b/drivers/devfreq/rockchip/rockchip_dmc.c
> @@ -0,0 +1,143 @@
> +/*
> + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
> + * Author: Lin Huang <hl at rock-chips.com>
> + * Base on: https://chromium-review.googlesource.com/#/c/231477/
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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/mutex.h>
> +#include <soc/rockchip/rockchip_dmc.h>
> +
> +static int num_wait;
> +static int num_disable;
> +static BLOCKING_NOTIFIER_HEAD(dmc_notifier_list);
> +static DEFINE_MUTEX(dmc_en_lock);
> +static DEFINE_MUTEX(dmc_sync_lock);
> +
> +/**
> + * rockchip_dmc_enabled - Returns true if dmc freq is enabled, false otherwise.
> + */
> +bool rockchip_dmc_enabled(void)
> +{
> +	return num_disable <= 0 && num_wait <= 1;
> +}
> +
> +/**
> + * rockchip_dmc_enable - Enable dmc frequency scaling. Will only enable
> + * frequency scaling if there are 1 or fewer notifiers. Call to undo
> + * rockchip_dmc_disable.
> + */
> +void rockchip_dmc_enable(void)
> +{
> +	mutex_lock(&dmc_en_lock);
> +	num_disable--;
> +	WARN_ON(num_disable < 0);
> +	if (rockchip_dmc_enabled())
> +		dmc_event(DMC_ENABLE);
> +	mutex_unlock(&dmc_en_lock);
> +}
> +
> +/**
> + * rockchip_dmc_disable - Disable dmc frequency scaling. Call when something
> + * cannot coincide with dmc frequency scaling.
> + */
> +void rockchip_dmc_disable(void)
> +{
> +	mutex_lock(&dmc_en_lock);
> +	if (rockchip_dmc_enabled())
> +		dmc_event(DMC_DISABLE);
> +	num_disable++;
> +	mutex_unlock(&dmc_en_lock);
> +}
> +
> +void dmc_event(int event)
> +{
> +	mutex_lock(&dmc_sync_lock);
> +	blocking_notifier_call_chain(&dmc_notifier_list, event, NULL);
> +	mutex_unlock(&dmc_sync_lock);
> +}
> +
> +/**
> + * dmc_register_notifier - register a driver to dmc chain
> + * @nb: notifier function to register
> + */
> +int dmc_register_notifier(struct notifier_block *nb)
> +{
> +	int ret;
> +
> +	if (!nb)
> +		return -EINVAL;
> +
> +	ret = blocking_notifier_chain_register(&dmc_notifier_list, nb);
> +
> +	return ret;
> +}
> +
> +/**
> + * dmc_unregister_notifier - unregister a driver from dmc chain
> + * @nb: remove notifier function
> + */
> +int dmc_unregister_notifier(struct notifier_block *nb)
> +{
> +	int ret;
> +
> +	if (!nb)
> +		return -EINVAL;
> +
> +	ret = blocking_notifier_chain_unregister(&dmc_notifier_list, nb);
> +
> +	return ret;
> +}
> +
> +/**
> + * rockchip_dmc_get - Register the notifier block for the dmc chain.
> + * @nb The dmc notifier block to register
> + */
> +int rockchip_dmc_get(struct notifier_block *nb)
> +{
> +	if (!nb)
> +		return -EINVAL;
> +
> +	mutex_lock(&dmc_en_lock);
> +
> +	/*
> +	 * if have two notifier(enable two vop etc),
> +	 * need to disable dmc
> +	 */
> +	if (num_wait == 1 && num_disable <= 0)
> +		dmc_event(DMC_DISABLE);
> +	num_wait++;
> +	dmc_register_notifier(nb);
> +	mutex_unlock(&dmc_en_lock);
> +
> +	return 0;
> +}
> +
> +/**
> + * rockchip_dmc_put - Remove the notifier block from the dmc chain.
> + * @nb The dmc notifier block to unregister
> + */
> +int rockchip_dmc_put(struct notifier_block *nb)
> +{
> +	if (!nb)
> +		return -EINVAL;
> +
> +	mutex_lock(&dmc_en_lock);
> +	num_wait--;
> +
> +	/* if notifier from 2 back to 1, enable dmc again */
> +	if (num_wait == 1 && num_disable <= 0)
> +		dmc_event(DMC_ENABLE);
> +	dmc_unregister_notifier(nb);
> +	mutex_unlock(&dmc_en_lock);
> +
> +	return 0;
> +}
> diff --git a/include/soc/rockchip/rockchip_dmc.h b/include/soc/rockchip/rockchip_dmc.h
> new file mode 100644
> index 0000000..3f69cbf
> --- /dev/null
> +++ b/include/soc/rockchip/rockchip_dmc.h
> @@ -0,0 +1,45 @@
> +/*
> + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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.
> + */
> +
> +#ifndef __SOC_ROCKCHIP_DMC_H
> +#define __SOC_ROCKCHIP_DMC_H
> +
> +#include <linux/notifier.h>
> +
> +#define DMC_ENABLE	0
> +#define DMC_DISABLE	1
> +#define DMCFREQ_ADJUST	2
> +#define DMCFREQ_FINISH	3
> +
> +#if IS_ENABLED(CONFIG_ARM_ROCKCHIP_DMC_DEVFREQ)
> +int rockchip_dmc_get(struct notifier_block *nb);
> +int rockchip_dmc_put(struct notifier_block *nb);
> +#else
> +static inline int rockchip_dmc_get(struct notifier_block *nb)
> +{
> +	return 0;
> +}
> +
> +static inline int rockchip_dmc_put(struct notifier_block *nb)
> +{
> +	return 0;
> +}
> +#endif
> +
> +void dmc_event(int event);
> +int dmc_register_notifier(struct notifier_block *nb);
> +int dmc_unregister_notifier(struct notifier_block *nb);
> +void rockchip_dmc_enable(void);
> +void rockchip_dmc_disable(void);
> +bool rockchip_dmc_enabled(void);
> +#endif
> 

Regards,
Chanwoo Choi



From ulf.hansson at linaro.org  Mon Jul 25 00:37:51 2016
From: ulf.hansson at linaro.org (Ulf Hansson)
Date: Mon, 25 Jul 2016 09:37:51 +0200
Subject: [PATCH 2/3] phy: rockchip-emmc: Be tolerant to card clock of 0 in
 power on
In-Reply-To: <5795AA41.7070604@ti.com>
References: <1467049167-14628-1-git-send-email-dianders@chromium.org>
 <1467049167-14628-3-git-send-email-dianders@chromium.org>
 <5773D1FD.8020601@ti.com>
 <CAD=FV=U_-8HJRjWa8ktT=7tmM5RB4OVnDce-Bv2teDibYfbCeA@mail.gmail.com>
 <CAPDyKFqPHB2O=-hDxVda-h04WS9xYpJ3MyzH8bk5Fc4ogTfB5A@mail.gmail.com>
 <5795AA41.7070604@ti.com>
Message-ID: <CAPDyKFpX=KahVqmHyC-55e4Er9ViF02E5c9Y_M-SmAGj=M4eEw@mail.gmail.com>

On 25 July 2016 at 07:57, Kishon Vijay Abraham I <kishon at ti.com> wrote:
> Ulf,
>
> On Saturday 23 July 2016 03:09 PM, Ulf Hansson wrote:
>> On 29 June 2016 at 17:18, Doug Anderson <dianders at chromium.org> wrote:
>>> Kishon,
>>>
>>> On Wed, Jun 29, 2016 at 6:49 AM, Kishon Vijay Abraham I <kishon at ti.com> wrote:
>>>> Hi,
>>>>
>>>> On Monday 27 June 2016 11:09 PM, Douglas Anderson wrote:
>>>>> It's possible that there are some reasons to turn the PHY on while the
>>>>> clock is 0.  In this case we just won't wait for the DLL to lock.
>>>>>
>>>>> This is a bit of a stopgap until we figure out exactly when we're
>>>>> supposed to wait for the DLL to lock and when we're supposed to power
>>>>> cycle the PHY.
>>>>>
>>>>> Note: this patch should help with suspend/resume where the system will
>>>>> try to turn the PHY back on when the clock is 0.
>>>>>
>>>>> Signed-off-by: Douglas Anderson <dianders at chromium.org>
>>>>> ---
>>>>>  drivers/phy/phy-rockchip-emmc.c | 59 ++++++++++++++++++++++++++---------------
>>>>>  1 file changed, 37 insertions(+), 22 deletions(-)
>>>>>
>>>>> diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c
>>>>> index 9dce958233a0..a2aa6aca7dec 100644
>>>>> --- a/drivers/phy/phy-rockchip-emmc.c
>>>>> +++ b/drivers/phy/phy-rockchip-emmc.c
>>>>> @@ -88,15 +88,36 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off)
>>>>>       unsigned int caldone;
>>>>>       unsigned int dllrdy;
>>>>>       unsigned int freqsel = PHYCTRL_FREQSEL_200M;
>>>>> +     unsigned long rate;
>>>>>       unsigned long timeout;
>>>>>
>>>>> -     if (rk_phy->emmcclk != NULL) {
>>>>> -             unsigned long rate = clk_get_rate(rk_phy->emmcclk);
>>>>> +     /*
>>>>> +      * Keep phyctrl_pdb and phyctrl_endll low to allow
>>>>> +      * initialization of CALIO state M/C DFFs
>>>>> +      */
>>>>> +     regmap_write(rk_phy->reg_base,
>>>>> +                  rk_phy->reg_offset + GRF_EMMCPHY_CON6,
>>>>> +                  HIWORD_UPDATE(PHYCTRL_PDB_PWR_OFF,
>>>>> +                                PHYCTRL_PDB_MASK,
>>>>> +                                PHYCTRL_PDB_SHIFT));
>>>>> +     regmap_write(rk_phy->reg_base,
>>>>> +                  rk_phy->reg_offset + GRF_EMMCPHY_CON6,
>>>>> +                  HIWORD_UPDATE(PHYCTRL_ENDLL_DISABLE,
>>>>> +                                PHYCTRL_ENDLL_MASK,
>>>>> +                                PHYCTRL_ENDLL_SHIFT));
>>>>> +
>>>>> +     /* Already finish power_off above */
>>>>> +     if (on_off == PHYCTRL_PDB_PWR_OFF)
>>>>> +             return 0;
>>>>> +
>>>>> +     rate = clk_get_rate(rk_phy->emmcclk);
>>>>> +
>>>>> +     if (rate != 0) {
>>>>>               unsigned long ideal_rate;
>>>>>               unsigned long diff;
>>>>>
>>>>>               switch (rate) {
>>>>> -             case 0 ... 74999999:
>>>>> +             case 1 ... 74999999:
>>>>>                       ideal_rate = 50000000;
>>>>>                       freqsel = PHYCTRL_FREQSEL_50M;
>>>>>                       break;
>>>>> @@ -127,25 +148,6 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off)
>>>>>       }
>>>>>
>>>>>       /*
>>>>> -      * Keep phyctrl_pdb and phyctrl_endll low to allow
>>>>> -      * initialization of CALIO state M/C DFFs
>>>>> -      */
>>>>> -     regmap_write(rk_phy->reg_base,
>>>>> -                  rk_phy->reg_offset + GRF_EMMCPHY_CON6,
>>>>> -                  HIWORD_UPDATE(PHYCTRL_PDB_PWR_OFF,
>>>>> -                                PHYCTRL_PDB_MASK,
>>>>> -                                PHYCTRL_PDB_SHIFT));
>>>>> -     regmap_write(rk_phy->reg_base,
>>>>> -                  rk_phy->reg_offset + GRF_EMMCPHY_CON6,
>>>>> -                  HIWORD_UPDATE(PHYCTRL_ENDLL_DISABLE,
>>>>> -                                PHYCTRL_ENDLL_MASK,
>>>>> -                                PHYCTRL_ENDLL_SHIFT));
>>>>> -
>>>>> -     /* Already finish power_off above */
>>>>> -     if (on_off == PHYCTRL_PDB_PWR_OFF)
>>>>> -             return 0;
>>>>> -
>>>>> -     /*
>>>>>        * According to the user manual, calpad calibration
>>>>>        * cycle takes more than 2us without the minimal recommended
>>>>>        * value, so we may need a little margin here
>>>>> @@ -183,6 +185,19 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off)
>>>>>                    HIWORD_UPDATE(PHYCTRL_ENDLL_ENABLE,
>>>>>                                  PHYCTRL_ENDLL_MASK,
>>>>>                                  PHYCTRL_ENDLL_SHIFT));
>>>>> +
>>>>> +     /*
>>>>> +      * We turned on the DLL even though the rate was 0 because we the
>>>>> +      * clock might be turned on later.  ...but we can't wait for the DLL
>>>>> +      * to lock when the rate is 0 because it will never lock with no
>>>>> +      * input clock.
>>>>> +      *
>>>>> +      * Technically we should be checking the lock later when the clock
>>>>> +      * is turned on, but for now we won't.
>>>>> +      */
>>>>> +     if (rate == 0)
>>>>> +             return 0;
>>>>
>>>> Why not return initially from rockchip_emmc_phy_power if the clock rate is '0'.
>>>> Are there other functions to lock the DLL apart from phy_power?
>>>
>>> Yeah, it's a big ugly right now.  This ugliness is really needed
>>> because of <https://patchwork.kernel.org/patch/9201035/> because:
>>>
>>> 1. We power on the PHY at probe time and the card clock is in an
>>> unknown state at that time.  It will be reported as 0 right now, but
>>> it may or may not actually be 0.
>>>
>>> 2. We don't have an easy way to call back into the PHY when we
>>> actually set the clock to a low rate (like 400kHz) for ID mode.
>>> Before this series I tried to power the PHY off and on for every clock
>>> change, but apparently that was causing problems.
>>>
>>>
>>> As talked about in <https://patchwork.kernel.org/patch/9201035/>, I
>>> think the right answer is to figure out how to get the common clock
>>> framework notifications to happen for the card clock and then remove
>>> the wholesale PHY power off / power on for every clock change.  The
>>> PHY itself can register for the clock change notifications and figure
>>> out how much or how little to do on every clock change.
>>>
>>> Unfortunately, as also discussed in the other patch, it's not trivial
>>> to do this because I think it requires surgery on the main SDHCI
>>> driver to change the way it deals with the card clock.  I'm not sure I
>>> have time for this delicate surgery right now and I'm hoping that
>>> perhaps Shawn will be able to help figure something out (maybe?) or I
>>> can try coming back to it later.
>>>
>>>
>>> In any case, I think a wholesale revert of my previous 150 MHz series
>>> probably puts us in a worse state than we started with, so I was just
>>> proposing reverting the one patch.  Once we do that, this PHY patch
>>> helps keep us in a sane state (keeps suspend/resume working).
>>>
>>>
>>> -Doug
>>
>> Doug, Kishon,
>>
>> Did you agree on how to move forward with this change?
>
> I think still few things are not very clear especially on what should be
> handled in PHY when the clock rate changes or if any new PHY APIs are required
> to handle any clock changes etc..
> But since this actually gets MMC working in rockchip, I'd be okay to merge this
> now. Though I'd expect this to be refined in the future release cycles.

Okay.

>
> FWIW:
> Reviewed-by: Kishon Vijay Abraham I <kishon at ti.com>

I assume it's okay that I queue this via my mmc tree then. If not, please tell!

Kind regards
Uffe


From hl at rock-chips.com  Mon Jul 25 01:47:18 2016
From: hl at rock-chips.com (hl)
Date: Mon, 25 Jul 2016 16:47:18 +0800
Subject: [PATCH v3 6/7] PM / devfreq: rockchip: add devfreq driver for
 rk3399 dmc
In-Reply-To: <5795AB4E.4010600@samsung.com>
References: <1469178440-4668-1-git-send-email-hl@rock-chips.com>
 <CGME20160722090916epcas1p28b9a4db33945627f5e4c1157cd6786b7@epcas1p2.samsung.com>
 <1469178440-4668-7-git-send-email-hl@rock-chips.com>
 <5795AB4E.4010600@samsung.com>
Message-ID: <5795D216.7010003@rock-chips.com>

Hi Chanwoo Choi,

On 2016?07?25? 14:01, Chanwoo Choi wrote:
> Hi Lin,
>
> I'm glad to support the for dmc ddr clock scaling with devfreq/devfreq-event.
>
> But, I think that you have to use the standard interface.
> As I already mentioned[1] on previous mail, devfreq fwk support
> the standard DEVFREQ_TRANSITION_NOTIFIER notifier which has the two notifications.
> - DEVFREQ_PRECHANGE
> - DEVFREQ_POSTCHANGE
> [1] https://patchwork.kernel.org/patch/9194305/
>
> You can use the DEVFREQ_PRECHANGE instead of DMCFREQ_ADJUST
> and use the DEVFREQ_POSTCHANGE instead of DMCFREQ_FINISH.
>
> On 2016? 07? 22? 18:07, Lin Huang wrote:
>> base on dfi result, we do ddr frequency scaling, register
>> dmc driver to devfreq framework, and use simple-ondemand
>> policy.
>>
>> Signed-off-by: Lin Huang <hl at rock-chips.com>
>> ---
>> Changes in v3:
>> - operate dram setting through sip call
>> - imporve set rate flow
>>
>> Changes in v2:
>> - None
>>   
>> Changes in v1:
>> - move dfi controller to event
>> - fix set voltage sequence when set rate fail
>> - change Kconfig type from tristate to bool
>> - move unuse EXPORT_SYMBOL_GPL()
>>
>>   drivers/devfreq/Kconfig                 |   1 +
>>   drivers/devfreq/Makefile                |   1 +
>>   drivers/devfreq/rockchip/Kconfig        |  15 +
>>   drivers/devfreq/rockchip/Makefile       |   2 +
>>   drivers/devfreq/rockchip/rk3399_dmc.c   | 482 ++++++++++++++++++++++++++++++++
>>   drivers/devfreq/rockchip/rockchip_dmc.c | 143 ++++++++++
>>   include/soc/rockchip/rockchip_dmc.h     |  45 +++
>>   7 files changed, 689 insertions(+)
>>   create mode 100644 drivers/devfreq/rockchip/Kconfig
>>   create mode 100644 drivers/devfreq/rockchip/Makefile
>>   create mode 100644 drivers/devfreq/rockchip/rk3399_dmc.c
>>   create mode 100644 drivers/devfreq/rockchip/rockchip_dmc.c
>>   create mode 100644 include/soc/rockchip/rockchip_dmc.h
>>
>> diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig
>> index a5be56e..cb67246 100644
>> --- a/drivers/devfreq/Kconfig
>> +++ b/drivers/devfreq/Kconfig
>> @@ -101,5 +101,6 @@ config ARM_TEGRA_DEVFREQ
>>            operating frequencies and voltages with OPP support.
>>   
>>   source "drivers/devfreq/event/Kconfig"
>> +source "drivers/devfreq/rockchip/Kconfig"
>>   
>>   endif # PM_DEVFREQ
>> diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile
>> index 09f11d9..48e2ae6 100644
>> --- a/drivers/devfreq/Makefile
>> +++ b/drivers/devfreq/Makefile
>> @@ -9,6 +9,7 @@ obj-$(CONFIG_DEVFREQ_GOV_PASSIVE)	+= governor_passive.o
>>   # DEVFREQ Drivers
>>   obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ)	+= exynos-bus.o
>>   obj-$(CONFIG_ARM_TEGRA_DEVFREQ)		+= tegra-devfreq.o
>> +obj-$(CONFIG_ARCH_ROCKCHIP)		+= rockchip/
>>   
>>   # DEVFREQ Event Drivers
>>   obj-$(CONFIG_PM_DEVFREQ_EVENT)		+= event/
>> diff --git a/drivers/devfreq/rockchip/Kconfig b/drivers/devfreq/rockchip/Kconfig
>> new file mode 100644
>> index 0000000..7fb1cff
>> --- /dev/null
>> +++ b/drivers/devfreq/rockchip/Kconfig
>> @@ -0,0 +1,15 @@
>> +config ARM_ROCKCHIP_DMC_DEVFREQ
>> +	bool "ARM ROCKCHIP DMC DEVFREQ Driver"
>> +	depends on ARCH_ROCKCHIP
>> +	help
>> +	  This adds the DEVFREQ driver framework for the rockchip dmc.
>> +
>> +config ARM_RK3399_DMC_DEVFREQ
>> +	bool "ARM RK3399 DMC DEVFREQ Driver"
>> +	depends on ARM_ROCKCHIP_DMC_DEVFREQ
>> +	select PM_OPP
>> +	select DEVFREQ_GOV_SIMPLE_ONDEMAND
>> +	help
>> +          This adds the DEVFREQ driver for the RK3399 dmc. It sets the frequency
>> +          for the memory controller and reads the usage counts from hardware.
>> +
>> diff --git a/drivers/devfreq/rockchip/Makefile b/drivers/devfreq/rockchip/Makefile
>> new file mode 100644
>> index 0000000..caca525
>> --- /dev/null
>> +++ b/drivers/devfreq/rockchip/Makefile
>> @@ -0,0 +1,2 @@
>> +obj-$(CONFIG_ARM_ROCKCHIP_DMC_DEVFREQ)	+= rockchip_dmc.o
>> +obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ)	+= rk3399_dmc.o
>> diff --git a/drivers/devfreq/rockchip/rk3399_dmc.c b/drivers/devfreq/rockchip/rk3399_dmc.c
>> new file mode 100644
>> index 0000000..f1d6120
>> --- /dev/null
>> +++ b/drivers/devfreq/rockchip/rk3399_dmc.c
>> @@ -0,0 +1,482 @@
>> +/*
>> + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
>> + * Author: Lin Huang <hl at rock-chips.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms and conditions of the GNU General Public License,
>> + * version 2, as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope 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/clk.h>
>> +#include <linux/completion.h>
>> +#include <linux/delay.h>
>> +#include <linux/devfreq.h>
>> +#include <linux/devfreq-event.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/pm_opp.h>
>> +#include <linux/regulator/consumer.h>
>> +#include <linux/rwsem.h>
>> +#include <linux/suspend.h>
>> +#include <linux/syscore_ops.h>
>> +
>> +#include <soc/rockchip/rockchip_dmc.h>
>> +#include "../../firmware/rockchip_sip.h"
>> +
>> +struct dram_timing {
>> +	unsigned int ddr3_speed_bin;
>> +	unsigned int pd_idle;
>> +	unsigned int sr_idle;
>> +	unsigned int sr_mc_gate_idle;
>> +	unsigned int srpd_lite_idle;
>> +	unsigned int standby_idle;
>> +	unsigned int dram_dll_dis_freq;
>> +	unsigned int phy_dll_dis_freq;
>> +	unsigned int ddr3_odt_dis_freq;
>> +	unsigned int ddr3_drv;
>> +	unsigned int ddr3_odt;
>> +	unsigned int phy_ddr3_ca_drv;
>> +	unsigned int phy_ddr3_dq_drv;
>> +	unsigned int phy_ddr3_odt;
>> +	unsigned int lpddr3_odt_dis_freq;
>> +	unsigned int lpddr3_drv;
>> +	unsigned int lpddr3_odt;
>> +	unsigned int phy_lpddr3_ca_drv;
>> +	unsigned int phy_lpddr3_dq_drv;
>> +	unsigned int phy_lpddr3_odt;
>> +	unsigned int lpddr4_odt_dis_freq;
>> +	unsigned int lpddr4_drv;
>> +	unsigned int lpddr4_dq_odt;
>> +	unsigned int lpddr4_ca_odt;
>> +	unsigned int phy_lpddr4_ca_drv;
>> +	unsigned int phy_lpddr4_ck_cs_drv;
>> +	unsigned int phy_lpddr4_dq_drv;
>> +	unsigned int phy_lpddr4_odt;
>> +};
>> +
>> +struct rk3399_dmcfreq {
>> +	struct device *dev;
>> +	struct devfreq *devfreq;
>> +	struct devfreq_simple_ondemand_data ondemand_data;
>> +	struct clk *dmc_clk;
>> +	struct devfreq_event_dev *edev;
>> +	struct mutex lock;
>> +	struct notifier_block dmc_nb;
>> +	struct dram_timing *timing;
>> +	wait_queue_head_t	wait_dcf_queue;
>> +	int irq;
>> +	int wait_dcf_flag;
>> +	struct regulator *vdd_center;
>> +	unsigned long rate, target_rate;
>> +	unsigned long volt, target_volt;
>> +};
>> +
>> +static int rk3399_dmcfreq_target(struct device *dev, unsigned long *freq,
>> +				 u32 flags)
>> +{
>> +	struct platform_device *pdev = container_of(dev, struct platform_device,
>> +						    dev);
>> +	struct rk3399_dmcfreq *dmcfreq = platform_get_drvdata(pdev);
>> +	struct dev_pm_opp *opp;
>> +	unsigned long old_clk_rate = dmcfreq->rate;
>> +	unsigned long target_volt, target_rate;
>> +	int err;
>> +
>> +	rcu_read_lock();
>> +	opp = devfreq_recommended_opp(dev, freq, flags);
>> +	if (IS_ERR(opp)) {
>> +		rcu_read_unlock();
>> +		return PTR_ERR(opp);
>> +	}
>> +	target_rate = dev_pm_opp_get_freq(opp);
>> +	target_volt = dev_pm_opp_get_voltage(opp);
>> +	opp = devfreq_recommended_opp(dev, &dmcfreq->rate, flags);
>> +	if (IS_ERR(opp)) {
>> +		rcu_read_unlock();
>> +		return PTR_ERR(opp);
>> +	}
>> +	dmcfreq->volt = dev_pm_opp_get_voltage(opp);
>> +	rcu_read_unlock();
>> +
>> +	if (dmcfreq->rate == target_rate)
>> +		return 0;
>> +
>> +	mutex_lock(&dmcfreq->lock);
>> +	/*
>> +	 * if frequency scaling from low to high, adjust voltage first;
>> +	 * if frequency scaling from high to low, adjuset frequency first;
>> +	 */
>> +	if (old_clk_rate < target_rate) {
>> +		err = regulator_set_voltage(dmcfreq->vdd_center, target_volt,
>> +					    target_volt);
>> +		if (err) {
>> +			dev_err(dev, "Unable to set vol %lu\n", target_volt);
>> +			goto out;
>> +		}
>> +	}
>> +
>> +	dmc_event(DMCFREQ_ADJUST);
> You don't need to send the notification in target function.
> When you use the DEVFREQ_TRANSITION_NOTIFIER notifier,
> devfreq send the notification before calling the target() and after calling the target().
I checked DEVFREQ_TRANSISTION_NOTIFIER notifier before, but i have some 
question:
Now i want to use
     DEVFREQ_PRECHANGE and DEVFREQ_POSTCHANGE in vop driver, but it hard 
to get the *devfreq
in vop driver, or i can register a DEVFREQ_TRANSITION_NOTIFIER notifier 
in rk3399_dmc.c and
when get message, post DEVFREQ_PRECHANGE or DEVFREQ_POSTCHANGE to vop 
driver through dmc_event(),
do you have a better way to do it?
>
>> +	dmcfreq->wait_dcf_flag = 1;
>> +
>> +	err = clk_set_rate(dmcfreq->dmc_clk, target_rate);
>> +	if (err) {
>> +		dev_err(dev,
>> +			"Unable to set freq %lu. Current freq %lu. Error %d\n",
>> +			target_rate, old_clk_rate, err);
>> +		regulator_set_voltage(dmcfreq->vdd_center, dmcfreq->volt,
>> +				      dmcfreq->volt);
>> +		dmc_event(DMCFREQ_FINISH);
> ditto.
>
>> +		goto out;
>> +	}
>> +
>> +	/*
>> +	 * wait until bcf irq happen, it means freq scaling finish in bl31,
>> +	 * use 100ms as timeout time
>> +	 */
>> +	wait_event_timeout(dmcfreq->wait_dcf_queue,
>> +			   !dmcfreq->wait_dcf_flag, HZ / 10);
>> +
>> +	dmc_event(DMCFREQ_FINISH);
> ditto.
>
>> +
>> +	/*
>> +	 * check the dpll rate
>> +	 * there only two result we will get,
>> +	 * 1. ddr frequency scaling fail, we still get the old rate
>> +	 * 2, ddr frequency scaling sucessful, we get the rate we set
>> +	 */
>> +	dmcfreq->rate = clk_get_rate(dmcfreq->dmc_clk);
>> +
>> +	/* if get the incorrect rate, set voltage to old value */
>> +	if (dmcfreq->rate != target_rate) {
>> +		dev_err(dev, "get wrong ddr frequency, Request freq %lu,\
>> +			Current freq %lu\n", target_rate, dmcfreq->rate);
>> +		regulator_set_voltage(dmcfreq->vdd_center, dmcfreq->volt,
>> +				      dmcfreq->volt);
>> +	} else if (old_clk_rate > target_rate)
>> +		err = regulator_set_voltage(dmcfreq->vdd_center, target_volt,
>> +					    target_volt);
>> +	if (err)
>> +		dev_err(dev, "Unable to set vol %lu\n", target_volt);
>> +
>> +out:
>> +	mutex_unlock(&dmcfreq->lock);
>> +	return err;
>> +}
>> +
>> +static int rk3399_dmcfreq_get_dev_status(struct device *dev,
>> +					 struct devfreq_dev_status *stat)
>> +{
>> +	struct platform_device *pdev = container_of(dev, struct platform_device,
>> +						    dev);
>> +	struct rk3399_dmcfreq *dmcfreq = platform_get_drvdata(pdev);
>> +	struct devfreq_event_data edata;
>> +
>> +	devfreq_event_get_event(dmcfreq->edev, &edata);
>> +
>> +	stat->current_frequency = dmcfreq->rate;
>> +	stat->busy_time = edata.load_count;
>> +	stat->total_time = edata.total_count;
>> +
>> +	return 0;
>> +}
>> +
>> +static int rk3399_dmcfreq_get_cur_freq(struct device *dev, unsigned long *freq)
>> +{
>> +	struct platform_device *pdev = container_of(dev, struct platform_device,
>> +						    dev);
>> +	struct rk3399_dmcfreq *dmcfreq = platform_get_drvdata(pdev);
>> +
>> +	*freq = dmcfreq->rate;
>> +
>> +	return 0;
>> +}
>> +
>> +static void rk3399_dmcfreq_exit(struct device *dev)
>> +{
>> +	struct platform_device *pdev = container_of(dev, struct platform_device,
>> +						    dev);
>> +	struct rk3399_dmcfreq *dmcfreq = platform_get_drvdata(pdev);
>> +
>> +	devfreq_unregister_opp_notifier(dev, dmcfreq->devfreq);
>> +}
>> +
>> +static struct devfreq_dev_profile rk3399_devfreq_dmc_profile = {
>> +	.polling_ms	= 200,
>> +	.target		= rk3399_dmcfreq_target,
>> +	.get_dev_status	= rk3399_dmcfreq_get_dev_status,
>> +	.get_cur_freq	= rk3399_dmcfreq_get_cur_freq,
>> +	.exit		= rk3399_dmcfreq_exit,
>> +};
>> +
>> +static __maybe_unused int rk3399_dmcfreq_suspend(struct device *dev)
>> +{
>> +	rockchip_dmc_disable();
>> +	return 0;
>> +}
>> +
>> +static __maybe_unused int rk3399_dmcfreq_resume(struct device *dev)
>> +{
>> +	rockchip_dmc_enable();
>> +	return 0;
>> +}
>> +
>> +static SIMPLE_DEV_PM_OPS(rk3399_dmcfreq_pm, rk3399_dmcfreq_suspend,
>> +			 rk3399_dmcfreq_resume);
>> +
>> +static int rk3399_dmc_enable_notify(struct notifier_block *nb,
>> +				    unsigned long event, void *data)
>> +{
>> +	struct rk3399_dmcfreq *dmcfreq =
>> +			      container_of(nb, struct rk3399_dmcfreq, dmc_nb);
>> +	unsigned long freq = ULONG_MAX;
>> +
>> +	if (event == DMC_ENABLE) {
>> +		devfreq_event_enable_edev(dmcfreq->edev);
>> +		devfreq_resume_device(dmcfreq->devfreq);
>> +		return NOTIFY_OK;
>> +	} else if (event == DMC_DISABLE) {
>> +		devfreq_event_disable_edev(dmcfreq->edev);
>> +		devfreq_suspend_device(dmcfreq->devfreq);
>> +
>> +		/* when disable dmc, set sdram to max frequency */
>> +		rk3399_dmcfreq_target(dmcfreq->dev, &freq, 0);
>> +		return NOTIFY_OK;
>> +	}
>> +
>> +	return NOTIFY_DONE;
>> +}
>> +
>> +static irqreturn_t rk3399_dmc_irq(int irq, void *dev_id)
>> +{
>> +	struct rk3399_dmcfreq *dmcfreq = dev_id;
>> +
>> +	dmcfreq->wait_dcf_flag = 0;
>> +	wake_up(&dmcfreq->wait_dcf_queue);
>> +
>> +	/* clr dcf irq */
>> +	sip_smc_clr_ddr_irq();
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static int of_do_get_timing(struct device_node *np,
>> +		struct dram_timing *timing)
>> +{
>> +	int ret;
>> +
>> +	ret = of_property_read_u32(np, "ddr3_speed_bin",
>> +				   &timing->ddr3_speed_bin);
>> +	ret |= of_property_read_u32(np, "pd_idle", &timing->pd_idle);
>> +	ret |= of_property_read_u32(np, "sr_idle", &timing->sr_idle);
>> +	ret |= of_property_read_u32(np, "sr_mc_gate_idle",
>> +				    &timing->sr_mc_gate_idle);
>> +	ret |= of_property_read_u32(np, "srpd_lite_idle",
>> +				    &timing->srpd_lite_idle);
>> +	ret |= of_property_read_u32(np, "standby_idle", &timing->standby_idle);
>> +	ret |= of_property_read_u32(np, "dram_dll_dis_freq",
>> +				    &timing->dram_dll_dis_freq);
>> +	ret |= of_property_read_u32(np, "phy_dll_dis_freq",
>> +				    &timing->phy_dll_dis_freq);
>> +	ret |= of_property_read_u32(np, "ddr3_odt_dis_freq",
>> +				    &timing->ddr3_odt_dis_freq);
>> +	ret |= of_property_read_u32(np, "ddr3_drv", &timing->ddr3_drv);
>> +	ret |= of_property_read_u32(np, "ddr3_odt", &timing->ddr3_odt);
>> +	ret |= of_property_read_u32(np, "phy_ddr3_ca_drv",
>> +				    &timing->phy_ddr3_ca_drv);
>> +	ret |= of_property_read_u32(np, "phy_ddr3_dq_drv",
>> +				    &timing->phy_ddr3_dq_drv);
>> +	ret |= of_property_read_u32(np, "phy_ddr3_odt", &timing->phy_ddr3_odt);
>> +	ret |= of_property_read_u32(np, "lpddr3_odt_dis_freq",
>> +				    &timing->lpddr3_odt_dis_freq);
>> +	ret |= of_property_read_u32(np, "lpddr3_drv", &timing->lpddr3_drv);
>> +	ret |= of_property_read_u32(np, "lpddr3_odt", &timing->lpddr3_odt);
>> +	ret |= of_property_read_u32(np, "phy_lpddr3_ca_drv",
>> +				    &timing->phy_lpddr3_ca_drv);
>> +	ret |= of_property_read_u32(np, "phy_lpddr3_dq_drv",
>> +				    &timing->phy_lpddr3_dq_drv);
>> +	ret |= of_property_read_u32(np, "phy_lpddr3_odt",
>> +				    &timing->phy_lpddr3_odt);
>> +	ret |= of_property_read_u32(np, "lpddr4_odt_dis_freq",
>> +				    &timing->lpddr4_odt_dis_freq);
>> +	ret |= of_property_read_u32(np, "lpddr4_drv",
>> +				    &timing->lpddr4_drv);
>> +	ret |= of_property_read_u32(np, "lpddr4_dq_odt",
>> +				    &timing->lpddr4_dq_odt);
>> +	ret |= of_property_read_u32(np, "lpddr4_ca_odt",
>> +				    &timing->lpddr4_ca_odt);
>> +	ret |= of_property_read_u32(np, "phy_lpddr4_ca_drv",
>> +				    &timing->phy_lpddr4_ca_drv);
>> +	ret |= of_property_read_u32(np, "phy_lpddr4_ck_cs_drv",
>> +				    &timing->phy_lpddr4_ck_cs_drv);
>> +	ret |= of_property_read_u32(np, "phy_lpddr4_dq_drv",
>> +				    &timing->phy_lpddr4_dq_drv);
>> +	ret |= of_property_read_u32(np, "phy_lpddr4_odt",
>> +				    &timing->phy_lpddr4_odt);
>> +
>> +	return ret;
>> +}
>> +
>> +static struct dram_timing *of_get_ddr_timings(struct device *dev,
>> +					      struct device_node *np)
>> +{
>> +	struct dram_timing	*timing = NULL;
>> +	struct device_node	*np_tim;
>> +
>> +	np_tim = of_parse_phandle(np, "ddr_timing", 0);
>> +	if (np_tim) {
>> +		timing = devm_kzalloc(dev, sizeof(*timing), GFP_KERNEL);
>> +		if (!timing)
>> +			goto err;
>> +		if (of_do_get_timing(np_tim, timing)) {
>> +			devm_kfree(dev, timing);
>> +			goto err;
>> +		}
>> +		return timing;
>> +	}
>> +
>> +err:
>> +	if (timing) {
>> +		devm_kfree(dev, timing);
>> +		timing = NULL;
>> +	}
>> +
>> +	return timing;
>> +}
>> +
>> +static int rk3399_dmcfreq_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct rk3399_dmcfreq *data;
>> +	int ret, irq, index;
>> +	uint64_t param = 0;
>> +	uint32_t *timing;
>> +	struct device_node *np = pdev->dev.of_node;
>> +
>> +	irq = platform_get_irq(pdev, 0);
>> +	if (irq < 0) {
>> +		dev_err(&pdev->dev, "no dmc irq resource\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	data = devm_kzalloc(dev, sizeof(struct rk3399_dmcfreq), GFP_KERNEL);
>> +	if (!data)
>> +		return -ENOMEM;
>> +
>> +	mutex_init(&data->lock);
>> +
>> +	data->vdd_center = devm_regulator_get(dev, "center");
>> +	if (IS_ERR(data->vdd_center)) {
>> +		dev_err(dev, "Cannot get the regulator \"center\"\n");
>> +		return PTR_ERR(data->vdd_center);
>> +	}
>> +
>> +	data->dmc_clk = devm_clk_get(dev, "dmc_clk");
>> +	if (IS_ERR(data->dmc_clk)) {
>> +		dev_err(dev, "Cannot get the clk dmc_clk\n");
>> +		return PTR_ERR(data->dmc_clk);
>> +	};
>> +
>> +	data->edev = devfreq_event_get_edev_by_phandle(dev, 0);
>> +	if (IS_ERR(data->edev))
>> +		return -EPROBE_DEFER;
>> +
>> +	ret = devfreq_event_enable_edev(data->edev);
>> +	if (ret < 0) {
>> +		dev_err(dev, "failed to enable devfreq-event devices\n");
>> +		return ret;
>> +	}
>> +
>> +	/*
>> +	 * We add a devfreq driver to our parent since it has a device tree node
>> +	 * with operating points.
>> +	 */
>> +	if (dev_pm_opp_of_add_table(dev)) {
>> +		dev_err(dev, "Invalid operating-points in device tree.\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	of_property_read_u32(np, "upthreshold",
>> +			     &data->ondemand_data.upthreshold);
>> +
>> +	of_property_read_u32(np, "downdifferential",
>> +			     &data->ondemand_data.downdifferential);
>> +
>> +	data->rate = clk_get_rate(data->dmc_clk);
>> +	rk3399_devfreq_dmc_profile.initial_freq = data->rate;
>> +	data->devfreq = devfreq_add_device(dev,
>> +					   &rk3399_devfreq_dmc_profile,
>> +					   "simple_ondemand",
>> +					   &data->ondemand_data);
>> +	if (IS_ERR(data->devfreq))
>> +		return PTR_ERR(data->devfreq);
>> +
>> +	devfreq_register_opp_notifier(dev, data->devfreq);
>> +
>> +	data->dmc_nb.notifier_call = rk3399_dmc_enable_notify;
>> +	dmc_register_notifier(&data->dmc_nb);
>> +	data->irq = irq;
>> +	ret = devm_request_irq(dev, irq, rk3399_dmc_irq, 0,
>> +			       dev_name(dev), data);
>> +	if (ret) {
>> +		dev_err(dev, "failed to request dmc irq: %d\n", ret);
>> +		return ret;
>> +	}
>> +
>> +	/* get dram timing and pass it to bl31 */
>> +	data->timing = of_get_ddr_timings(dev, np);
>> +	if (data->timing) {
>> +		timing = (uint32_t *)data->timing;
>> +		for (index = 0; index < (sizeof(struct dram_timing) / 4);
>> +		     index++) {
>> +			param = index;
>> +			param = param << 32 | *timing++;
>> +			sip_smc_set_ddr_param(param);
>> +			param = 0;
>> +		}
>> +	}
>> +	sip_smc_ddr_init();
>> +
>> +	init_waitqueue_head(&data->wait_dcf_queue);
>> +	data->wait_dcf_flag = 0;
>> +
>> +	data->dev = dev;
>> +	platform_set_drvdata(pdev, data);
>> +
>> +	return 0;
>> +}
>> +
>> +static int rk3399_dmcfreq_remove(struct platform_device *pdev)
>> +{
>> +	struct rk3399_dmcfreq *dmcfreq = platform_get_drvdata(pdev);
>> +
>> +	devfreq_remove_device(dmcfreq->devfreq);
>> +	regulator_put(dmcfreq->vdd_center);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct of_device_id rk3399dmc_devfreq_of_match[] = {
>> +	{ .compatible = "rockchip,rk3399-dmc" },
>> +	{ },
>> +};
>> +
>> +static struct platform_driver rk3399_dmcfreq_driver = {
>> +	.probe	= rk3399_dmcfreq_probe,
>> +	.remove	= rk3399_dmcfreq_remove,
>> +	.driver = {
>> +		.name	= "rk3399-dmc-freq",
>> +		.pm	= &rk3399_dmcfreq_pm,
>> +		.of_match_table = rk3399dmc_devfreq_of_match,
>> +	},
>> +};
>> +module_platform_driver(rk3399_dmcfreq_driver);
>> +
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_DESCRIPTION("RK3399 dmcfreq driver with devfreq framework");
>> diff --git a/drivers/devfreq/rockchip/rockchip_dmc.c b/drivers/devfreq/rockchip/rockchip_dmc.c
>> new file mode 100644
>> index 0000000..78baee5
>> --- /dev/null
>> +++ b/drivers/devfreq/rockchip/rockchip_dmc.c
>> @@ -0,0 +1,143 @@
>> +/*
>> + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
>> + * Author: Lin Huang <hl at rock-chips.com>
>> + * Base on: https://chromium-review.googlesource.com/#/c/231477/
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms and conditions of the GNU General Public License,
>> + * version 2, as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope 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/mutex.h>
>> +#include <soc/rockchip/rockchip_dmc.h>
>> +
>> +static int num_wait;
>> +static int num_disable;
>> +static BLOCKING_NOTIFIER_HEAD(dmc_notifier_list);
>> +static DEFINE_MUTEX(dmc_en_lock);
>> +static DEFINE_MUTEX(dmc_sync_lock);
>> +
>> +/**
>> + * rockchip_dmc_enabled - Returns true if dmc freq is enabled, false otherwise.
>> + */
>> +bool rockchip_dmc_enabled(void)
>> +{
>> +	return num_disable <= 0 && num_wait <= 1;
>> +}
>> +
>> +/**
>> + * rockchip_dmc_enable - Enable dmc frequency scaling. Will only enable
>> + * frequency scaling if there are 1 or fewer notifiers. Call to undo
>> + * rockchip_dmc_disable.
>> + */
>> +void rockchip_dmc_enable(void)
>> +{
>> +	mutex_lock(&dmc_en_lock);
>> +	num_disable--;
>> +	WARN_ON(num_disable < 0);
>> +	if (rockchip_dmc_enabled())
>> +		dmc_event(DMC_ENABLE);
>> +	mutex_unlock(&dmc_en_lock);
>> +}
>> +
>> +/**
>> + * rockchip_dmc_disable - Disable dmc frequency scaling. Call when something
>> + * cannot coincide with dmc frequency scaling.
>> + */
>> +void rockchip_dmc_disable(void)
>> +{
>> +	mutex_lock(&dmc_en_lock);
>> +	if (rockchip_dmc_enabled())
>> +		dmc_event(DMC_DISABLE);
>> +	num_disable++;
>> +	mutex_unlock(&dmc_en_lock);
>> +}
>> +
>> +void dmc_event(int event)
>> +{
>> +	mutex_lock(&dmc_sync_lock);
>> +	blocking_notifier_call_chain(&dmc_notifier_list, event, NULL);
>> +	mutex_unlock(&dmc_sync_lock);
>> +}
>> +
>> +/**
>> + * dmc_register_notifier - register a driver to dmc chain
>> + * @nb: notifier function to register
>> + */
>> +int dmc_register_notifier(struct notifier_block *nb)
>> +{
>> +	int ret;
>> +
>> +	if (!nb)
>> +		return -EINVAL;
>> +
>> +	ret = blocking_notifier_chain_register(&dmc_notifier_list, nb);
>> +
>> +	return ret;
>> +}
>> +
>> +/**
>> + * dmc_unregister_notifier - unregister a driver from dmc chain
>> + * @nb: remove notifier function
>> + */
>> +int dmc_unregister_notifier(struct notifier_block *nb)
>> +{
>> +	int ret;
>> +
>> +	if (!nb)
>> +		return -EINVAL;
>> +
>> +	ret = blocking_notifier_chain_unregister(&dmc_notifier_list, nb);
>> +
>> +	return ret;
>> +}
>> +
>> +/**
>> + * rockchip_dmc_get - Register the notifier block for the dmc chain.
>> + * @nb The dmc notifier block to register
>> + */
>> +int rockchip_dmc_get(struct notifier_block *nb)
>> +{
>> +	if (!nb)
>> +		return -EINVAL;
>> +
>> +	mutex_lock(&dmc_en_lock);
>> +
>> +	/*
>> +	 * if have two notifier(enable two vop etc),
>> +	 * need to disable dmc
>> +	 */
>> +	if (num_wait == 1 && num_disable <= 0)
>> +		dmc_event(DMC_DISABLE);
>> +	num_wait++;
>> +	dmc_register_notifier(nb);
>> +	mutex_unlock(&dmc_en_lock);
>> +
>> +	return 0;
>> +}
>> +
>> +/**
>> + * rockchip_dmc_put - Remove the notifier block from the dmc chain.
>> + * @nb The dmc notifier block to unregister
>> + */
>> +int rockchip_dmc_put(struct notifier_block *nb)
>> +{
>> +	if (!nb)
>> +		return -EINVAL;
>> +
>> +	mutex_lock(&dmc_en_lock);
>> +	num_wait--;
>> +
>> +	/* if notifier from 2 back to 1, enable dmc again */
>> +	if (num_wait == 1 && num_disable <= 0)
>> +		dmc_event(DMC_ENABLE);
>> +	dmc_unregister_notifier(nb);
>> +	mutex_unlock(&dmc_en_lock);
>> +
>> +	return 0;
>> +}
>> diff --git a/include/soc/rockchip/rockchip_dmc.h b/include/soc/rockchip/rockchip_dmc.h
>> new file mode 100644
>> index 0000000..3f69cbf
>> --- /dev/null
>> +++ b/include/soc/rockchip/rockchip_dmc.h
>> @@ -0,0 +1,45 @@
>> +/*
>> + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms and conditions of the GNU General Public License,
>> + * version 2, as published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope 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.
>> + */
>> +
>> +#ifndef __SOC_ROCKCHIP_DMC_H
>> +#define __SOC_ROCKCHIP_DMC_H
>> +
>> +#include <linux/notifier.h>
>> +
>> +#define DMC_ENABLE	0
>> +#define DMC_DISABLE	1
>> +#define DMCFREQ_ADJUST	2
>> +#define DMCFREQ_FINISH	3
>> +
>> +#if IS_ENABLED(CONFIG_ARM_ROCKCHIP_DMC_DEVFREQ)
>> +int rockchip_dmc_get(struct notifier_block *nb);
>> +int rockchip_dmc_put(struct notifier_block *nb);
>> +#else
>> +static inline int rockchip_dmc_get(struct notifier_block *nb)
>> +{
>> +	return 0;
>> +}
>> +
>> +static inline int rockchip_dmc_put(struct notifier_block *nb)
>> +{
>> +	return 0;
>> +}
>> +#endif
>> +
>> +void dmc_event(int event);
>> +int dmc_register_notifier(struct notifier_block *nb);
>> +int dmc_unregister_notifier(struct notifier_block *nb);
>> +void rockchip_dmc_enable(void);
>> +void rockchip_dmc_disable(void);
>> +bool rockchip_dmc_enabled(void);
>> +#endif
>>
> Regards,
> Chanwoo Choi
>
>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip

-- 
Lin Huang




From ulf.hansson at linaro.org  Mon Jul 25 01:49:02 2016
From: ulf.hansson at linaro.org (Ulf Hansson)
Date: Mon, 25 Jul 2016 10:49:02 +0200
Subject: [PATCH 0/3] mmc: Fixes for 150 MHz Rockchip eMMC series
In-Reply-To: <1467049167-14628-1-git-send-email-dianders@chromium.org>
References: <1467049167-14628-1-git-send-email-dianders@chromium.org>
Message-ID: <CAPDyKFpT=5LHO49QrO=MuQUgrDULx5zug=L46SihEReNo5h7iA@mail.gmail.com>

On 27 June 2016 at 19:39, Douglas Anderson <dianders at chromium.org> wrote:
> Despite positive testing across a number of machines and a number of
> people, last week I got a report that my 150 MHz series was breaking
> things on a handful of boards.
>
> It turns out that reverting the patch to always power cycle across clock
> changes fixes things and reverting that patch also doesn't actually
> cause any known problems (and 150 MHz should even continue to work).
>
> Although we really need to get to the bottom of things, it seems
> expedient to revert while we're waiting for a better solution, hence
> this series.  Also in this series is further increase in the time we'll
> wait for the DLL to stabilize (found during reboot stress testing) and a
> fix to the way we handle the card clock being reported as 0 (needed for
> suspend/resume because of the revert we just did).
>
> It's expected that this series could go through Ulf's mmc tree (like the
> previous) with Kishon's Ack as appropriate.
>
>
> Douglas Anderson (3):
>   mmc: sdhci-of-arasan: Revert: Always power the PHY off/on when clock
>     changes
>   phy: rockchip-emmc: Be tolerant to card clock of 0 in power on
>   phy: rockchip-emmc: Wait even longer for the DLL to lock
>
>  drivers/mmc/host/sdhci-of-arasan.c | 21 +++++++----
>  drivers/phy/phy-rockchip-emmc.c    | 71 ++++++++++++++++++++++++++------------
>  2 files changed, 62 insertions(+), 30 deletions(-)
>
> --
> 2.8.0.rc3.226.g39d4020
>

Thanks, applied for next!

Kind regards
Uffe


From cw00.choi at samsung.com  Mon Jul 25 02:45:52 2016
From: cw00.choi at samsung.com (Chanwoo Choi)
Date: Mon, 25 Jul 2016 18:45:52 +0900
Subject: [PATCH v3 6/7] PM / devfreq: rockchip: add devfreq driver for
 rk3399 dmc
In-Reply-To: <5795D216.7010003@rock-chips.com>
References: <1469178440-4668-1-git-send-email-hl@rock-chips.com>
 <CGME20160722090916epcas1p28b9a4db33945627f5e4c1157cd6786b7@epcas1p2.samsung.com>
 <1469178440-4668-7-git-send-email-hl@rock-chips.com>
 <5795AB4E.4010600@samsung.com> <5795D216.7010003@rock-chips.com>
Message-ID: <5795DFD0.7010901@samsung.com>

Hi Lin,

On 2016? 07? 25? 17:47, hl wrote:
> Hi Chanwoo Choi,
> 
> On 2016?07?25? 14:01, Chanwoo Choi wrote:
>> Hi Lin,
>>
>> I'm glad to support the for dmc ddr clock scaling with devfreq/devfreq-event.
>>
>> But, I think that you have to use the standard interface.
>> As I already mentioned[1] on previous mail, devfreq fwk support
>> the standard DEVFREQ_TRANSITION_NOTIFIER notifier which has the two notifications.
>> - DEVFREQ_PRECHANGE
>> - DEVFREQ_POSTCHANGE
>> [1] https://patchwork.kernel.org/patch/9194305/
>>
>> You can use the DEVFREQ_PRECHANGE instead of DMCFREQ_ADJUST
>> and use the DEVFREQ_POSTCHANGE instead of DMCFREQ_FINISH.
>>
>> On 2016? 07? 22? 18:07, Lin Huang wrote:
>>> base on dfi result, we do ddr frequency scaling, register
>>> dmc driver to devfreq framework, and use simple-ondemand
>>> policy.
>>>
>>> Signed-off-by: Lin Huang <hl at rock-chips.com>
>>> ---
>>> Changes in v3:
>>> - operate dram setting through sip call
>>> - imporve set rate flow
>>>
>>> Changes in v2:
>>> - None
>>>   Changes in v1:
>>> - move dfi controller to event
>>> - fix set voltage sequence when set rate fail
>>> - change Kconfig type from tristate to bool
>>> - move unuse EXPORT_SYMBOL_GPL()
>>>
>>>   drivers/devfreq/Kconfig                 |   1 +
>>>   drivers/devfreq/Makefile                |   1 +
>>>   drivers/devfreq/rockchip/Kconfig        |  15 +
>>>   drivers/devfreq/rockchip/Makefile       |   2 +
>>>   drivers/devfreq/rockchip/rk3399_dmc.c   | 482 ++++++++++++++++++++++++++++++++
>>>   drivers/devfreq/rockchip/rockchip_dmc.c | 143 ++++++++++
>>>   include/soc/rockchip/rockchip_dmc.h     |  45 +++
>>>   7 files changed, 689 insertions(+)
>>>   create mode 100644 drivers/devfreq/rockchip/Kconfig
>>>   create mode 100644 drivers/devfreq/rockchip/Makefile
>>>   create mode 100644 drivers/devfreq/rockchip/rk3399_dmc.c
>>>   create mode 100644 drivers/devfreq/rockchip/rockchip_dmc.c
>>>   create mode 100644 include/soc/rockchip/rockchip_dmc.h
>>>
>>> diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig
>>> index a5be56e..cb67246 100644
>>> --- a/drivers/devfreq/Kconfig
>>> +++ b/drivers/devfreq/Kconfig
>>> @@ -101,5 +101,6 @@ config ARM_TEGRA_DEVFREQ
>>>            operating frequencies and voltages with OPP support.
>>>     source "drivers/devfreq/event/Kconfig"
>>> +source "drivers/devfreq/rockchip/Kconfig"
>>>     endif # PM_DEVFREQ
>>> diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile
>>> index 09f11d9..48e2ae6 100644
>>> --- a/drivers/devfreq/Makefile
>>> +++ b/drivers/devfreq/Makefile
>>> @@ -9,6 +9,7 @@ obj-$(CONFIG_DEVFREQ_GOV_PASSIVE)    += governor_passive.o
>>>   # DEVFREQ Drivers
>>>   obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ)    += exynos-bus.o
>>>   obj-$(CONFIG_ARM_TEGRA_DEVFREQ)        += tegra-devfreq.o
>>> +obj-$(CONFIG_ARCH_ROCKCHIP)        += rockchip/
>>>     # DEVFREQ Event Drivers
>>>   obj-$(CONFIG_PM_DEVFREQ_EVENT)        += event/
>>> diff --git a/drivers/devfreq/rockchip/Kconfig b/drivers/devfreq/rockchip/Kconfig
>>> new file mode 100644
>>> index 0000000..7fb1cff
>>> --- /dev/null
>>> +++ b/drivers/devfreq/rockchip/Kconfig
>>> @@ -0,0 +1,15 @@
>>> +config ARM_ROCKCHIP_DMC_DEVFREQ
>>> +    bool "ARM ROCKCHIP DMC DEVFREQ Driver"
>>> +    depends on ARCH_ROCKCHIP
>>> +    help
>>> +      This adds the DEVFREQ driver framework for the rockchip dmc.
>>> +
>>> +config ARM_RK3399_DMC_DEVFREQ
>>> +    bool "ARM RK3399 DMC DEVFREQ Driver"
>>> +    depends on ARM_ROCKCHIP_DMC_DEVFREQ
>>> +    select PM_OPP
>>> +    select DEVFREQ_GOV_SIMPLE_ONDEMAND
>>> +    help
>>> +          This adds the DEVFREQ driver for the RK3399 dmc. It sets the frequency
>>> +          for the memory controller and reads the usage counts from hardware.
>>> +
>>> diff --git a/drivers/devfreq/rockchip/Makefile b/drivers/devfreq/rockchip/Makefile
>>> new file mode 100644
>>> index 0000000..caca525
>>> --- /dev/null
>>> +++ b/drivers/devfreq/rockchip/Makefile
>>> @@ -0,0 +1,2 @@
>>> +obj-$(CONFIG_ARM_ROCKCHIP_DMC_DEVFREQ)    += rockchip_dmc.o
>>> +obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ)    += rk3399_dmc.o
>>> diff --git a/drivers/devfreq/rockchip/rk3399_dmc.c b/drivers/devfreq/rockchip/rk3399_dmc.c
>>> new file mode 100644
>>> index 0000000..f1d6120
>>> --- /dev/null
>>> +++ b/drivers/devfreq/rockchip/rk3399_dmc.c
>>> @@ -0,0 +1,482 @@
>>> +/*
>>> + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
>>> + * Author: Lin Huang <hl at rock-chips.com>
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify it
>>> + * under the terms and conditions of the GNU General Public License,
>>> + * version 2, as published by the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope 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/clk.h>
>>> +#include <linux/completion.h>
>>> +#include <linux/delay.h>
>>> +#include <linux/devfreq.h>
>>> +#include <linux/devfreq-event.h>
>>> +#include <linux/interrupt.h>
>>> +#include <linux/module.h>
>>> +#include <linux/of.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/pm_opp.h>
>>> +#include <linux/regulator/consumer.h>
>>> +#include <linux/rwsem.h>
>>> +#include <linux/suspend.h>
>>> +#include <linux/syscore_ops.h>
>>> +
>>> +#include <soc/rockchip/rockchip_dmc.h>
>>> +#include "../../firmware/rockchip_sip.h"
>>> +
>>> +struct dram_timing {
>>> +    unsigned int ddr3_speed_bin;
>>> +    unsigned int pd_idle;
>>> +    unsigned int sr_idle;
>>> +    unsigned int sr_mc_gate_idle;
>>> +    unsigned int srpd_lite_idle;
>>> +    unsigned int standby_idle;
>>> +    unsigned int dram_dll_dis_freq;
>>> +    unsigned int phy_dll_dis_freq;
>>> +    unsigned int ddr3_odt_dis_freq;
>>> +    unsigned int ddr3_drv;
>>> +    unsigned int ddr3_odt;
>>> +    unsigned int phy_ddr3_ca_drv;
>>> +    unsigned int phy_ddr3_dq_drv;
>>> +    unsigned int phy_ddr3_odt;
>>> +    unsigned int lpddr3_odt_dis_freq;
>>> +    unsigned int lpddr3_drv;
>>> +    unsigned int lpddr3_odt;
>>> +    unsigned int phy_lpddr3_ca_drv;
>>> +    unsigned int phy_lpddr3_dq_drv;
>>> +    unsigned int phy_lpddr3_odt;
>>> +    unsigned int lpddr4_odt_dis_freq;
>>> +    unsigned int lpddr4_drv;
>>> +    unsigned int lpddr4_dq_odt;
>>> +    unsigned int lpddr4_ca_odt;
>>> +    unsigned int phy_lpddr4_ca_drv;
>>> +    unsigned int phy_lpddr4_ck_cs_drv;
>>> +    unsigned int phy_lpddr4_dq_drv;
>>> +    unsigned int phy_lpddr4_odt;
>>> +};
>>> +
>>> +struct rk3399_dmcfreq {
>>> +    struct device *dev;
>>> +    struct devfreq *devfreq;
>>> +    struct devfreq_simple_ondemand_data ondemand_data;
>>> +    struct clk *dmc_clk;
>>> +    struct devfreq_event_dev *edev;
>>> +    struct mutex lock;
>>> +    struct notifier_block dmc_nb;
>>> +    struct dram_timing *timing;
>>> +    wait_queue_head_t    wait_dcf_queue;
>>> +    int irq;
>>> +    int wait_dcf_flag;
>>> +    struct regulator *vdd_center;
>>> +    unsigned long rate, target_rate;
>>> +    unsigned long volt, target_volt;
>>> +};
>>> +
>>> +static int rk3399_dmcfreq_target(struct device *dev, unsigned long *freq,
>>> +                 u32 flags)
>>> +{
>>> +    struct platform_device *pdev = container_of(dev, struct platform_device,
>>> +                            dev);
>>> +    struct rk3399_dmcfreq *dmcfreq = platform_get_drvdata(pdev);
>>> +    struct dev_pm_opp *opp;
>>> +    unsigned long old_clk_rate = dmcfreq->rate;
>>> +    unsigned long target_volt, target_rate;
>>> +    int err;
>>> +
>>> +    rcu_read_lock();
>>> +    opp = devfreq_recommended_opp(dev, freq, flags);
>>> +    if (IS_ERR(opp)) {
>>> +        rcu_read_unlock();
>>> +        return PTR_ERR(opp);
>>> +    }
>>> +    target_rate = dev_pm_opp_get_freq(opp);
>>> +    target_volt = dev_pm_opp_get_voltage(opp);
>>> +    opp = devfreq_recommended_opp(dev, &dmcfreq->rate, flags);
>>> +    if (IS_ERR(opp)) {
>>> +        rcu_read_unlock();
>>> +        return PTR_ERR(opp);
>>> +    }
>>> +    dmcfreq->volt = dev_pm_opp_get_voltage(opp);
>>> +    rcu_read_unlock();
>>> +
>>> +    if (dmcfreq->rate == target_rate)
>>> +        return 0;
>>> +
>>> +    mutex_lock(&dmcfreq->lock);
>>> +    /*
>>> +     * if frequency scaling from low to high, adjust voltage first;
>>> +     * if frequency scaling from high to low, adjuset frequency first;
>>> +     */
>>> +    if (old_clk_rate < target_rate) {
>>> +        err = regulator_set_voltage(dmcfreq->vdd_center, target_volt,
>>> +                        target_volt);
>>> +        if (err) {
>>> +            dev_err(dev, "Unable to set vol %lu\n", target_volt);
>>> +            goto out;
>>> +        }
>>> +    }
>>> +
>>> +    dmc_event(DMCFREQ_ADJUST);
>> You don't need to send the notification in target function.
>> When you use the DEVFREQ_TRANSITION_NOTIFIER notifier,
>> devfreq send the notification before calling the target() and after calling the target().
> I checked DEVFREQ_TRANSISTION_NOTIFIER notifier before, but i have some question:
> Now i want to use
>     DEVFREQ_PRECHANGE and DEVFREQ_POSTCHANGE in vop driver, but it hard to get the *devfreq
> in vop driver, or i can register a DEVFREQ_TRANSITION_NOTIFIER notifier in rk3399_dmc.c and
> when get message, post DEVFREQ_PRECHANGE or DEVFREQ_POSTCHANGE to vop driver through dmc_event(),
> do you have a better way to do it?

Firstly, 
The devfreq has the devfreq_get_devfreq_by_phandle() function
to get the devfreq pointer in device tree. The vop_bind()
get the IORESOURCE_MEM and interrupt. So, I think that you
would get the devfreq pointer in the vop_bind() function.
So, DEVFREQ_TRANSITION_NOTIFIER can alter the following two notifications:
- dmc_event(DMCFREQ_ADJUST);
- dmc_event(DMCFREQ_FINISH);


Also,
devfreq-event fwk provide the devfreq_event_get_edev_by_phandle() funtcion
to get the devfreq-event pointer in the device tree. 

You could call the devfreq_event_[enable/disable[_edev()
and devfreq_[resume/suspend]_device in the drivers/gpu/drm/rockchip/rockchip_drm_vop.c
instead of dmc_event(DMC_ENABLE) / dmc_event(DMC_DISABLE).

dmc_event(DMC_ENABLE);
	devfreq_event_enable_edev(dmcfreq->edev);
	devfreq_resume_device(dmcfreq->devfreq);

dmc_event(DMC_DISABLE);
	devfreq_event_disable_edev(dmcfreq->edev);
	devfreq_suspend_device(dmcfreq->devfreq);

I think that we have to use the standard interfaces and
functions providing by framework. It is not appropriate way
to define the specific functions for only one device driver
between other framework.

Regards,
Chanwoo Choi

>>
>>> +    dmcfreq->wait_dcf_flag = 1;
>>> +
>>> +    err = clk_set_rate(dmcfreq->dmc_clk, target_rate);
>>> +    if (err) {
>>> +        dev_err(dev,
>>> +            "Unable to set freq %lu. Current freq %lu. Error %d\n",
>>> +            target_rate, old_clk_rate, err);
>>> +        regulator_set_voltage(dmcfreq->vdd_center, dmcfreq->volt,
>>> +                      dmcfreq->volt);
>>> +        dmc_event(DMCFREQ_FINISH);
>> ditto.
>>
>>> +        goto out;
>>> +    }
>>> +
>>> +    /*
>>> +     * wait until bcf irq happen, it means freq scaling finish in bl31,
>>> +     * use 100ms as timeout time
>>> +     */
>>> +    wait_event_timeout(dmcfreq->wait_dcf_queue,
>>> +               !dmcfreq->wait_dcf_flag, HZ / 10);
>>> +
>>> +    dmc_event(DMCFREQ_FINISH);
>> ditto.
>>
>>> +
>>> +    /*
>>> +     * check the dpll rate
>>> +     * there only two result we will get,
>>> +     * 1. ddr frequency scaling fail, we still get the old rate
>>> +     * 2, ddr frequency scaling sucessful, we get the rate we set
>>> +     */
>>> +    dmcfreq->rate = clk_get_rate(dmcfreq->dmc_clk);
>>> +
>>> +    /* if get the incorrect rate, set voltage to old value */
>>> +    if (dmcfreq->rate != target_rate) {
>>> +        dev_err(dev, "get wrong ddr frequency, Request freq %lu,\
>>> +            Current freq %lu\n", target_rate, dmcfreq->rate);
>>> +        regulator_set_voltage(dmcfreq->vdd_center, dmcfreq->volt,
>>> +                      dmcfreq->volt);
>>> +    } else if (old_clk_rate > target_rate)
>>> +        err = regulator_set_voltage(dmcfreq->vdd_center, target_volt,
>>> +                        target_volt);
>>> +    if (err)
>>> +        dev_err(dev, "Unable to set vol %lu\n", target_volt);
>>> +
>>> +out:
>>> +    mutex_unlock(&dmcfreq->lock);
>>> +    return err;
>>> +}
>>> +
>>> +static int rk3399_dmcfreq_get_dev_status(struct device *dev,
>>> +                     struct devfreq_dev_status *stat)
>>> +{
>>> +    struct platform_device *pdev = container_of(dev, struct platform_device,
>>> +                            dev);
>>> +    struct rk3399_dmcfreq *dmcfreq = platform_get_drvdata(pdev);
>>> +    struct devfreq_event_data edata;
>>> +
>>> +    devfreq_event_get_event(dmcfreq->edev, &edata);
>>> +
>>> +    stat->current_frequency = dmcfreq->rate;
>>> +    stat->busy_time = edata.load_count;
>>> +    stat->total_time = edata.total_count;
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int rk3399_dmcfreq_get_cur_freq(struct device *dev, unsigned long *freq)
>>> +{
>>> +    struct platform_device *pdev = container_of(dev, struct platform_device,
>>> +                            dev);
>>> +    struct rk3399_dmcfreq *dmcfreq = platform_get_drvdata(pdev);
>>> +
>>> +    *freq = dmcfreq->rate;
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static void rk3399_dmcfreq_exit(struct device *dev)
>>> +{
>>> +    struct platform_device *pdev = container_of(dev, struct platform_device,
>>> +                            dev);
>>> +    struct rk3399_dmcfreq *dmcfreq = platform_get_drvdata(pdev);
>>> +
>>> +    devfreq_unregister_opp_notifier(dev, dmcfreq->devfreq);
>>> +}
>>> +
>>> +static struct devfreq_dev_profile rk3399_devfreq_dmc_profile = {
>>> +    .polling_ms    = 200,
>>> +    .target        = rk3399_dmcfreq_target,
>>> +    .get_dev_status    = rk3399_dmcfreq_get_dev_status,
>>> +    .get_cur_freq    = rk3399_dmcfreq_get_cur_freq,
>>> +    .exit        = rk3399_dmcfreq_exit,
>>> +};
>>> +
>>> +static __maybe_unused int rk3399_dmcfreq_suspend(struct device *dev)
>>> +{
>>> +    rockchip_dmc_disable();
>>> +    return 0;
>>> +}
>>> +
>>> +static __maybe_unused int rk3399_dmcfreq_resume(struct device *dev)
>>> +{
>>> +    rockchip_dmc_enable();
>>> +    return 0;
>>> +}
>>> +
>>> +static SIMPLE_DEV_PM_OPS(rk3399_dmcfreq_pm, rk3399_dmcfreq_suspend,
>>> +             rk3399_dmcfreq_resume);
>>> +
>>> +static int rk3399_dmc_enable_notify(struct notifier_block *nb,
>>> +                    unsigned long event, void *data)
>>> +{
>>> +    struct rk3399_dmcfreq *dmcfreq =
>>> +                  container_of(nb, struct rk3399_dmcfreq, dmc_nb);
>>> +    unsigned long freq = ULONG_MAX;
>>> +
>>> +    if (event == DMC_ENABLE) {
>>> +        devfreq_event_enable_edev(dmcfreq->edev);
>>> +        devfreq_resume_device(dmcfreq->devfreq);
>>> +        return NOTIFY_OK;
>>> +    } else if (event == DMC_DISABLE) {
>>> +        devfreq_event_disable_edev(dmcfreq->edev);
>>> +        devfreq_suspend_device(dmcfreq->devfreq);
>>> +
>>> +        /* when disable dmc, set sdram to max frequency */
>>> +        rk3399_dmcfreq_target(dmcfreq->dev, &freq, 0);
>>> +        return NOTIFY_OK;
>>> +    }
>>> +
>>> +    return NOTIFY_DONE;
>>> +}
>>> +
>>> +static irqreturn_t rk3399_dmc_irq(int irq, void *dev_id)
>>> +{
>>> +    struct rk3399_dmcfreq *dmcfreq = dev_id;
>>> +
>>> +    dmcfreq->wait_dcf_flag = 0;
>>> +    wake_up(&dmcfreq->wait_dcf_queue);
>>> +
>>> +    /* clr dcf irq */
>>> +    sip_smc_clr_ddr_irq();
>>> +
>>> +    return IRQ_HANDLED;
>>> +}
>>> +
>>> +static int of_do_get_timing(struct device_node *np,
>>> +        struct dram_timing *timing)
>>> +{
>>> +    int ret;
>>> +
>>> +    ret = of_property_read_u32(np, "ddr3_speed_bin",
>>> +                   &timing->ddr3_speed_bin);
>>> +    ret |= of_property_read_u32(np, "pd_idle", &timing->pd_idle);
>>> +    ret |= of_property_read_u32(np, "sr_idle", &timing->sr_idle);
>>> +    ret |= of_property_read_u32(np, "sr_mc_gate_idle",
>>> +                    &timing->sr_mc_gate_idle);
>>> +    ret |= of_property_read_u32(np, "srpd_lite_idle",
>>> +                    &timing->srpd_lite_idle);
>>> +    ret |= of_property_read_u32(np, "standby_idle", &timing->standby_idle);
>>> +    ret |= of_property_read_u32(np, "dram_dll_dis_freq",
>>> +                    &timing->dram_dll_dis_freq);
>>> +    ret |= of_property_read_u32(np, "phy_dll_dis_freq",
>>> +                    &timing->phy_dll_dis_freq);
>>> +    ret |= of_property_read_u32(np, "ddr3_odt_dis_freq",
>>> +                    &timing->ddr3_odt_dis_freq);
>>> +    ret |= of_property_read_u32(np, "ddr3_drv", &timing->ddr3_drv);
>>> +    ret |= of_property_read_u32(np, "ddr3_odt", &timing->ddr3_odt);
>>> +    ret |= of_property_read_u32(np, "phy_ddr3_ca_drv",
>>> +                    &timing->phy_ddr3_ca_drv);
>>> +    ret |= of_property_read_u32(np, "phy_ddr3_dq_drv",
>>> +                    &timing->phy_ddr3_dq_drv);
>>> +    ret |= of_property_read_u32(np, "phy_ddr3_odt", &timing->phy_ddr3_odt);
>>> +    ret |= of_property_read_u32(np, "lpddr3_odt_dis_freq",
>>> +                    &timing->lpddr3_odt_dis_freq);
>>> +    ret |= of_property_read_u32(np, "lpddr3_drv", &timing->lpddr3_drv);
>>> +    ret |= of_property_read_u32(np, "lpddr3_odt", &timing->lpddr3_odt);
>>> +    ret |= of_property_read_u32(np, "phy_lpddr3_ca_drv",
>>> +                    &timing->phy_lpddr3_ca_drv);
>>> +    ret |= of_property_read_u32(np, "phy_lpddr3_dq_drv",
>>> +                    &timing->phy_lpddr3_dq_drv);
>>> +    ret |= of_property_read_u32(np, "phy_lpddr3_odt",
>>> +                    &timing->phy_lpddr3_odt);
>>> +    ret |= of_property_read_u32(np, "lpddr4_odt_dis_freq",
>>> +                    &timing->lpddr4_odt_dis_freq);
>>> +    ret |= of_property_read_u32(np, "lpddr4_drv",
>>> +                    &timing->lpddr4_drv);
>>> +    ret |= of_property_read_u32(np, "lpddr4_dq_odt",
>>> +                    &timing->lpddr4_dq_odt);
>>> +    ret |= of_property_read_u32(np, "lpddr4_ca_odt",
>>> +                    &timing->lpddr4_ca_odt);
>>> +    ret |= of_property_read_u32(np, "phy_lpddr4_ca_drv",
>>> +                    &timing->phy_lpddr4_ca_drv);
>>> +    ret |= of_property_read_u32(np, "phy_lpddr4_ck_cs_drv",
>>> +                    &timing->phy_lpddr4_ck_cs_drv);
>>> +    ret |= of_property_read_u32(np, "phy_lpddr4_dq_drv",
>>> +                    &timing->phy_lpddr4_dq_drv);
>>> +    ret |= of_property_read_u32(np, "phy_lpddr4_odt",
>>> +                    &timing->phy_lpddr4_odt);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static struct dram_timing *of_get_ddr_timings(struct device *dev,
>>> +                          struct device_node *np)
>>> +{
>>> +    struct dram_timing    *timing = NULL;
>>> +    struct device_node    *np_tim;
>>> +
>>> +    np_tim = of_parse_phandle(np, "ddr_timing", 0);
>>> +    if (np_tim) {
>>> +        timing = devm_kzalloc(dev, sizeof(*timing), GFP_KERNEL);
>>> +        if (!timing)
>>> +            goto err;
>>> +        if (of_do_get_timing(np_tim, timing)) {
>>> +            devm_kfree(dev, timing);
>>> +            goto err;
>>> +        }
>>> +        return timing;
>>> +    }
>>> +
>>> +err:
>>> +    if (timing) {
>>> +        devm_kfree(dev, timing);
>>> +        timing = NULL;
>>> +    }
>>> +
>>> +    return timing;
>>> +}
>>> +
>>> +static int rk3399_dmcfreq_probe(struct platform_device *pdev)
>>> +{
>>> +    struct device *dev = &pdev->dev;
>>> +    struct rk3399_dmcfreq *data;
>>> +    int ret, irq, index;
>>> +    uint64_t param = 0;
>>> +    uint32_t *timing;
>>> +    struct device_node *np = pdev->dev.of_node;
>>> +
>>> +    irq = platform_get_irq(pdev, 0);
>>> +    if (irq < 0) {
>>> +        dev_err(&pdev->dev, "no dmc irq resource\n");
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    data = devm_kzalloc(dev, sizeof(struct rk3399_dmcfreq), GFP_KERNEL);
>>> +    if (!data)
>>> +        return -ENOMEM;
>>> +
>>> +    mutex_init(&data->lock);
>>> +
>>> +    data->vdd_center = devm_regulator_get(dev, "center");
>>> +    if (IS_ERR(data->vdd_center)) {
>>> +        dev_err(dev, "Cannot get the regulator \"center\"\n");
>>> +        return PTR_ERR(data->vdd_center);
>>> +    }
>>> +
>>> +    data->dmc_clk = devm_clk_get(dev, "dmc_clk");
>>> +    if (IS_ERR(data->dmc_clk)) {
>>> +        dev_err(dev, "Cannot get the clk dmc_clk\n");
>>> +        return PTR_ERR(data->dmc_clk);
>>> +    };
>>> +
>>> +    data->edev = devfreq_event_get_edev_by_phandle(dev, 0);
>>> +    if (IS_ERR(data->edev))
>>> +        return -EPROBE_DEFER;
>>> +
>>> +    ret = devfreq_event_enable_edev(data->edev);
>>> +    if (ret < 0) {
>>> +        dev_err(dev, "failed to enable devfreq-event devices\n");
>>> +        return ret;
>>> +    }
>>> +
>>> +    /*
>>> +     * We add a devfreq driver to our parent since it has a device tree node
>>> +     * with operating points.
>>> +     */
>>> +    if (dev_pm_opp_of_add_table(dev)) {
>>> +        dev_err(dev, "Invalid operating-points in device tree.\n");
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    of_property_read_u32(np, "upthreshold",
>>> +                 &data->ondemand_data.upthreshold);
>>> +
>>> +    of_property_read_u32(np, "downdifferential",
>>> +                 &data->ondemand_data.downdifferential);
>>> +
>>> +    data->rate = clk_get_rate(data->dmc_clk);
>>> +    rk3399_devfreq_dmc_profile.initial_freq = data->rate;
>>> +    data->devfreq = devfreq_add_device(dev,
>>> +                       &rk3399_devfreq_dmc_profile,
>>> +                       "simple_ondemand",
>>> +                       &data->ondemand_data);
>>> +    if (IS_ERR(data->devfreq))
>>> +        return PTR_ERR(data->devfreq);
>>> +
>>> +    devfreq_register_opp_notifier(dev, data->devfreq);
>>> +
>>> +    data->dmc_nb.notifier_call = rk3399_dmc_enable_notify;
>>> +    dmc_register_notifier(&data->dmc_nb);
>>> +    data->irq = irq;
>>> +    ret = devm_request_irq(dev, irq, rk3399_dmc_irq, 0,
>>> +                   dev_name(dev), data);
>>> +    if (ret) {
>>> +        dev_err(dev, "failed to request dmc irq: %d\n", ret);
>>> +        return ret;
>>> +    }
>>> +
>>> +    /* get dram timing and pass it to bl31 */
>>> +    data->timing = of_get_ddr_timings(dev, np);
>>> +    if (data->timing) {
>>> +        timing = (uint32_t *)data->timing;
>>> +        for (index = 0; index < (sizeof(struct dram_timing) / 4);
>>> +             index++) {
>>> +            param = index;
>>> +            param = param << 32 | *timing++;
>>> +            sip_smc_set_ddr_param(param);
>>> +            param = 0;
>>> +        }
>>> +    }
>>> +    sip_smc_ddr_init();
>>> +
>>> +    init_waitqueue_head(&data->wait_dcf_queue);
>>> +    data->wait_dcf_flag = 0;
>>> +
>>> +    data->dev = dev;
>>> +    platform_set_drvdata(pdev, data);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int rk3399_dmcfreq_remove(struct platform_device *pdev)
>>> +{
>>> +    struct rk3399_dmcfreq *dmcfreq = platform_get_drvdata(pdev);
>>> +
>>> +    devfreq_remove_device(dmcfreq->devfreq);
>>> +    regulator_put(dmcfreq->vdd_center);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static const struct of_device_id rk3399dmc_devfreq_of_match[] = {
>>> +    { .compatible = "rockchip,rk3399-dmc" },
>>> +    { },
>>> +};
>>> +
>>> +static struct platform_driver rk3399_dmcfreq_driver = {
>>> +    .probe    = rk3399_dmcfreq_probe,
>>> +    .remove    = rk3399_dmcfreq_remove,
>>> +    .driver = {
>>> +        .name    = "rk3399-dmc-freq",
>>> +        .pm    = &rk3399_dmcfreq_pm,
>>> +        .of_match_table = rk3399dmc_devfreq_of_match,
>>> +    },
>>> +};
>>> +module_platform_driver(rk3399_dmcfreq_driver);
>>> +
>>> +MODULE_LICENSE("GPL v2");
>>> +MODULE_DESCRIPTION("RK3399 dmcfreq driver with devfreq framework");
>>> diff --git a/drivers/devfreq/rockchip/rockchip_dmc.c b/drivers/devfreq/rockchip/rockchip_dmc.c
>>> new file mode 100644
>>> index 0000000..78baee5
>>> --- /dev/null
>>> +++ b/drivers/devfreq/rockchip/rockchip_dmc.c
>>> @@ -0,0 +1,143 @@
>>> +/*
>>> + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
>>> + * Author: Lin Huang <hl at rock-chips.com>
>>> + * Base on: https://chromium-review.googlesource.com/#/c/231477/
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify it
>>> + * under the terms and conditions of the GNU General Public License,
>>> + * version 2, as published by the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope 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/mutex.h>
>>> +#include <soc/rockchip/rockchip_dmc.h>
>>> +
>>> +static int num_wait;
>>> +static int num_disable;
>>> +static BLOCKING_NOTIFIER_HEAD(dmc_notifier_list);
>>> +static DEFINE_MUTEX(dmc_en_lock);
>>> +static DEFINE_MUTEX(dmc_sync_lock);
>>> +
>>> +/**
>>> + * rockchip_dmc_enabled - Returns true if dmc freq is enabled, false otherwise.
>>> + */
>>> +bool rockchip_dmc_enabled(void)
>>> +{
>>> +    return num_disable <= 0 && num_wait <= 1;
>>> +}
>>> +
>>> +/**
>>> + * rockchip_dmc_enable - Enable dmc frequency scaling. Will only enable
>>> + * frequency scaling if there are 1 or fewer notifiers. Call to undo
>>> + * rockchip_dmc_disable.
>>> + */
>>> +void rockchip_dmc_enable(void)
>>> +{
>>> +    mutex_lock(&dmc_en_lock);
>>> +    num_disable--;
>>> +    WARN_ON(num_disable < 0);
>>> +    if (rockchip_dmc_enabled())
>>> +        dmc_event(DMC_ENABLE);
>>> +    mutex_unlock(&dmc_en_lock);
>>> +}
>>> +
>>> +/**
>>> + * rockchip_dmc_disable - Disable dmc frequency scaling. Call when something
>>> + * cannot coincide with dmc frequency scaling.
>>> + */
>>> +void rockchip_dmc_disable(void)
>>> +{
>>> +    mutex_lock(&dmc_en_lock);
>>> +    if (rockchip_dmc_enabled())
>>> +        dmc_event(DMC_DISABLE);
>>> +    num_disable++;
>>> +    mutex_unlock(&dmc_en_lock);
>>> +}
>>> +
>>> +void dmc_event(int event)
>>> +{
>>> +    mutex_lock(&dmc_sync_lock);
>>> +    blocking_notifier_call_chain(&dmc_notifier_list, event, NULL);
>>> +    mutex_unlock(&dmc_sync_lock);
>>> +}
>>> +
>>> +/**
>>> + * dmc_register_notifier - register a driver to dmc chain
>>> + * @nb: notifier function to register
>>> + */
>>> +int dmc_register_notifier(struct notifier_block *nb)
>>> +{
>>> +    int ret;
>>> +
>>> +    if (!nb)
>>> +        return -EINVAL;
>>> +
>>> +    ret = blocking_notifier_chain_register(&dmc_notifier_list, nb);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +/**
>>> + * dmc_unregister_notifier - unregister a driver from dmc chain
>>> + * @nb: remove notifier function
>>> + */
>>> +int dmc_unregister_notifier(struct notifier_block *nb)
>>> +{
>>> +    int ret;
>>> +
>>> +    if (!nb)
>>> +        return -EINVAL;
>>> +
>>> +    ret = blocking_notifier_chain_unregister(&dmc_notifier_list, nb);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +/**
>>> + * rockchip_dmc_get - Register the notifier block for the dmc chain.
>>> + * @nb The dmc notifier block to register
>>> + */
>>> +int rockchip_dmc_get(struct notifier_block *nb)
>>> +{
>>> +    if (!nb)
>>> +        return -EINVAL;
>>> +
>>> +    mutex_lock(&dmc_en_lock);
>>> +
>>> +    /*
>>> +     * if have two notifier(enable two vop etc),
>>> +     * need to disable dmc
>>> +     */
>>> +    if (num_wait == 1 && num_disable <= 0)
>>> +        dmc_event(DMC_DISABLE);
>>> +    num_wait++;
>>> +    dmc_register_notifier(nb);
>>> +    mutex_unlock(&dmc_en_lock);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +/**
>>> + * rockchip_dmc_put - Remove the notifier block from the dmc chain.
>>> + * @nb The dmc notifier block to unregister
>>> + */
>>> +int rockchip_dmc_put(struct notifier_block *nb)
>>> +{
>>> +    if (!nb)
>>> +        return -EINVAL;
>>> +
>>> +    mutex_lock(&dmc_en_lock);
>>> +    num_wait--;
>>> +
>>> +    /* if notifier from 2 back to 1, enable dmc again */
>>> +    if (num_wait == 1 && num_disable <= 0)
>>> +        dmc_event(DMC_ENABLE);
>>> +    dmc_unregister_notifier(nb);
>>> +    mutex_unlock(&dmc_en_lock);
>>> +
>>> +    return 0;
>>> +}
>>> diff --git a/include/soc/rockchip/rockchip_dmc.h b/include/soc/rockchip/rockchip_dmc.h
>>> new file mode 100644
>>> index 0000000..3f69cbf
>>> --- /dev/null
>>> +++ b/include/soc/rockchip/rockchip_dmc.h
>>> @@ -0,0 +1,45 @@
>>> +/*
>>> + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify it
>>> + * under the terms and conditions of the GNU General Public License,
>>> + * version 2, as published by the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope 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.
>>> + */
>>> +
>>> +#ifndef __SOC_ROCKCHIP_DMC_H
>>> +#define __SOC_ROCKCHIP_DMC_H
>>> +
>>> +#include <linux/notifier.h>
>>> +
>>> +#define DMC_ENABLE    0
>>> +#define DMC_DISABLE    1
>>> +#define DMCFREQ_ADJUST    2
>>> +#define DMCFREQ_FINISH    3
>>> +
>>> +#if IS_ENABLED(CONFIG_ARM_ROCKCHIP_DMC_DEVFREQ)
>>> +int rockchip_dmc_get(struct notifier_block *nb);
>>> +int rockchip_dmc_put(struct notifier_block *nb);
>>> +#else
>>> +static inline int rockchip_dmc_get(struct notifier_block *nb)
>>> +{
>>> +    return 0;
>>> +}
>>> +
>>> +static inline int rockchip_dmc_put(struct notifier_block *nb)
>>> +{
>>> +    return 0;
>>> +}
>>> +#endif
>>> +
>>> +void dmc_event(int event);
>>> +int dmc_register_notifier(struct notifier_block *nb);
>>> +int dmc_unregister_notifier(struct notifier_block *nb);
>>> +void rockchip_dmc_enable(void);
>>> +void rockchip_dmc_disable(void);
>>> +bool rockchip_dmc_enabled(void);
>>> +#endif
>>>
>> Regards,
>> Chanwoo Choi
>>
>>
>> _______________________________________________
>> Linux-rockchip mailing list
>> Linux-rockchip at lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-rockchip
> 



From dianders at chromium.org  Mon Jul 25 07:19:03 2016
From: dianders at chromium.org (Doug Anderson)
Date: Mon, 25 Jul 2016 07:19:03 -0700
Subject: [PATCH 2/3] phy: rockchip-emmc: Be tolerant to card clock of 0 in
 power on
In-Reply-To: <5795AA41.7070604@ti.com>
References: <1467049167-14628-1-git-send-email-dianders@chromium.org>
 <1467049167-14628-3-git-send-email-dianders@chromium.org>
 <5773D1FD.8020601@ti.com>
 <CAD=FV=U_-8HJRjWa8ktT=7tmM5RB4OVnDce-Bv2teDibYfbCeA@mail.gmail.com>
 <CAPDyKFqPHB2O=-hDxVda-h04WS9xYpJ3MyzH8bk5Fc4ogTfB5A@mail.gmail.com>
 <5795AA41.7070604@ti.com>
Message-ID: <CAD=FV=X1uZCH2273fR3VcYiD4_K2w2CPRgyejomQ-KoYrkHLjg@mail.gmail.com>

Hi,

On Sun, Jul 24, 2016 at 10:57 PM, Kishon Vijay Abraham I <kishon at ti.com> wrote:
>> Did you agree on how to move forward with this change?
>
> I think still few things are not very clear especially on what should be
> handled in PHY when the clock rate changes or if any new PHY APIs are required
> to handle any clock changes etc..
> But since this actually gets MMC working in rockchip, I'd be okay to merge this
> now. Though I'd expect this to be refined in the future release cycles.
>
> FWIW:
> Reviewed-by: Kishon Vijay Abraham I <kishon at ti.com>

Thank you.  Agreed that I don't particularly like the revert and last
I checked with Shawn at Rockchip he was trying to find a better
solution.  ...but I also agree that landing the revert is better than
not landing it while we wait for a better solution.

IMHO in this particular case PHY API changes shouldn't be needed since
we should be able to use the CCF notifications.  ...but we need to
figure out how to do that in the case of the current card clock since
it's not really a fully fledged clock and doesn't support
notifications.  We might need to get advice from CCF guys...


-Doug


From thierry.reding at gmail.com  Mon Jul 25 08:21:25 2016
From: thierry.reding at gmail.com (Thierry Reding)
Date: Mon, 25 Jul 2016 17:21:25 +0200
Subject: [PATCH 2/2] dt-bindings: add simple-panel-dsi and simple-panel
In-Reply-To: <1468984730-23186-2-git-send-email-mark.yao@rock-chips.com>
References: <1468984730-23186-1-git-send-email-mark.yao@rock-chips.com>
 <1468984730-23186-2-git-send-email-mark.yao@rock-chips.com>
Message-ID: <20160725152125.GS21170@ulmo.ba.sec>

On Wed, Jul 20, 2016 at 11:18:50AM +0800, Mark Yao wrote:
> Allow user add display timing on device tree with simple-panel-dsi
> or simple-panel.
> 
> Cc: Thierry Reding <thierry.reding at gmail.com>
> Cc: Rob Herring <robh+dt at kernel.org>
> Cc: Mark Rutland <mark.rutland at arm.com>
> 
> Signed-off-by: Mark Yao <mark.yao at rock-chips.com>
> ---
>  .../bindings/display/panel/simple-panel.txt        | 48 ++++++++++++++++++++++
>  1 file changed, 48 insertions(+)

Sorry, not going to happen. Read this for an explanation of why not:

	https://sietch-tagr.blogspot.de/2016/04/display-panels-are-not-special.html

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160725/42a90bff/attachment.sig>

From sudeep.holla at arm.com  Mon Jul 25 10:36:04 2016
From: sudeep.holla at arm.com (Sudeep Holla)
Date: Mon, 25 Jul 2016 18:36:04 +0100
Subject: [PATCH v3 1/7] firmware: rockchip: sip: Add rockchip SIP runtime
 service
In-Reply-To: <1497407.KruMAzy5tb@diego>
References: <1469178440-4668-1-git-send-email-hl@rock-chips.com>
 <1469178440-4668-2-git-send-email-hl@rock-chips.com>
 <1497407.KruMAzy5tb@diego>
Message-ID: <6d3827c1-976c-ba96-600d-a405da2f645c@arm.com>



On 22/07/16 21:50, Heiko St?bner wrote:
> Hi again,
>
> one bigger thing I noticed only now.
>
> Am Freitag, 22. Juli 2016, 17:07:14 schrieben Sie:
>> diff --git a/drivers/firmware/rockchip_sip.c
>> b/drivers/firmware/rockchip_sip.c new file mode 100644
>> index 0000000..7756af9
>> --- /dev/null
>> +++ b/drivers/firmware/rockchip_sip.c
>> @@ -0,0 +1,64 @@
>> +/*
>> + * 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.
>> + *
>> + * Copyright (C) 2016 ARM Limited
>> + */
>> +#include <linux/errno.h>
>> +#include <linux/linkage.h>
>> +#include <linux/of.h>
>> +#include <linux/pm.h>
>> +#include <linux/printk.h>
>> +#include "rockchip_sip.h"
>> +
>> +typedef unsigned long (psci_fn)(unsigned long, unsigned long,
>> +				unsigned long, unsigned long);
>> +asmlinkage psci_fn __invoke_psci_fn_smc;
>> +
>> +#define CONFIG_DRAM_INIT	0x00
>> +#define CONFIG_DRAM_SET_RATE	0x01
>> +#define CONFIG_DRAM_ROUND_RATE	0x02
>> +#define CONFIG_DRAM_SET_AT_SR	0x03
>> +#define CONFIG_DRAM_GET_BW	0x04
>> +#define CONFIG_DRAM_GET_RATE	0x05
>> +#define CONFIG_DRAM_CLR_IRQ	0x06
>> +#define CONFIG_DRAM_SET_PARAM   0x07
>> +
>> +uint64_t sip_smc_ddr_init(void)
>> +{
>> +	return __invoke_psci_fn_smc(SIP_DDR_FREQ, 0,
>> +				    0, CONFIG_DRAM_INIT);
>
> I don't think that is legal to use. For one this function itself is declared
> static in the psci code - most likely for a specific reason.
>
> And also if anything invoke_psci_fn would hold the correct pointer depending
> on the calling method.
>
> But as said above, accessing psci static stuff is most likely wrong. Maybe the
> two psci people I've included can tell us how this is to be accessed.
>

Thanks Heiko for looping us in this thread.

The feature being added in this series is completely out of scope of
PSCI specification and hence PSCI can't be used. Firstly we need to
audit if these are need in Linux and why they can't be handled within
the existing PSCI APIs. But yes, this series is misuse of PSCI.

I also see to know that ARM Trusted Firmware community has not accepted
this PSCI approach, so this patches are useless without that.

If they are still needed they need to make use of SMC Calling Convention
(arm_smccc_smc). Either make these smc function identifiers standard on
their platforms and use them directly in the driver. If they tend to
change too much across their platforms, use the DT approach with
appropriate bindings.

-- 
Regards,
Sudeep


From linux at roeck-us.net  Mon Jul 25 12:39:00 2016
From: linux at roeck-us.net (Guenter Roeck)
Date: Mon, 25 Jul 2016 12:39:00 -0700
Subject: [PATCH] iio: adc: rockchip_saradc: Explicitly disable ADC on probe
Message-ID: <1469475540-26855-1-git-send-email-linux@roeck-us.net>

If the ADC is read for the first time, the caller gets a timeout error,
and the kernel log shows

read channel() error: -110

The ADC may be enabled on boot, and needs to be explicitly disabled
for a read sequence to work (otherwise there is no completion interrupt).
Disaple it explicitly in the probe function.

Fixes: 44d6f2ef94f9 ("iio: adc: add driver for Rockchip saradc")
Signed-off-by: Guenter Roeck <linux at roeck-us.net>
---
 drivers/iio/adc/rockchip_saradc.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c
index f9ad6c2d6821..6aa3271d86b5 100644
--- a/drivers/iio/adc/rockchip_saradc.c
+++ b/drivers/iio/adc/rockchip_saradc.c
@@ -280,6 +280,9 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
 		goto err_pclk;
 	}
 
+	/* Make sure ADC is disabled */
+	writel_relaxed(0, info->regs + SARADC_CTRL);
+
 	platform_set_drvdata(pdev, indio_dev);
 
 	indio_dev->name = dev_name(&pdev->dev);
-- 
2.5.0



From heiko at sntech.de  Mon Jul 25 12:41:41 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Mon, 25 Jul 2016 21:41:41 +0200
Subject: [PATCH] iio: adc: rockchip_saradc: Explicitly disable ADC on probe
In-Reply-To: <1469475540-26855-1-git-send-email-linux@roeck-us.net>
References: <1469475540-26855-1-git-send-email-linux@roeck-us.net>
Message-ID: <2966977.qtjPvpdIFp@diego>

Am Montag, 25. Juli 2016, 12:39:00 schrieb Guenter Roeck:
> If the ADC is read for the first time, the caller gets a timeout error,
> and the kernel log shows
> 
> read channel() error: -110
> 
> The ADC may be enabled on boot, and needs to be explicitly disabled
> for a read sequence to work (otherwise there is no completion interrupt).
> Disaple it explicitly in the probe function.
> 
> Fixes: 44d6f2ef94f9 ("iio: adc: add driver for Rockchip saradc")
> Signed-off-by: Guenter Roeck <linux at roeck-us.net>

in my tests I hadn't notices that issue before, but that is likely because the 
bootloaders I had didn't enable the saradc. Anyway, that looks definitly sane, 
so

Reviewed-by: Heiko Stuebner <heiko at sntech.de>


From hl at rock-chips.com  Mon Jul 25 18:13:31 2016
From: hl at rock-chips.com (hl)
Date: Tue, 26 Jul 2016 09:13:31 +0800
Subject: [PATCH v3 1/7] firmware: rockchip: sip: Add rockchip SIP runtime
 service
In-Reply-To: <6d3827c1-976c-ba96-600d-a405da2f645c@arm.com>
References: <1469178440-4668-1-git-send-email-hl@rock-chips.com>
 <1469178440-4668-2-git-send-email-hl@rock-chips.com>
 <1497407.KruMAzy5tb@diego> <6d3827c1-976c-ba96-600d-a405da2f645c@arm.com>
Message-ID: <5796B93B.9070109@rock-chips.com>

Hi Sudeep Holla,

On 2016?07?26? 01:36, Sudeep Holla wrote:
>
>
> On 22/07/16 21:50, Heiko St?bner wrote:
>> Hi again,
>>
>> one bigger thing I noticed only now.
>>
>> Am Freitag, 22. Juli 2016, 17:07:14 schrieben Sie:
>>> diff --git a/drivers/firmware/rockchip_sip.c
>>> b/drivers/firmware/rockchip_sip.c new file mode 100644
>>> index 0000000..7756af9
>>> --- /dev/null
>>> +++ b/drivers/firmware/rockchip_sip.c
>>> @@ -0,0 +1,64 @@
>>> +/*
>>> + * 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.
>>> + *
>>> + * Copyright (C) 2016 ARM Limited
>>> + */
>>> +#include <linux/errno.h>
>>> +#include <linux/linkage.h>
>>> +#include <linux/of.h>
>>> +#include <linux/pm.h>
>>> +#include <linux/printk.h>
>>> +#include "rockchip_sip.h"
>>> +
>>> +typedef unsigned long (psci_fn)(unsigned long, unsigned long,
>>> +                unsigned long, unsigned long);
>>> +asmlinkage psci_fn __invoke_psci_fn_smc;
>>> +
>>> +#define CONFIG_DRAM_INIT    0x00
>>> +#define CONFIG_DRAM_SET_RATE    0x01
>>> +#define CONFIG_DRAM_ROUND_RATE    0x02
>>> +#define CONFIG_DRAM_SET_AT_SR    0x03
>>> +#define CONFIG_DRAM_GET_BW    0x04
>>> +#define CONFIG_DRAM_GET_RATE    0x05
>>> +#define CONFIG_DRAM_CLR_IRQ    0x06
>>> +#define CONFIG_DRAM_SET_PARAM   0x07
>>> +
>>> +uint64_t sip_smc_ddr_init(void)
>>> +{
>>> +    return __invoke_psci_fn_smc(SIP_DDR_FREQ, 0,
>>> +                    0, CONFIG_DRAM_INIT);
>>
>> I don't think that is legal to use. For one this function itself is 
>> declared
>> static in the psci code - most likely for a specific reason.
>>
>> And also if anything invoke_psci_fn would hold the correct pointer 
>> depending
>> on the calling method.
>>
>> But as said above, accessing psci static stuff is most likely wrong. 
>> Maybe the
>> two psci people I've included can tell us how this is to be accessed.
>>
>
> Thanks Heiko for looping us in this thread.
>
> The feature being added in this series is completely out of scope of
> PSCI specification and hence PSCI can't be used. Firstly we need to
> audit if these are need in Linux and why they can't be handled within
> the existing PSCI APIs. But yes, this series is misuse of PSCI.
>
> I also see to know that ARM Trusted Firmware community has not accepted
> this PSCI approach, so this patches are useless without that.
>
> If they are still needed they need to make use of SMC Calling Convention
> (arm_smccc_smc). Either make these smc function identifiers standard on
> their platforms and use them directly in the driver. If they tend to
> change too much across their platforms, use the DT approach with
> appropriate bindings.
Thanks for your suggestion, i will update the code in next version.

-- 
Lin Huang




From hl at rock-chips.com  Mon Jul 25 18:15:10 2016
From: hl at rock-chips.com (hl)
Date: Tue, 26 Jul 2016 09:15:10 +0800
Subject: [PATCH v3 6/7] PM / devfreq: rockchip: add devfreq driver for
 rk3399 dmc
In-Reply-To: <5795DFD0.7010901@samsung.com>
References: <1469178440-4668-1-git-send-email-hl@rock-chips.com>
 <CGME20160722090916epcas1p28b9a4db33945627f5e4c1157cd6786b7@epcas1p2.samsung.com>
 <1469178440-4668-7-git-send-email-hl@rock-chips.com>
 <5795AB4E.4010600@samsung.com> <5795D216.7010003@rock-chips.com>
 <5795DFD0.7010901@samsung.com>
Message-ID: <5796B99E.7010406@rock-chips.com>

Hi Chanwoo Choi,

On 2016?07?25? 17:45, Chanwoo Choi wrote:
> Hi Lin,
>
> On 2016? 07? 25? 17:47, hl wrote:
>> Hi Chanwoo Choi,
>>
>> On 2016?07?25? 14:01, Chanwoo Choi wrote:
>>> Hi Lin,
>>>
>>> I'm glad to support the for dmc ddr clock scaling with devfreq/devfreq-event.
>>>
>>> But, I think that you have to use the standard interface.
>>> As I already mentioned[1] on previous mail, devfreq fwk support
>>> the standard DEVFREQ_TRANSITION_NOTIFIER notifier which has the two notifications.
>>> - DEVFREQ_PRECHANGE
>>> - DEVFREQ_POSTCHANGE
>>> [1] https://patchwork.kernel.org/patch/9194305/
>>>
>>> You can use the DEVFREQ_PRECHANGE instead of DMCFREQ_ADJUST
>>> and use the DEVFREQ_POSTCHANGE instead of DMCFREQ_FINISH.
>>>
>>> On 2016? 07? 22? 18:07, Lin Huang wrote:
>>>> base on dfi result, we do ddr frequency scaling, register
>>>> dmc driver to devfreq framework, and use simple-ondemand
>>>> policy.
>>>>
>>>> Signed-off-by: Lin Huang <hl at rock-chips.com>
>>>> ---
>>>> Changes in v3:
>>>> - operate dram setting through sip call
>>>> - imporve set rate flow
>>>>
>>>> Changes in v2:
>>>> - None
>>>>    Changes in v1:
>>>> - move dfi controller to event
>>>> - fix set voltage sequence when set rate fail
>>>> - change Kconfig type from tristate to bool
>>>> - move unuse EXPORT_SYMBOL_GPL()
>>>>
>>>>    drivers/devfreq/Kconfig                 |   1 +
>>>>    drivers/devfreq/Makefile                |   1 +
>>>>    drivers/devfreq/rockchip/Kconfig        |  15 +
>>>>    drivers/devfreq/rockchip/Makefile       |   2 +
>>>>    drivers/devfreq/rockchip/rk3399_dmc.c   | 482 ++++++++++++++++++++++++++++++++
>>>>    drivers/devfreq/rockchip/rockchip_dmc.c | 143 ++++++++++
>>>>    include/soc/rockchip/rockchip_dmc.h     |  45 +++
>>>>    7 files changed, 689 insertions(+)
>>>>    create mode 100644 drivers/devfreq/rockchip/Kconfig
>>>>    create mode 100644 drivers/devfreq/rockchip/Makefile
>>>>    create mode 100644 drivers/devfreq/rockchip/rk3399_dmc.c
>>>>    create mode 100644 drivers/devfreq/rockchip/rockchip_dmc.c
>>>>    create mode 100644 include/soc/rockchip/rockchip_dmc.h
>>>>
>>>> diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig
>>>> index a5be56e..cb67246 100644
>>>> --- a/drivers/devfreq/Kconfig
>>>> +++ b/drivers/devfreq/Kconfig
>>>> @@ -101,5 +101,6 @@ config ARM_TEGRA_DEVFREQ
>>>>             operating frequencies and voltages with OPP support.
>>>>      source "drivers/devfreq/event/Kconfig"
>>>> +source "drivers/devfreq/rockchip/Kconfig"
>>>>      endif # PM_DEVFREQ
>>>> diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile
>>>> index 09f11d9..48e2ae6 100644
>>>> --- a/drivers/devfreq/Makefile
>>>> +++ b/drivers/devfreq/Makefile
>>>> @@ -9,6 +9,7 @@ obj-$(CONFIG_DEVFREQ_GOV_PASSIVE)    += governor_passive.o
>>>>    # DEVFREQ Drivers
>>>>    obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ)    += exynos-bus.o
>>>>    obj-$(CONFIG_ARM_TEGRA_DEVFREQ)        += tegra-devfreq.o
>>>> +obj-$(CONFIG_ARCH_ROCKCHIP)        += rockchip/
>>>>      # DEVFREQ Event Drivers
>>>>    obj-$(CONFIG_PM_DEVFREQ_EVENT)        += event/
>>>> diff --git a/drivers/devfreq/rockchip/Kconfig b/drivers/devfreq/rockchip/Kconfig
>>>> new file mode 100644
>>>> index 0000000..7fb1cff
>>>> --- /dev/null
>>>> +++ b/drivers/devfreq/rockchip/Kconfig
>>>> @@ -0,0 +1,15 @@
>>>> +config ARM_ROCKCHIP_DMC_DEVFREQ
>>>> +    bool "ARM ROCKCHIP DMC DEVFREQ Driver"
>>>> +    depends on ARCH_ROCKCHIP
>>>> +    help
>>>> +      This adds the DEVFREQ driver framework for the rockchip dmc.
>>>> +
>>>> +config ARM_RK3399_DMC_DEVFREQ
>>>> +    bool "ARM RK3399 DMC DEVFREQ Driver"
>>>> +    depends on ARM_ROCKCHIP_DMC_DEVFREQ
>>>> +    select PM_OPP
>>>> +    select DEVFREQ_GOV_SIMPLE_ONDEMAND
>>>> +    help
>>>> +          This adds the DEVFREQ driver for the RK3399 dmc. It sets the frequency
>>>> +          for the memory controller and reads the usage counts from hardware.
>>>> +
>>>> diff --git a/drivers/devfreq/rockchip/Makefile b/drivers/devfreq/rockchip/Makefile
>>>> new file mode 100644
>>>> index 0000000..caca525
>>>> --- /dev/null
>>>> +++ b/drivers/devfreq/rockchip/Makefile
>>>> @@ -0,0 +1,2 @@
>>>> +obj-$(CONFIG_ARM_ROCKCHIP_DMC_DEVFREQ)    += rockchip_dmc.o
>>>> +obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ)    += rk3399_dmc.o
>>>> diff --git a/drivers/devfreq/rockchip/rk3399_dmc.c b/drivers/devfreq/rockchip/rk3399_dmc.c
>>>> new file mode 100644
>>>> index 0000000..f1d6120
>>>> --- /dev/null
>>>> +++ b/drivers/devfreq/rockchip/rk3399_dmc.c
>>>> @@ -0,0 +1,482 @@
>>>> +/*
>>>> + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
>>>> + * Author: Lin Huang <hl at rock-chips.com>
>>>> + *
>>>> + * This program is free software; you can redistribute it and/or modify it
>>>> + * under the terms and conditions of the GNU General Public License,
>>>> + * version 2, as published by the Free Software Foundation.
>>>> + *
>>>> + * This program is distributed in the hope 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/clk.h>
>>>> +#include <linux/completion.h>
>>>> +#include <linux/delay.h>
>>>> +#include <linux/devfreq.h>
>>>> +#include <linux/devfreq-event.h>
>>>> +#include <linux/interrupt.h>
>>>> +#include <linux/module.h>
>>>> +#include <linux/of.h>
>>>> +#include <linux/platform_device.h>
>>>> +#include <linux/pm_opp.h>
>>>> +#include <linux/regulator/consumer.h>
>>>> +#include <linux/rwsem.h>
>>>> +#include <linux/suspend.h>
>>>> +#include <linux/syscore_ops.h>
>>>> +
>>>> +#include <soc/rockchip/rockchip_dmc.h>
>>>> +#include "../../firmware/rockchip_sip.h"
>>>> +
>>>> +struct dram_timing {
>>>> +    unsigned int ddr3_speed_bin;
>>>> +    unsigned int pd_idle;
>>>> +    unsigned int sr_idle;
>>>> +    unsigned int sr_mc_gate_idle;
>>>> +    unsigned int srpd_lite_idle;
>>>> +    unsigned int standby_idle;
>>>> +    unsigned int dram_dll_dis_freq;
>>>> +    unsigned int phy_dll_dis_freq;
>>>> +    unsigned int ddr3_odt_dis_freq;
>>>> +    unsigned int ddr3_drv;
>>>> +    unsigned int ddr3_odt;
>>>> +    unsigned int phy_ddr3_ca_drv;
>>>> +    unsigned int phy_ddr3_dq_drv;
>>>> +    unsigned int phy_ddr3_odt;
>>>> +    unsigned int lpddr3_odt_dis_freq;
>>>> +    unsigned int lpddr3_drv;
>>>> +    unsigned int lpddr3_odt;
>>>> +    unsigned int phy_lpddr3_ca_drv;
>>>> +    unsigned int phy_lpddr3_dq_drv;
>>>> +    unsigned int phy_lpddr3_odt;
>>>> +    unsigned int lpddr4_odt_dis_freq;
>>>> +    unsigned int lpddr4_drv;
>>>> +    unsigned int lpddr4_dq_odt;
>>>> +    unsigned int lpddr4_ca_odt;
>>>> +    unsigned int phy_lpddr4_ca_drv;
>>>> +    unsigned int phy_lpddr4_ck_cs_drv;
>>>> +    unsigned int phy_lpddr4_dq_drv;
>>>> +    unsigned int phy_lpddr4_odt;
>>>> +};
>>>> +
>>>> +struct rk3399_dmcfreq {
>>>> +    struct device *dev;
>>>> +    struct devfreq *devfreq;
>>>> +    struct devfreq_simple_ondemand_data ondemand_data;
>>>> +    struct clk *dmc_clk;
>>>> +    struct devfreq_event_dev *edev;
>>>> +    struct mutex lock;
>>>> +    struct notifier_block dmc_nb;
>>>> +    struct dram_timing *timing;
>>>> +    wait_queue_head_t    wait_dcf_queue;
>>>> +    int irq;
>>>> +    int wait_dcf_flag;
>>>> +    struct regulator *vdd_center;
>>>> +    unsigned long rate, target_rate;
>>>> +    unsigned long volt, target_volt;
>>>> +};
>>>> +
>>>> +static int rk3399_dmcfreq_target(struct device *dev, unsigned long *freq,
>>>> +                 u32 flags)
>>>> +{
>>>> +    struct platform_device *pdev = container_of(dev, struct platform_device,
>>>> +                            dev);
>>>> +    struct rk3399_dmcfreq *dmcfreq = platform_get_drvdata(pdev);
>>>> +    struct dev_pm_opp *opp;
>>>> +    unsigned long old_clk_rate = dmcfreq->rate;
>>>> +    unsigned long target_volt, target_rate;
>>>> +    int err;
>>>> +
>>>> +    rcu_read_lock();
>>>> +    opp = devfreq_recommended_opp(dev, freq, flags);
>>>> +    if (IS_ERR(opp)) {
>>>> +        rcu_read_unlock();
>>>> +        return PTR_ERR(opp);
>>>> +    }
>>>> +    target_rate = dev_pm_opp_get_freq(opp);
>>>> +    target_volt = dev_pm_opp_get_voltage(opp);
>>>> +    opp = devfreq_recommended_opp(dev, &dmcfreq->rate, flags);
>>>> +    if (IS_ERR(opp)) {
>>>> +        rcu_read_unlock();
>>>> +        return PTR_ERR(opp);
>>>> +    }
>>>> +    dmcfreq->volt = dev_pm_opp_get_voltage(opp);
>>>> +    rcu_read_unlock();
>>>> +
>>>> +    if (dmcfreq->rate == target_rate)
>>>> +        return 0;
>>>> +
>>>> +    mutex_lock(&dmcfreq->lock);
>>>> +    /*
>>>> +     * if frequency scaling from low to high, adjust voltage first;
>>>> +     * if frequency scaling from high to low, adjuset frequency first;
>>>> +     */
>>>> +    if (old_clk_rate < target_rate) {
>>>> +        err = regulator_set_voltage(dmcfreq->vdd_center, target_volt,
>>>> +                        target_volt);
>>>> +        if (err) {
>>>> +            dev_err(dev, "Unable to set vol %lu\n", target_volt);
>>>> +            goto out;
>>>> +        }
>>>> +    }
>>>> +
>>>> +    dmc_event(DMCFREQ_ADJUST);
>>> You don't need to send the notification in target function.
>>> When you use the DEVFREQ_TRANSITION_NOTIFIER notifier,
>>> devfreq send the notification before calling the target() and after calling the target().
>> I checked DEVFREQ_TRANSISTION_NOTIFIER notifier before, but i have some question:
>> Now i want to use
>>      DEVFREQ_PRECHANGE and DEVFREQ_POSTCHANGE in vop driver, but it hard to get the *devfreq
>> in vop driver, or i can register a DEVFREQ_TRANSITION_NOTIFIER notifier in rk3399_dmc.c and
>> when get message, post DEVFREQ_PRECHANGE or DEVFREQ_POSTCHANGE to vop driver through dmc_event(),
>> do you have a better way to do it?
> Firstly,
> The devfreq has the devfreq_get_devfreq_by_phandle() function
> to get the devfreq pointer in device tree. The vop_bind()
> get the IORESOURCE_MEM and interrupt. So, I think that you
> would get the devfreq pointer in the vop_bind() function.
> So, DEVFREQ_TRANSITION_NOTIFIER can alter the following two notifications:
> - dmc_event(DMCFREQ_ADJUST);
> - dmc_event(DMCFREQ_FINISH);
>
>
> Also,
> devfreq-event fwk provide the devfreq_event_get_edev_by_phandle() funtcion
> to get the devfreq-event pointer in the device tree.
>
> You could call the devfreq_event_[enable/disable[_edev()
> and devfreq_[resume/suspend]_device in the drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> instead of dmc_event(DMC_ENABLE) / dmc_event(DMC_DISABLE).
>
> dmc_event(DMC_ENABLE);
> 	devfreq_event_enable_edev(dmcfreq->edev);
> 	devfreq_resume_device(dmcfreq->devfreq);
>
> dmc_event(DMC_DISABLE);
> 	devfreq_event_disable_edev(dmcfreq->edev);
> 	devfreq_suspend_device(dmcfreq->devfreq);
>
> I think that we have to use the standard interfaces and
> functions providing by framework. It is not appropriate way
> to define the specific functions for only one device driver
> between other framework.

Thanks for you suggestion, this may be a huge update, i will try to 
implement it.
>
> Regards,
> Chanwoo Choi
>
>>>> +    dmcfreq->wait_dcf_flag = 1;
>>>> +
>>>> +    err = clk_set_rate(dmcfreq->dmc_clk, target_rate);
>>>> +    if (err) {
>>>> +        dev_err(dev,
>>>> +            "Unable to set freq %lu. Current freq %lu. Error %d\n",
>>>> +            target_rate, old_clk_rate, err);
>>>> +        regulator_set_voltage(dmcfreq->vdd_center, dmcfreq->volt,
>>>> +                      dmcfreq->volt);
>>>> +        dmc_event(DMCFREQ_FINISH);
>>> ditto.
>>>
>>>> +        goto out;
>>>> +    }
>>>> +
>>>> +    /*
>>>> +     * wait until bcf irq happen, it means freq scaling finish in bl31,
>>>> +     * use 100ms as timeout time
>>>> +     */
>>>> +    wait_event_timeout(dmcfreq->wait_dcf_queue,
>>>> +               !dmcfreq->wait_dcf_flag, HZ / 10);
>>>> +
>>>> +    dmc_event(DMCFREQ_FINISH);
>>> ditto.
>>>
>>>> +
>>>> +    /*
>>>> +     * check the dpll rate
>>>> +     * there only two result we will get,
>>>> +     * 1. ddr frequency scaling fail, we still get the old rate
>>>> +     * 2, ddr frequency scaling sucessful, we get the rate we set
>>>> +     */
>>>> +    dmcfreq->rate = clk_get_rate(dmcfreq->dmc_clk);
>>>> +
>>>> +    /* if get the incorrect rate, set voltage to old value */
>>>> +    if (dmcfreq->rate != target_rate) {
>>>> +        dev_err(dev, "get wrong ddr frequency, Request freq %lu,\
>>>> +            Current freq %lu\n", target_rate, dmcfreq->rate);
>>>> +        regulator_set_voltage(dmcfreq->vdd_center, dmcfreq->volt,
>>>> +                      dmcfreq->volt);
>>>> +    } else if (old_clk_rate > target_rate)
>>>> +        err = regulator_set_voltage(dmcfreq->vdd_center, target_volt,
>>>> +                        target_volt);
>>>> +    if (err)
>>>> +        dev_err(dev, "Unable to set vol %lu\n", target_volt);
>>>> +
>>>> +out:
>>>> +    mutex_unlock(&dmcfreq->lock);
>>>> +    return err;
>>>> +}
>>>> +
>>>> +static int rk3399_dmcfreq_get_dev_status(struct device *dev,
>>>> +                     struct devfreq_dev_status *stat)
>>>> +{
>>>> +    struct platform_device *pdev = container_of(dev, struct platform_device,
>>>> +                            dev);
>>>> +    struct rk3399_dmcfreq *dmcfreq = platform_get_drvdata(pdev);
>>>> +    struct devfreq_event_data edata;
>>>> +
>>>> +    devfreq_event_get_event(dmcfreq->edev, &edata);
>>>> +
>>>> +    stat->current_frequency = dmcfreq->rate;
>>>> +    stat->busy_time = edata.load_count;
>>>> +    stat->total_time = edata.total_count;
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static int rk3399_dmcfreq_get_cur_freq(struct device *dev, unsigned long *freq)
>>>> +{
>>>> +    struct platform_device *pdev = container_of(dev, struct platform_device,
>>>> +                            dev);
>>>> +    struct rk3399_dmcfreq *dmcfreq = platform_get_drvdata(pdev);
>>>> +
>>>> +    *freq = dmcfreq->rate;
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static void rk3399_dmcfreq_exit(struct device *dev)
>>>> +{
>>>> +    struct platform_device *pdev = container_of(dev, struct platform_device,
>>>> +                            dev);
>>>> +    struct rk3399_dmcfreq *dmcfreq = platform_get_drvdata(pdev);
>>>> +
>>>> +    devfreq_unregister_opp_notifier(dev, dmcfreq->devfreq);
>>>> +}
>>>> +
>>>> +static struct devfreq_dev_profile rk3399_devfreq_dmc_profile = {
>>>> +    .polling_ms    = 200,
>>>> +    .target        = rk3399_dmcfreq_target,
>>>> +    .get_dev_status    = rk3399_dmcfreq_get_dev_status,
>>>> +    .get_cur_freq    = rk3399_dmcfreq_get_cur_freq,
>>>> +    .exit        = rk3399_dmcfreq_exit,
>>>> +};
>>>> +
>>>> +static __maybe_unused int rk3399_dmcfreq_suspend(struct device *dev)
>>>> +{
>>>> +    rockchip_dmc_disable();
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static __maybe_unused int rk3399_dmcfreq_resume(struct device *dev)
>>>> +{
>>>> +    rockchip_dmc_enable();
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static SIMPLE_DEV_PM_OPS(rk3399_dmcfreq_pm, rk3399_dmcfreq_suspend,
>>>> +             rk3399_dmcfreq_resume);
>>>> +
>>>> +static int rk3399_dmc_enable_notify(struct notifier_block *nb,
>>>> +                    unsigned long event, void *data)
>>>> +{
>>>> +    struct rk3399_dmcfreq *dmcfreq =
>>>> +                  container_of(nb, struct rk3399_dmcfreq, dmc_nb);
>>>> +    unsigned long freq = ULONG_MAX;
>>>> +
>>>> +    if (event == DMC_ENABLE) {
>>>> +        devfreq_event_enable_edev(dmcfreq->edev);
>>>> +        devfreq_resume_device(dmcfreq->devfreq);
>>>> +        return NOTIFY_OK;
>>>> +    } else if (event == DMC_DISABLE) {
>>>> +        devfreq_event_disable_edev(dmcfreq->edev);
>>>> +        devfreq_suspend_device(dmcfreq->devfreq);
>>>> +
>>>> +        /* when disable dmc, set sdram to max frequency */
>>>> +        rk3399_dmcfreq_target(dmcfreq->dev, &freq, 0);
>>>> +        return NOTIFY_OK;
>>>> +    }
>>>> +
>>>> +    return NOTIFY_DONE;
>>>> +}
>>>> +
>>>> +static irqreturn_t rk3399_dmc_irq(int irq, void *dev_id)
>>>> +{
>>>> +    struct rk3399_dmcfreq *dmcfreq = dev_id;
>>>> +
>>>> +    dmcfreq->wait_dcf_flag = 0;
>>>> +    wake_up(&dmcfreq->wait_dcf_queue);
>>>> +
>>>> +    /* clr dcf irq */
>>>> +    sip_smc_clr_ddr_irq();
>>>> +
>>>> +    return IRQ_HANDLED;
>>>> +}
>>>> +
>>>> +static int of_do_get_timing(struct device_node *np,
>>>> +        struct dram_timing *timing)
>>>> +{
>>>> +    int ret;
>>>> +
>>>> +    ret = of_property_read_u32(np, "ddr3_speed_bin",
>>>> +                   &timing->ddr3_speed_bin);
>>>> +    ret |= of_property_read_u32(np, "pd_idle", &timing->pd_idle);
>>>> +    ret |= of_property_read_u32(np, "sr_idle", &timing->sr_idle);
>>>> +    ret |= of_property_read_u32(np, "sr_mc_gate_idle",
>>>> +                    &timing->sr_mc_gate_idle);
>>>> +    ret |= of_property_read_u32(np, "srpd_lite_idle",
>>>> +                    &timing->srpd_lite_idle);
>>>> +    ret |= of_property_read_u32(np, "standby_idle", &timing->standby_idle);
>>>> +    ret |= of_property_read_u32(np, "dram_dll_dis_freq",
>>>> +                    &timing->dram_dll_dis_freq);
>>>> +    ret |= of_property_read_u32(np, "phy_dll_dis_freq",
>>>> +                    &timing->phy_dll_dis_freq);
>>>> +    ret |= of_property_read_u32(np, "ddr3_odt_dis_freq",
>>>> +                    &timing->ddr3_odt_dis_freq);
>>>> +    ret |= of_property_read_u32(np, "ddr3_drv", &timing->ddr3_drv);
>>>> +    ret |= of_property_read_u32(np, "ddr3_odt", &timing->ddr3_odt);
>>>> +    ret |= of_property_read_u32(np, "phy_ddr3_ca_drv",
>>>> +                    &timing->phy_ddr3_ca_drv);
>>>> +    ret |= of_property_read_u32(np, "phy_ddr3_dq_drv",
>>>> +                    &timing->phy_ddr3_dq_drv);
>>>> +    ret |= of_property_read_u32(np, "phy_ddr3_odt", &timing->phy_ddr3_odt);
>>>> +    ret |= of_property_read_u32(np, "lpddr3_odt_dis_freq",
>>>> +                    &timing->lpddr3_odt_dis_freq);
>>>> +    ret |= of_property_read_u32(np, "lpddr3_drv", &timing->lpddr3_drv);
>>>> +    ret |= of_property_read_u32(np, "lpddr3_odt", &timing->lpddr3_odt);
>>>> +    ret |= of_property_read_u32(np, "phy_lpddr3_ca_drv",
>>>> +                    &timing->phy_lpddr3_ca_drv);
>>>> +    ret |= of_property_read_u32(np, "phy_lpddr3_dq_drv",
>>>> +                    &timing->phy_lpddr3_dq_drv);
>>>> +    ret |= of_property_read_u32(np, "phy_lpddr3_odt",
>>>> +                    &timing->phy_lpddr3_odt);
>>>> +    ret |= of_property_read_u32(np, "lpddr4_odt_dis_freq",
>>>> +                    &timing->lpddr4_odt_dis_freq);
>>>> +    ret |= of_property_read_u32(np, "lpddr4_drv",
>>>> +                    &timing->lpddr4_drv);
>>>> +    ret |= of_property_read_u32(np, "lpddr4_dq_odt",
>>>> +                    &timing->lpddr4_dq_odt);
>>>> +    ret |= of_property_read_u32(np, "lpddr4_ca_odt",
>>>> +                    &timing->lpddr4_ca_odt);
>>>> +    ret |= of_property_read_u32(np, "phy_lpddr4_ca_drv",
>>>> +                    &timing->phy_lpddr4_ca_drv);
>>>> +    ret |= of_property_read_u32(np, "phy_lpddr4_ck_cs_drv",
>>>> +                    &timing->phy_lpddr4_ck_cs_drv);
>>>> +    ret |= of_property_read_u32(np, "phy_lpddr4_dq_drv",
>>>> +                    &timing->phy_lpddr4_dq_drv);
>>>> +    ret |= of_property_read_u32(np, "phy_lpddr4_odt",
>>>> +                    &timing->phy_lpddr4_odt);
>>>> +
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +static struct dram_timing *of_get_ddr_timings(struct device *dev,
>>>> +                          struct device_node *np)
>>>> +{
>>>> +    struct dram_timing    *timing = NULL;
>>>> +    struct device_node    *np_tim;
>>>> +
>>>> +    np_tim = of_parse_phandle(np, "ddr_timing", 0);
>>>> +    if (np_tim) {
>>>> +        timing = devm_kzalloc(dev, sizeof(*timing), GFP_KERNEL);
>>>> +        if (!timing)
>>>> +            goto err;
>>>> +        if (of_do_get_timing(np_tim, timing)) {
>>>> +            devm_kfree(dev, timing);
>>>> +            goto err;
>>>> +        }
>>>> +        return timing;
>>>> +    }
>>>> +
>>>> +err:
>>>> +    if (timing) {
>>>> +        devm_kfree(dev, timing);
>>>> +        timing = NULL;
>>>> +    }
>>>> +
>>>> +    return timing;
>>>> +}
>>>> +
>>>> +static int rk3399_dmcfreq_probe(struct platform_device *pdev)
>>>> +{
>>>> +    struct device *dev = &pdev->dev;
>>>> +    struct rk3399_dmcfreq *data;
>>>> +    int ret, irq, index;
>>>> +    uint64_t param = 0;
>>>> +    uint32_t *timing;
>>>> +    struct device_node *np = pdev->dev.of_node;
>>>> +
>>>> +    irq = platform_get_irq(pdev, 0);
>>>> +    if (irq < 0) {
>>>> +        dev_err(&pdev->dev, "no dmc irq resource\n");
>>>> +        return -EINVAL;
>>>> +    }
>>>> +
>>>> +    data = devm_kzalloc(dev, sizeof(struct rk3399_dmcfreq), GFP_KERNEL);
>>>> +    if (!data)
>>>> +        return -ENOMEM;
>>>> +
>>>> +    mutex_init(&data->lock);
>>>> +
>>>> +    data->vdd_center = devm_regulator_get(dev, "center");
>>>> +    if (IS_ERR(data->vdd_center)) {
>>>> +        dev_err(dev, "Cannot get the regulator \"center\"\n");
>>>> +        return PTR_ERR(data->vdd_center);
>>>> +    }
>>>> +
>>>> +    data->dmc_clk = devm_clk_get(dev, "dmc_clk");
>>>> +    if (IS_ERR(data->dmc_clk)) {
>>>> +        dev_err(dev, "Cannot get the clk dmc_clk\n");
>>>> +        return PTR_ERR(data->dmc_clk);
>>>> +    };
>>>> +
>>>> +    data->edev = devfreq_event_get_edev_by_phandle(dev, 0);
>>>> +    if (IS_ERR(data->edev))
>>>> +        return -EPROBE_DEFER;
>>>> +
>>>> +    ret = devfreq_event_enable_edev(data->edev);
>>>> +    if (ret < 0) {
>>>> +        dev_err(dev, "failed to enable devfreq-event devices\n");
>>>> +        return ret;
>>>> +    }
>>>> +
>>>> +    /*
>>>> +     * We add a devfreq driver to our parent since it has a device tree node
>>>> +     * with operating points.
>>>> +     */
>>>> +    if (dev_pm_opp_of_add_table(dev)) {
>>>> +        dev_err(dev, "Invalid operating-points in device tree.\n");
>>>> +        return -EINVAL;
>>>> +    }
>>>> +
>>>> +    of_property_read_u32(np, "upthreshold",
>>>> +                 &data->ondemand_data.upthreshold);
>>>> +
>>>> +    of_property_read_u32(np, "downdifferential",
>>>> +                 &data->ondemand_data.downdifferential);
>>>> +
>>>> +    data->rate = clk_get_rate(data->dmc_clk);
>>>> +    rk3399_devfreq_dmc_profile.initial_freq = data->rate;
>>>> +    data->devfreq = devfreq_add_device(dev,
>>>> +                       &rk3399_devfreq_dmc_profile,
>>>> +                       "simple_ondemand",
>>>> +                       &data->ondemand_data);
>>>> +    if (IS_ERR(data->devfreq))
>>>> +        return PTR_ERR(data->devfreq);
>>>> +
>>>> +    devfreq_register_opp_notifier(dev, data->devfreq);
>>>> +
>>>> +    data->dmc_nb.notifier_call = rk3399_dmc_enable_notify;
>>>> +    dmc_register_notifier(&data->dmc_nb);
>>>> +    data->irq = irq;
>>>> +    ret = devm_request_irq(dev, irq, rk3399_dmc_irq, 0,
>>>> +                   dev_name(dev), data);
>>>> +    if (ret) {
>>>> +        dev_err(dev, "failed to request dmc irq: %d\n", ret);
>>>> +        return ret;
>>>> +    }
>>>> +
>>>> +    /* get dram timing and pass it to bl31 */
>>>> +    data->timing = of_get_ddr_timings(dev, np);
>>>> +    if (data->timing) {
>>>> +        timing = (uint32_t *)data->timing;
>>>> +        for (index = 0; index < (sizeof(struct dram_timing) / 4);
>>>> +             index++) {
>>>> +            param = index;
>>>> +            param = param << 32 | *timing++;
>>>> +            sip_smc_set_ddr_param(param);
>>>> +            param = 0;
>>>> +        }
>>>> +    }
>>>> +    sip_smc_ddr_init();
>>>> +
>>>> +    init_waitqueue_head(&data->wait_dcf_queue);
>>>> +    data->wait_dcf_flag = 0;
>>>> +
>>>> +    data->dev = dev;
>>>> +    platform_set_drvdata(pdev, data);
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static int rk3399_dmcfreq_remove(struct platform_device *pdev)
>>>> +{
>>>> +    struct rk3399_dmcfreq *dmcfreq = platform_get_drvdata(pdev);
>>>> +
>>>> +    devfreq_remove_device(dmcfreq->devfreq);
>>>> +    regulator_put(dmcfreq->vdd_center);
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static const struct of_device_id rk3399dmc_devfreq_of_match[] = {
>>>> +    { .compatible = "rockchip,rk3399-dmc" },
>>>> +    { },
>>>> +};
>>>> +
>>>> +static struct platform_driver rk3399_dmcfreq_driver = {
>>>> +    .probe    = rk3399_dmcfreq_probe,
>>>> +    .remove    = rk3399_dmcfreq_remove,
>>>> +    .driver = {
>>>> +        .name    = "rk3399-dmc-freq",
>>>> +        .pm    = &rk3399_dmcfreq_pm,
>>>> +        .of_match_table = rk3399dmc_devfreq_of_match,
>>>> +    },
>>>> +};
>>>> +module_platform_driver(rk3399_dmcfreq_driver);
>>>> +
>>>> +MODULE_LICENSE("GPL v2");
>>>> +MODULE_DESCRIPTION("RK3399 dmcfreq driver with devfreq framework");
>>>> diff --git a/drivers/devfreq/rockchip/rockchip_dmc.c b/drivers/devfreq/rockchip/rockchip_dmc.c
>>>> new file mode 100644
>>>> index 0000000..78baee5
>>>> --- /dev/null
>>>> +++ b/drivers/devfreq/rockchip/rockchip_dmc.c
>>>> @@ -0,0 +1,143 @@
>>>> +/*
>>>> + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
>>>> + * Author: Lin Huang <hl at rock-chips.com>
>>>> + * Base on: https://chromium-review.googlesource.com/#/c/231477/
>>>> + *
>>>> + * This program is free software; you can redistribute it and/or modify it
>>>> + * under the terms and conditions of the GNU General Public License,
>>>> + * version 2, as published by the Free Software Foundation.
>>>> + *
>>>> + * This program is distributed in the hope 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/mutex.h>
>>>> +#include <soc/rockchip/rockchip_dmc.h>
>>>> +
>>>> +static int num_wait;
>>>> +static int num_disable;
>>>> +static BLOCKING_NOTIFIER_HEAD(dmc_notifier_list);
>>>> +static DEFINE_MUTEX(dmc_en_lock);
>>>> +static DEFINE_MUTEX(dmc_sync_lock);
>>>> +
>>>> +/**
>>>> + * rockchip_dmc_enabled - Returns true if dmc freq is enabled, false otherwise.
>>>> + */
>>>> +bool rockchip_dmc_enabled(void)
>>>> +{
>>>> +    return num_disable <= 0 && num_wait <= 1;
>>>> +}
>>>> +
>>>> +/**
>>>> + * rockchip_dmc_enable - Enable dmc frequency scaling. Will only enable
>>>> + * frequency scaling if there are 1 or fewer notifiers. Call to undo
>>>> + * rockchip_dmc_disable.
>>>> + */
>>>> +void rockchip_dmc_enable(void)
>>>> +{
>>>> +    mutex_lock(&dmc_en_lock);
>>>> +    num_disable--;
>>>> +    WARN_ON(num_disable < 0);
>>>> +    if (rockchip_dmc_enabled())
>>>> +        dmc_event(DMC_ENABLE);
>>>> +    mutex_unlock(&dmc_en_lock);
>>>> +}
>>>> +
>>>> +/**
>>>> + * rockchip_dmc_disable - Disable dmc frequency scaling. Call when something
>>>> + * cannot coincide with dmc frequency scaling.
>>>> + */
>>>> +void rockchip_dmc_disable(void)
>>>> +{
>>>> +    mutex_lock(&dmc_en_lock);
>>>> +    if (rockchip_dmc_enabled())
>>>> +        dmc_event(DMC_DISABLE);
>>>> +    num_disable++;
>>>> +    mutex_unlock(&dmc_en_lock);
>>>> +}
>>>> +
>>>> +void dmc_event(int event)
>>>> +{
>>>> +    mutex_lock(&dmc_sync_lock);
>>>> +    blocking_notifier_call_chain(&dmc_notifier_list, event, NULL);
>>>> +    mutex_unlock(&dmc_sync_lock);
>>>> +}
>>>> +
>>>> +/**
>>>> + * dmc_register_notifier - register a driver to dmc chain
>>>> + * @nb: notifier function to register
>>>> + */
>>>> +int dmc_register_notifier(struct notifier_block *nb)
>>>> +{
>>>> +    int ret;
>>>> +
>>>> +    if (!nb)
>>>> +        return -EINVAL;
>>>> +
>>>> +    ret = blocking_notifier_chain_register(&dmc_notifier_list, nb);
>>>> +
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +/**
>>>> + * dmc_unregister_notifier - unregister a driver from dmc chain
>>>> + * @nb: remove notifier function
>>>> + */
>>>> +int dmc_unregister_notifier(struct notifier_block *nb)
>>>> +{
>>>> +    int ret;
>>>> +
>>>> +    if (!nb)
>>>> +        return -EINVAL;
>>>> +
>>>> +    ret = blocking_notifier_chain_unregister(&dmc_notifier_list, nb);
>>>> +
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +/**
>>>> + * rockchip_dmc_get - Register the notifier block for the dmc chain.
>>>> + * @nb The dmc notifier block to register
>>>> + */
>>>> +int rockchip_dmc_get(struct notifier_block *nb)
>>>> +{
>>>> +    if (!nb)
>>>> +        return -EINVAL;
>>>> +
>>>> +    mutex_lock(&dmc_en_lock);
>>>> +
>>>> +    /*
>>>> +     * if have two notifier(enable two vop etc),
>>>> +     * need to disable dmc
>>>> +     */
>>>> +    if (num_wait == 1 && num_disable <= 0)
>>>> +        dmc_event(DMC_DISABLE);
>>>> +    num_wait++;
>>>> +    dmc_register_notifier(nb);
>>>> +    mutex_unlock(&dmc_en_lock);
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +/**
>>>> + * rockchip_dmc_put - Remove the notifier block from the dmc chain.
>>>> + * @nb The dmc notifier block to unregister
>>>> + */
>>>> +int rockchip_dmc_put(struct notifier_block *nb)
>>>> +{
>>>> +    if (!nb)
>>>> +        return -EINVAL;
>>>> +
>>>> +    mutex_lock(&dmc_en_lock);
>>>> +    num_wait--;
>>>> +
>>>> +    /* if notifier from 2 back to 1, enable dmc again */
>>>> +    if (num_wait == 1 && num_disable <= 0)
>>>> +        dmc_event(DMC_ENABLE);
>>>> +    dmc_unregister_notifier(nb);
>>>> +    mutex_unlock(&dmc_en_lock);
>>>> +
>>>> +    return 0;
>>>> +}
>>>> diff --git a/include/soc/rockchip/rockchip_dmc.h b/include/soc/rockchip/rockchip_dmc.h
>>>> new file mode 100644
>>>> index 0000000..3f69cbf
>>>> --- /dev/null
>>>> +++ b/include/soc/rockchip/rockchip_dmc.h
>>>> @@ -0,0 +1,45 @@
>>>> +/*
>>>> + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
>>>> + *
>>>> + * This program is free software; you can redistribute it and/or modify it
>>>> + * under the terms and conditions of the GNU General Public License,
>>>> + * version 2, as published by the Free Software Foundation.
>>>> + *
>>>> + * This program is distributed in the hope 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.
>>>> + */
>>>> +
>>>> +#ifndef __SOC_ROCKCHIP_DMC_H
>>>> +#define __SOC_ROCKCHIP_DMC_H
>>>> +
>>>> +#include <linux/notifier.h>
>>>> +
>>>> +#define DMC_ENABLE    0
>>>> +#define DMC_DISABLE    1
>>>> +#define DMCFREQ_ADJUST    2
>>>> +#define DMCFREQ_FINISH    3
>>>> +
>>>> +#if IS_ENABLED(CONFIG_ARM_ROCKCHIP_DMC_DEVFREQ)
>>>> +int rockchip_dmc_get(struct notifier_block *nb);
>>>> +int rockchip_dmc_put(struct notifier_block *nb);
>>>> +#else
>>>> +static inline int rockchip_dmc_get(struct notifier_block *nb)
>>>> +{
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static inline int rockchip_dmc_put(struct notifier_block *nb)
>>>> +{
>>>> +    return 0;
>>>> +}
>>>> +#endif
>>>> +
>>>> +void dmc_event(int event);
>>>> +int dmc_register_notifier(struct notifier_block *nb);
>>>> +int dmc_unregister_notifier(struct notifier_block *nb);
>>>> +void rockchip_dmc_enable(void);
>>>> +void rockchip_dmc_disable(void);
>>>> +bool rockchip_dmc_enabled(void);
>>>> +#endif
>>>>
>>> Regards,
>>> Chanwoo Choi
>>>
>>>
>>> _______________________________________________
>>> Linux-rockchip mailing list
>>> Linux-rockchip at lists.infradead.org
>>> http://lists.infradead.org/mailman/listinfo/linux-rockchip
>
>
>

-- 
Lin Huang




From wxt at rock-chips.com  Mon Jul 25 19:51:34 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Tue, 26 Jul 2016 10:51:34 +0800
Subject: [PATCH] iio: adc: rockchip_saradc: Explicitly disable ADC on probe
In-Reply-To: <1469475540-26855-1-git-send-email-linux@roeck-us.net>
References: <1469475540-26855-1-git-send-email-linux@roeck-us.net>
Message-ID: <5796D036.1060101@rock-chips.com>

Hi Guenter,

Thanks for fixing it.

On 2016?07?26? 03:39, Guenter Roeck wrote:
> If the ADC is read for the first time, the caller gets a timeout error,
> and the kernel log shows
>
> read channel() error: -110
>
> The ADC may be enabled on boot, and needs to be explicitly disabled
> for a read sequence to work (otherwise there is no completion interrupt).
> Disaple it explicitly in the probe function.
>
> Fixes: 44d6f2ef94f9 ("iio: adc: add driver for Rockchip saradc")
> Signed-off-by: Guenter Roeck <linux at roeck-us.net>
> ---
>   drivers/iio/adc/rockchip_saradc.c | 3 +++
>   1 file changed, 3 insertions(+)
>
> diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c
> index f9ad6c2d6821..6aa3271d86b5 100644
> --- a/drivers/iio/adc/rockchip_saradc.c
> +++ b/drivers/iio/adc/rockchip_saradc.c
> @@ -280,6 +280,9 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
>   		goto err_pclk;
>   	}
>   
> +	/* Make sure ADC is disabled */
> +	writel_relaxed(0, info->regs + SARADC_CTRL);

I think we should reset the saradc controller.
Since make sure the reset value is 0 and loader-->kernel may even cause 
harm, as my experience on tsadc. (drivers/thermal/rockchip_thermal.c)


e.g.:
/**
* Reset SARADC Controller, reset all saradc registers.
*/
static void rockchip_saradc_reset_controller(struct reset_control *reset)
{
reset_control_assert(reset);
usleep_range(10, 20);
reset_control_deassert(reset);
}

..probe()
{
...
rockchip_saradc_reset_controller();
...
}


-
Caesar

> +
>   	platform_set_drvdata(pdev, indio_dev);
>   
>   	indio_dev->name = dev_name(&pdev->dev);


-- 
caesar wang | software engineer | wxt at rock-chip.com




From linux at roeck-us.net  Mon Jul 25 20:22:58 2016
From: linux at roeck-us.net (Guenter Roeck)
Date: Mon, 25 Jul 2016 20:22:58 -0700
Subject: [PATCH] iio: adc: rockchip_saradc: Explicitly disable ADC on probe
In-Reply-To: <5796D036.1060101@rock-chips.com>
References: <1469475540-26855-1-git-send-email-linux@roeck-us.net>
 <5796D036.1060101@rock-chips.com>
Message-ID: <5796D792.4080407@roeck-us.net>

On 07/25/2016 07:51 PM, Caesar Wang wrote:
> Hi Guenter,
>
> Thanks for fixing it.
>
> On 2016?07?26? 03:39, Guenter Roeck wrote:
>> If the ADC is read for the first time, the caller gets a timeout error,
>> and the kernel log shows
>>
>> read channel() error: -110
>>
>> The ADC may be enabled on boot, and needs to be explicitly disabled
>> for a read sequence to work (otherwise there is no completion interrupt).
>> Disaple it explicitly in the probe function.
>>
>> Fixes: 44d6f2ef94f9 ("iio: adc: add driver for Rockchip saradc")
>> Signed-off-by: Guenter Roeck <linux at roeck-us.net>
>> ---
>>   drivers/iio/adc/rockchip_saradc.c | 3 +++
>>   1 file changed, 3 insertions(+)
>>
>> diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c
>> index f9ad6c2d6821..6aa3271d86b5 100644
>> --- a/drivers/iio/adc/rockchip_saradc.c
>> +++ b/drivers/iio/adc/rockchip_saradc.c
>> @@ -280,6 +280,9 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
>>           goto err_pclk;
>>       }
>> +    /* Make sure ADC is disabled */
>> +    writel_relaxed(0, info->regs + SARADC_CTRL);
>
> I think we should reset the saradc controller.
> Since make sure the reset value is 0 and loader-->kernel may even cause harm, as my experience on tsadc. (drivers/thermal/rockchip_thermal.c)
>
>
> e.g.:
> /**
> * Reset SARADC Controller, reset all saradc registers.
> */
> static void rockchip_saradc_reset_controller(struct reset_control *reset)
> {
> reset_control_assert(reset);
> usleep_range(10, 20);
> reset_control_deassert(reset);
> }
>
> ..probe()
> {
> ...
> rockchip_saradc_reset_controller();
> ...
> }
>

Ok, I'll give it a try.

Guenter

>
> -
> Caesar
>
>> +
>>       platform_set_drvdata(pdev, indio_dev);
>>       indio_dev->name = dev_name(&pdev->dev);
>
>



From wxt at rock-chips.com  Mon Jul 25 23:11:47 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Tue, 26 Jul 2016 14:11:47 +0800
Subject: [PATCH 1/4] iio: adc: rockchip_saradc: reset saradc controller before
 programming it
Message-ID: <1469513510-1516-1-git-send-email-wxt@rock-chips.com>

SARADC controller needs to be reset before programming it, otherwise
it will not function properly.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>
Cc: Jonathan Cameron <jic23 at kernel.org>
Cc: Heiko Stuebner <heiko at sntech.de>
Cc: Rob Herring <robh+dt at kernel.org>
Cc: linux-iio at vger.kernel.org
Cc: linux-rockchip at lists.infradead.org
---

 .../bindings/iio/adc/rockchip-saradc.txt           |  5 +++++
 drivers/iio/adc/Kconfig                            |  1 +
 drivers/iio/adc/rockchip_saradc.c                  | 22 ++++++++++++++++++++++
 3 files changed, 28 insertions(+)

diff --git a/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt b/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt
index bf99e2f..d2258be 100644
--- a/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt
+++ b/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt
@@ -13,6 +13,9 @@ Required properties:
 - clocks: Must contain an entry for each entry in clock-names.
 - clock-names: Shall be "saradc" for the converter-clock, and "apb_pclk" for
                the peripheral clock.
+- resets: Must contain an entry for each entry in reset-names.
+	  See ../reset/reset.txt for details.
+- reset-names: Must include the name "saradc-apb".
 - vref-supply: The regulator supply ADC reference voltage.
 - #io-channel-cells: Should be 1, see ../iio-bindings.txt
 
@@ -23,6 +26,8 @@ Example:
 		interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
 		clocks = <&cru SCLK_SARADC>, <&cru PCLK_SARADC>;
 		clock-names = "saradc", "apb_pclk";
+		resets = <&cru SRST_SARADC>;
+		reset-names = "saradc-apb";
 		#io-channel-cells = <1>;
 		vref-supply = <&vcc18>;
 	};
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 1de31bd..7675772 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -389,6 +389,7 @@ config QCOM_SPMI_VADC
 config ROCKCHIP_SARADC
 	tristate "Rockchip SARADC driver"
 	depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST)
+	depends on RESET_CONTROLLER
 	help
 	  Say yes here to build support for the SARADC found in SoCs from
 	  Rockchip.
diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c
index f9ad6c2..2f0e110 100644
--- a/drivers/iio/adc/rockchip_saradc.c
+++ b/drivers/iio/adc/rockchip_saradc.c
@@ -21,6 +21,8 @@
 #include <linux/of_device.h>
 #include <linux/clk.h>
 #include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/reset.h>
 #include <linux/regulator/consumer.h>
 #include <linux/iio/iio.h>
 
@@ -53,6 +55,7 @@ struct rockchip_saradc {
 	struct clk		*clk;
 	struct completion	completion;
 	struct regulator	*vref;
+	struct reset_control	*reset;
 	const struct rockchip_saradc_data *data;
 	u16			last_val;
 };
@@ -190,6 +193,16 @@ static const struct of_device_id rockchip_saradc_match[] = {
 };
 MODULE_DEVICE_TABLE(of, rockchip_saradc_match);
 
+/**
+ * Reset SARADC Controller.
+ */
+static void rockchip_saradc_reset_controller(struct reset_control *reset)
+{
+	reset_control_assert(reset);
+	usleep_range(10, 20);
+	reset_control_deassert(reset);
+}
+
 static int rockchip_saradc_probe(struct platform_device *pdev)
 {
 	struct rockchip_saradc *info = NULL;
@@ -218,6 +231,13 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
 	if (IS_ERR(info->regs))
 		return PTR_ERR(info->regs);
 
+	info->reset = devm_reset_control_get(&pdev->dev, "saradc-apb");
+	if (IS_ERR(info->reset)) {
+		ret = PTR_ERR(info->reset);
+		dev_err(&pdev->dev, "failed to get saradc reset: %d\n", ret);
+		return ret;
+	}
+
 	init_completion(&info->completion);
 
 	irq = platform_get_irq(pdev, 0);
@@ -252,6 +272,8 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
 		return PTR_ERR(info->vref);
 	}
 
+	rockchip_saradc_reset_controller(info->reset);
+
 	/*
 	 * Use a default value for the converter clock.
 	 * This may become user-configurable in the future.
-- 
1.9.1



From wxt at rock-chips.com  Mon Jul 25 23:11:48 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Tue, 26 Jul 2016 14:11:48 +0800
Subject: [PATCH 2/4] arm64: dts: rockchip: add the saradc for rk3399
In-Reply-To: <1469513510-1516-1-git-send-email-wxt@rock-chips.com>
References: <1469513510-1516-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1469513510-1516-2-git-send-email-wxt@rock-chips.com>

This patch adds saradc needed information on rk3399 SoCs.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>
---

 arch/arm64/boot/dts/rockchip/rk3399.dtsi | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index 4c84229..b81f84b 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -299,6 +299,18 @@
 		};
 	};
 
+	saradc: saradc at ff100000 {
+		compatible = "rockchip,rk3399-saradc";
+		reg = <0x0 0xff100000 0x0 0x100>;
+		interrupts = <GIC_SPI 62 IRQ_TYPE_LEVEL_HIGH>;
+		#io-channel-cells = <1>;
+		clocks = <&cru SCLK_SARADC>, <&cru PCLK_SARADC>;
+		clock-names = "saradc", "apb_pclk";
+		resets = <&cru SRST_P_SARADC>;
+		reset-names = "saradc-apb";
+		status = "disabled";
+	};
+
 	i2c1: i2c at ff110000 {
 		compatible = "rockchip,rk3399-i2c";
 		reg = <0x0 0xff110000 0x0 0x1000>;
-- 
1.9.1



From wxt at rock-chips.com  Mon Jul 25 23:11:50 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Tue, 26 Jul 2016 14:11:50 +0800
Subject: [PATCH 4/4] arm: dts: rockchip: add reset node for the exist saradc
 SoCs
In-Reply-To: <1469513510-1516-1-git-send-email-wxt@rock-chips.com>
References: <1469513510-1516-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1469513510-1516-4-git-send-email-wxt@rock-chips.com>

SARADC controller needs to be reset before programming it, otherwise
it will not function properly.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>
---

 arch/arm/boot/dts/rk3066a.dtsi | 2 ++
 arch/arm/boot/dts/rk3288.dtsi  | 2 ++
 arch/arm/boot/dts/rk3xxx.dtsi  | 2 ++
 3 files changed, 6 insertions(+)

diff --git a/arch/arm/boot/dts/rk3066a.dtsi b/arch/arm/boot/dts/rk3066a.dtsi
index c0ba86c..0d0dae3 100644
--- a/arch/arm/boot/dts/rk3066a.dtsi
+++ b/arch/arm/boot/dts/rk3066a.dtsi
@@ -197,6 +197,8 @@
 		clock-names = "saradc", "apb_pclk";
 		interrupts = <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
 		#io-channel-cells = <1>;
+		resets = <&cru SRST_SARADC>;
+		reset-names = "saradc-apb";
 		status = "disabled";
 	};
 
diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi
index cd33f01..91c4b3c 100644
--- a/arch/arm/boot/dts/rk3288.dtsi
+++ b/arch/arm/boot/dts/rk3288.dtsi
@@ -279,6 +279,8 @@
 		#io-channel-cells = <1>;
 		clocks = <&cru SCLK_SARADC>, <&cru PCLK_SARADC>;
 		clock-names = "saradc", "apb_pclk";
+		resets = <&cru SRST_SARADC>;
+		reset-names = "saradc-apb";
 		status = "disabled";
 	};
 
diff --git a/arch/arm/boot/dts/rk3xxx.dtsi b/arch/arm/boot/dts/rk3xxx.dtsi
index 99bbcc2..e2cd683 100644
--- a/arch/arm/boot/dts/rk3xxx.dtsi
+++ b/arch/arm/boot/dts/rk3xxx.dtsi
@@ -399,6 +399,8 @@
 		#io-channel-cells = <1>;
 		clocks = <&cru SCLK_SARADC>, <&cru PCLK_SARADC>;
 		clock-names = "saradc", "apb_pclk";
+		resets = <&cru SRST_SARADC>;
+		reset-names = "saradc-apb";
 		status = "disabled";
 	};
 
-- 
1.9.1



From wxt at rock-chips.com  Mon Jul 25 23:11:49 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Tue, 26 Jul 2016 14:11:49 +0800
Subject: [PATCH 3/4] arm64: dts: rockchip: add reset saradc node for rk3368
 SoCs
In-Reply-To: <1469513510-1516-1-git-send-email-wxt@rock-chips.com>
References: <1469513510-1516-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1469513510-1516-3-git-send-email-wxt@rock-chips.com>

SARADC controller needs to be reset before programming it, otherwise
it will not function properly.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>
---

 arch/arm64/boot/dts/rockchip/rk3368.dtsi | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3368.dtsi b/arch/arm64/boot/dts/rockchip/rk3368.dtsi
index d02a9003..4f44d11 100644
--- a/arch/arm64/boot/dts/rockchip/rk3368.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3368.dtsi
@@ -270,6 +270,8 @@
 		#io-channel-cells = <1>;
 		clocks = <&cru SCLK_SARADC>, <&cru PCLK_SARADC>;
 		clock-names = "saradc", "apb_pclk";
+		resets = <&cru SRST_SARADC>;
+		reset-names = "saradc-apb";
 		status = "disabled";
 	};
 
-- 
1.9.1



From wxt at rock-chips.com  Mon Jul 25 23:48:29 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Tue, 26 Jul 2016 14:48:29 +0800
Subject: [PATCH] iio: adc: rockchip_saradc: Explicitly disable ADC on probe
In-Reply-To: <5796D792.4080407@roeck-us.net>
References: <1469475540-26855-1-git-send-email-linux@roeck-us.net>
 <5796D036.1060101@rock-chips.com> <5796D792.4080407@roeck-us.net>
Message-ID: <579707BD.8060800@rock-chips.com>


On 2016?07?26? 11:22, Guenter Roeck wrote:
> On 07/25/2016 07:51 PM, Caesar Wang wrote:
>> Hi Guenter,
>>
>> Thanks for fixing it.
>>
>> On 2016?07?26? 03:39, Guenter Roeck wrote:
>>> If the ADC is read for the first time, the caller gets a timeout error,
>>> and the kernel log shows
>>>
>>> read channel() error: -110
>>>
>>> The ADC may be enabled on boot, and needs to be explicitly disabled
>>> for a read sequence to work (otherwise there is no completion 
>>> interrupt).
>>> Disaple it explicitly in the probe function.
>>>
>>> Fixes: 44d6f2ef94f9 ("iio: adc: add driver for Rockchip saradc")
>>> Signed-off-by: Guenter Roeck <linux at roeck-us.net>
>>> ---
>>>   drivers/iio/adc/rockchip_saradc.c | 3 +++
>>>   1 file changed, 3 insertions(+)
>>>
>>> diff --git a/drivers/iio/adc/rockchip_saradc.c 
>>> b/drivers/iio/adc/rockchip_saradc.c
>>> index f9ad6c2d6821..6aa3271d86b5 100644
>>> --- a/drivers/iio/adc/rockchip_saradc.c
>>> +++ b/drivers/iio/adc/rockchip_saradc.c
>>> @@ -280,6 +280,9 @@ static int rockchip_saradc_probe(struct 
>>> platform_device *pdev)
>>>           goto err_pclk;
>>>       }
>>> +    /* Make sure ADC is disabled */
>>> +    writel_relaxed(0, info->regs + SARADC_CTRL);
>>
>> I think we should reset the saradc controller.
>> Since make sure the reset value is 0 and loader-->kernel may even 
>> cause harm, as my experience on tsadc. 
>> (drivers/thermal/rockchip_thermal.c)
>>
>>
>> e.g.:
>> /**
>> * Reset SARADC Controller, reset all saradc registers.
>> */
>> static void rockchip_saradc_reset_controller(struct reset_control 
>> *reset)
>> {
>> reset_control_assert(reset);
>> usleep_range(10, 20);
>> reset_control_deassert(reset);
>> }
>>
>> ..probe()
>> {
>> ...
>> rockchip_saradc_reset_controller();
>> ...
>> }
>>
>
> Ok, I'll give it a try.
>

I posted it on https://patchwork.kernel.org/patch/9247661/


> Guenter
>
>>
>> -
>> Caesar
>>
>>> +
>>>       platform_set_drvdata(pdev, indio_dev);
>>>       indio_dev->name = dev_name(&pdev->dev);
>>
>>
>
>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip


-- 
caesar wang | software engineer | wxt at rock-chip.com




From mark.yao at rock-chips.com  Tue Jul 26 00:46:32 2016
From: mark.yao at rock-chips.com (Mark Yao)
Date: Tue, 26 Jul 2016 15:46:32 +0800
Subject: [PATCH 1/3] drm: introduce share plane
Message-ID: <1469519194-23133-1-git-send-email-mark.yao@rock-chips.com>

What is share plane:
Plane hardware only be used when the display scanout run into plane active
scanout, that means we can reuse the plane hardware resources on plane
non-active scanout.

     --------------------------------------------------
    |  scanout                                       |
    |         ------------------                     |
    |         | parent plane   |                     |
    |         | active scanout |                     |
    |         |                |   ----------------- |
    |         ------------------   | share plane 1 | |
    |  -----------------           |active scanout | |
    |  | share plane 0 |           |               | |
    |  |active scanout |           ----------------- |
    |  |               |                             |
    |  -----------------                             |
    --------------------------------------------------
One plane hardware can be reuse for multi-planes, we assume the first
plane is parent plane, other planes share the resource with first one.
    parent plane
        |---share plane 0
        |---share plane 1
        ...

Because resource share, There are some limit on share plane: one group
of share planes need use same zpos, can not overlap, etc.

We assume share plane is a universal plane with some limit flags.
people who use the share plane need know the limit, should call the ioctl
DRM_CLIENT_CAP_SHARE_PLANES, and judge the planes limit before use it.

A group of share planes would has same shard id, so userspace can
group them, judge share plane's limit.

Signed-off-by: Mark Yao <mark.yao at rock-chips.com>
---
 drivers/gpu/drm/drm_crtc.c  | 110 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/drm_ioctl.c |   5 ++
 include/drm/drmP.h          |   5 ++
 include/drm/drm_crtc.h      |  14 ++++++
 include/uapi/drm/drm.h      |   7 +++
 5 files changed, 141 insertions(+)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 9d3f80e..3a8257e 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -1426,6 +1426,96 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
 EXPORT_SYMBOL(drm_plane_init);
 
 /**
+ * drm_share_plane_init - Initialize a share plane
+ * @dev: DRM device
+ * @plane: plane object to init
+ * @parent: this plane share some resources with parent plane.
+ * @possible_crtcs: bitmask of possible CRTCs
+ * @funcs: callbacks for the new plane
+ * @formats: array of supported formats (%DRM_FORMAT_*)
+ * @format_count: number of elements in @formats
+ * @type: type of plane (overlay, primary, cursor)
+ *
+ * With this API, the plane can share hardware resources with other planes.
+ *
+ *   --------------------------------------------------
+ *   |  scanout                                       |
+ *   |         ------------------                     |
+ *   |         |  parent plane  |                     |
+ *   |         | active scanout |                     |
+ *   |         |                |   ----------------- |
+ *   |         ------------------   | share plane 1 | |
+ *   |  -----------------           |active scanout | |
+ *   |  | share plane 0 |           |               | |
+ *   |  |active scanout |           ----------------- |
+ *   |  |               |                             |
+ *   |  -----------------                             |
+ *   --------------------------------------------------
+ *
+ *    parent plane
+ *        |---share plane 0
+ *        |---share plane 1
+ *        ...
+ *
+ * The plane hardware is used when the display scanout run into plane active
+ * scanout, that means we can reuse the plane hardware resources on plane
+ * non-active scanout.
+ *
+ * Because resource share, There are some limit on share plane: one group
+ * of share planes need use same zpos, can't not overlap, etc.
+ *
+ * Here assume share plane is a universal plane with some limit flags.
+ * people who use the share plane need know the limit, should call the ioctl
+ * DRM_CLIENT_CAP_SHARE_PLANES, and judge the planes limit before use it.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+
+int drm_share_plane_init(struct drm_device *dev, struct drm_plane *plane,
+			 struct drm_plane *parent,
+			 unsigned long possible_crtcs,
+			 const struct drm_plane_funcs *funcs,
+			 const uint32_t *formats, unsigned int format_count,
+			 enum drm_plane_type type)
+{
+	struct drm_mode_config *config = &dev->mode_config;
+	int ret;
+	int share_id;
+
+	/*
+	 * TODO: only verified on ATOMIC drm driver.
+	 */
+	if (!drm_core_check_feature(dev, DRIVER_ATOMIC))
+		return -EINVAL;
+
+	ret = drm_universal_plane_init(dev, plane, possible_crtcs, funcs,
+				       formats, format_count, type, NULL);
+	if (ret)
+		return ret;
+
+	if (parent) {
+		/*
+		 * Can't support more than two level plane share.
+		 */
+		WARN_ON(parent->parent);
+		share_id = parent->base.id;
+		plane->parent = parent;
+
+		config->num_share_plane++;
+		if (plane->type == DRM_PLANE_TYPE_OVERLAY)
+			config->num_share_overlay_plane++;
+	} else {
+		share_id = plane->base.id;
+	}
+
+	drm_object_attach_property(&plane->base,
+				   config->prop_share_id, share_id);
+	return 0;
+}
+EXPORT_SYMBOL(drm_share_plane_init);
+
+/**
  * drm_plane_cleanup - Clean up the core plane usage
  * @plane: plane to cleanup
  *
@@ -1452,6 +1542,11 @@ void drm_plane_cleanup(struct drm_plane *plane)
 	dev->mode_config.num_total_plane--;
 	if (plane->type == DRM_PLANE_TYPE_OVERLAY)
 		dev->mode_config.num_overlay_plane--;
+	if (plane->parent) {
+		dev->mode_config.num_share_plane--;
+		if (plane->type == DRM_PLANE_TYPE_OVERLAY)
+			dev->mode_config.num_share_overlay_plane--;
+	}
 	drm_modeset_unlock_all(dev);
 
 	WARN_ON(plane->state && !plane->funcs->atomic_destroy_state);
@@ -1600,6 +1695,13 @@ static int drm_mode_create_standard_properties(struct drm_device *dev)
 		return -ENOMEM;
 	dev->mode_config.plane_type_property = prop;
 
+	prop = drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE,
+					 "SHARE_ID", 0, UINT_MAX);
+	if (!prop)
+		return -ENOMEM;
+
+	dev->mode_config.prop_share_id = prop;
+
 	prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
 			"SRC_X", 0, UINT_MAX);
 	if (!prop)
@@ -2431,6 +2533,12 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data,
 		num_planes = config->num_total_plane;
 	else
 		num_planes = config->num_overlay_plane;
+	if (!file_priv->share_planes) {
+		if (file_priv->universal_planes)
+			num_planes -= config->num_share_plane;
+		else
+			num_planes -= config->num_share_overlay_plane;
+	}
 
 	/*
 	 * This ioctl is called twice, once to determine how much space is
@@ -2449,6 +2557,8 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data,
 			if (plane->type != DRM_PLANE_TYPE_OVERLAY &&
 			    !file_priv->universal_planes)
 				continue;
+			if (plane->parent && !file_priv->share_planes)
+				continue;
 
 			if (put_user(plane->base.id, plane_ptr + copied))
 				return -EFAULT;
diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
index 33af4a5..8b0120d 100644
--- a/drivers/gpu/drm/drm_ioctl.c
+++ b/drivers/gpu/drm/drm_ioctl.c
@@ -294,6 +294,11 @@ drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
 			return -EINVAL;
 		file_priv->universal_planes = req->value;
 		break;
+	case DRM_CLIENT_CAP_SHARE_PLANES:
+		if (req->value > 1)
+			return -EINVAL;
+		file_priv->share_planes = req->value;
+		break;
 	case DRM_CLIENT_CAP_ATOMIC:
 		if (!drm_core_check_feature(dev, DRIVER_ATOMIC))
 			return -EINVAL;
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index c2fe2cf..285d177 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -314,6 +314,11 @@ struct drm_file {
 	/* true if client understands atomic properties */
 	unsigned atomic:1;
 	/*
+	 * true if client understands share planes and
+	 * hardware support share planes.
+	 */
+	unsigned share_planes:1;
+	/*
 	 * This client is the creator of @master.
 	 * Protected by struct drm_device::master_mutex.
 	 */
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 9e6ab4a..a3fe9b0 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -1660,6 +1660,7 @@ enum drm_plane_type {
 /**
  * struct drm_plane - central DRM plane control structure
  * @dev: DRM device this plane belongs to
+ * @parent: this plane share some resources with parent plane.
  * @head: for list management
  * @name: human readable name, can be overwritten by the driver
  * @base: base mode object
@@ -1679,6 +1680,7 @@ enum drm_plane_type {
  */
 struct drm_plane {
 	struct drm_device *dev;
+	struct drm_plane *parent;
 	struct list_head head;
 
 	char *name;
@@ -2408,6 +2410,8 @@ struct drm_mode_config {
 	 */
 	int num_overlay_plane;
 	int num_total_plane;
+	int num_share_plane;
+	int num_share_overlay_plane;
 	struct list_head plane_list;
 
 	int num_crtc;
@@ -2428,6 +2432,9 @@ struct drm_mode_config {
 
 	struct mutex blob_lock;
 
+	/* pointers to share properties */
+	struct drm_property *prop_share_id;
+
 	/* pointers to standard properties */
 	struct list_head property_blob_list;
 	struct drm_property *edid_property;
@@ -2636,6 +2643,13 @@ extern int drm_plane_init(struct drm_device *dev,
 			  const struct drm_plane_funcs *funcs,
 			  const uint32_t *formats, unsigned int format_count,
 			  bool is_primary);
+extern int drm_share_plane_init(struct drm_device *dev, struct drm_plane *plane,
+				struct drm_plane *parent,
+				unsigned long possible_crtcs,
+				const struct drm_plane_funcs *funcs,
+				const uint32_t *formats,
+				unsigned int format_count,
+				enum drm_plane_type type);
 extern void drm_plane_cleanup(struct drm_plane *plane);
 
 /**
diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h
index 452675f..01979a4 100644
--- a/include/uapi/drm/drm.h
+++ b/include/uapi/drm/drm.h
@@ -677,6 +677,13 @@ struct drm_get_cap {
  */
 #define DRM_CLIENT_CAP_ATOMIC	3
 
+/**
+ * DRM_CLIENT_CAP_SHARE_PLANES
+ *
+ * If set to 1, the DRM core will expose share planes to userspace.
+ */
+#define DRM_CLIENT_CAP_SHARE_PLANES	4
+
 /** DRM_IOCTL_SET_CLIENT_CAP ioctl argument type */
 struct drm_set_client_cap {
 	__u64 capability;
-- 
1.9.1




From mark.yao at rock-chips.com  Tue Jul 26 00:46:33 2016
From: mark.yao at rock-chips.com (Mark Yao)
Date: Tue, 26 Jul 2016 15:46:33 +0800
Subject: [PATCH 2/3] drm/rockchip: vop: support multi area plane
In-Reply-To: <1469519194-23133-1-git-send-email-mark.yao@rock-chips.com>
References: <1469519194-23133-1-git-send-email-mark.yao@rock-chips.com>
Message-ID: <1469519194-23133-2-git-send-email-mark.yao@rock-chips.com>

The series vop of VOP_FULL framework support area plane, such as
RK3288 and RK3399, one group of area planes share same hardware,
reuse the hardware on different scanout time, this design is
useful to support mulit planes with low hardware consume.

Signed-off-by: Mark Yao <mark.yao at rock-chips.com>
---
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 191 +++++++++++++++++-----------
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   4 +-
 2 files changed, 123 insertions(+), 72 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 91305eb..bd084c0 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -41,17 +41,19 @@
 #define __REG_SET_NORMAL(x, off, mask, shift, v) \
 		vop_mask_write(x, off, (mask) << shift, (v) << shift)
 
-#define REG_SET(x, base, reg, v, mode) \
-		__REG_SET_##mode(x, base + reg.offset, reg.mask, reg.shift, v)
-#define REG_SET_MASK(x, base, reg, mask, v, mode) \
-		__REG_SET_##mode(x, base + reg.offset, mask, reg.shift, v)
+#define REG_SET(x, off, reg, v, mode) \
+		__REG_SET_##mode(x, off + reg.offset, \
+				 reg.mask, reg.shift, v)
+#define REG_SET_MASK(x, off, reg, mask, v, mode) \
+		__REG_SET_##mode(x, off + reg.offset, mask, reg.shift, v)
 
 #define VOP_WIN_SET(x, win, name, v) \
-		REG_SET(x, win->base, win->phy->name, v, RELAXED)
+		REG_SET(x, win->offset, VOP_WIN_NAME(win, name), v, RELAXED)
 #define VOP_SCL_SET(x, win, name, v) \
-		REG_SET(x, win->base, win->phy->scl->name, v, RELAXED)
+		REG_SET(x, win->offset, win->phy->scl->name, v, RELAXED)
 #define VOP_SCL_SET_EXT(x, win, name, v) \
-		REG_SET(x, win->base, win->phy->scl->ext->name, v, RELAXED)
+		REG_SET(x, win->offset, win->phy->scl->ext->name, v, RELAXED)
+
 #define VOP_CTRL_SET(x, name, v) \
 		REG_SET(x, 0, (x)->data->ctrl->name, v, NORMAL)
 
@@ -75,10 +77,13 @@
 		vop_get_intr_type(vop, &vop->data->intr->name, type)
 
 #define VOP_WIN_GET(x, win, name) \
-		vop_read_reg(x, win->base, &win->phy->name)
+		vop_read_reg(x, win->offset, &VOP_WIN_NAME(win, name))
+
+#define VOP_WIN_NAME(win, name) \
+		(vop_get_win_phy(win, &win->phy->name)->name)
 
 #define VOP_WIN_GET_YRGBADDR(vop, win) \
-		vop_readl(vop, win->base + win->phy->yrgb_mst.offset)
+		vop_readl(vop, win->offset + VOP_WIN_NAME(win, yrgb_mst).offset)
 
 #define to_vop(x) container_of(x, struct vop, crtc)
 #define to_vop_win(x) container_of(x, struct vop_win, base)
@@ -94,8 +99,14 @@ struct vop_plane_state {
 };
 
 struct vop_win {
+	struct vop_win *parent;
 	struct drm_plane base;
-	const struct vop_win_data *data;
+
+	uint32_t offset;
+	enum drm_plane_type type;
+	const struct vop_win_phy *phy;
+	const uint32_t *data_formats;
+	uint32_t nformats;
 	struct vop *vop;
 
 	/* protected by dev->event_lock */
@@ -119,6 +130,7 @@ struct vop {
 	struct drm_pending_vblank_event *event;
 
 	const struct vop_data *data;
+	int num_wins;
 
 	uint32_t *regsbak;
 	void __iomem *regs;
@@ -187,6 +199,15 @@ static inline void vop_mask_write_relaxed(struct vop *vop, uint32_t offset,
 	}
 }
 
+static inline const struct vop_win_phy *
+vop_get_win_phy(struct vop_win *win, const struct vop_reg *reg)
+{
+	if (!reg->mask && win->parent)
+		return win->parent->phy;
+
+	return win->phy;
+}
+
 static inline uint32_t vop_get_intr_type(struct vop *vop,
 					 const struct vop_reg *reg, int type)
 {
@@ -299,9 +320,9 @@ static uint16_t scl_vop_cal_scale(enum scale_mode mode, uint32_t src,
 	return val;
 }
 
-static void scl_vop_cal_scl_fac(struct vop *vop, const struct vop_win_data *win,
-			     uint32_t src_w, uint32_t src_h, uint32_t dst_w,
-			     uint32_t dst_h, uint32_t pixel_format)
+static void scl_vop_cal_scl_fac(struct vop *vop, struct vop_win *win,
+				uint32_t src_w, uint32_t src_h, uint32_t dst_w,
+				uint32_t dst_h, uint32_t pixel_format)
 {
 	uint16_t yrgb_hor_scl_mode, yrgb_ver_scl_mode;
 	uint16_t cbcr_hor_scl_mode = SCALE_NONE;
@@ -316,6 +337,9 @@ static void scl_vop_cal_scl_fac(struct vop *vop, const struct vop_win_data *win,
 	uint32_t val;
 	int vskiplines = 0;
 
+	if (!win->phy->scl)
+		return;
+
 	if (dst_w > 3840) {
 		DRM_ERROR("Maximum destination width (3840) exceeded\n");
 		return;
@@ -509,9 +533,8 @@ static void vop_crtc_disable(struct drm_crtc *crtc)
 	 * disable that crtc. Otherwise we might try to scan from a destroyed
 	 * buffer later.
 	 */
-	for (i = 0; i < vop->data->win_size; i++) {
-		struct vop_win *vop_win = &vop->win[i];
-		const struct vop_win_data *win = vop_win->data;
+	for (i = 0; i < vop->num_wins; i++) {
+		struct vop_win *win = &vop->win[i];
 
 		spin_lock(&vop->reg_lock);
 		VOP_WIN_SET(vop, win, enable, 0);
@@ -590,9 +613,8 @@ static int vop_plane_atomic_check(struct drm_plane *plane,
 	struct drm_crtc *crtc = state->crtc;
 	struct drm_crtc_state *crtc_state;
 	struct drm_framebuffer *fb = state->fb;
-	struct vop_win *vop_win = to_vop_win(plane);
+	struct vop_win *win = to_vop_win(plane);
 	struct vop_plane_state *vop_plane_state = to_vop_plane_state(state);
-	const struct vop_win_data *win = vop_win->data;
 	bool visible;
 	int ret;
 	struct drm_rect *dest = &vop_plane_state->dest;
@@ -660,16 +682,15 @@ static void vop_plane_atomic_disable(struct drm_plane *plane,
 				     struct drm_plane_state *old_state)
 {
 	struct vop_plane_state *vop_plane_state = to_vop_plane_state(old_state);
-	struct vop_win *vop_win = to_vop_win(plane);
-	const struct vop_win_data *win = vop_win->data;
+	struct vop_win *win = to_vop_win(plane);
 	struct vop *vop = to_vop(old_state->crtc);
 
 	if (!old_state->crtc)
 		return;
 
 	spin_lock_irq(&plane->dev->event_lock);
-	vop_win->enable = false;
-	vop_win->yrgb_mst = 0;
+	win->enable = false;
+	win->yrgb_mst = 0;
 	spin_unlock_irq(&plane->dev->event_lock);
 
 	spin_lock(&vop->reg_lock);
@@ -686,9 +707,8 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
 {
 	struct drm_plane_state *state = plane->state;
 	struct drm_crtc *crtc = state->crtc;
-	struct vop_win *vop_win = to_vop_win(plane);
+	struct vop_win *win = to_vop_win(plane);
 	struct vop_plane_state *vop_plane_state = to_vop_plane_state(state);
-	const struct vop_win_data *win = vop_win->data;
 	struct vop *vop = to_vop(state->crtc);
 	struct drm_framebuffer *fb = state->fb;
 	unsigned int actual_w, actual_h;
@@ -736,8 +756,8 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
 	vop_plane_state->yrgb_mst = rk_obj->dma_addr + offset + fb->offsets[0];
 
 	spin_lock_irq(&plane->dev->event_lock);
-	vop_win->enable = true;
-	vop_win->yrgb_mst = vop_plane_state->yrgb_mst;
+	win->enable = true;
+	win->yrgb_mst = vop_plane_state->yrgb_mst;
 	spin_unlock_irq(&plane->dev->event_lock);
 
 	spin_lock(&vop->reg_lock);
@@ -761,10 +781,9 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
 		VOP_WIN_SET(vop, win, uv_mst, dma_addr);
 	}
 
-	if (win->phy->scl)
-		scl_vop_cal_scl_fac(vop, win, actual_w, actual_h,
-				    drm_rect_width(dest), drm_rect_height(dest),
-				    fb->pixel_format);
+	scl_vop_cal_scl_fac(vop, win, actual_w, actual_h,
+			    drm_rect_width(dest), drm_rect_height(dest),
+			    fb->pixel_format);
 
 	VOP_WIN_SET(vop, win, act_info, act_info);
 	VOP_WIN_SET(vop, win, dsp_info, dsp_info);
@@ -1098,9 +1117,9 @@ static bool vop_win_pending_is_complete(struct vop_win *vop_win)
 	dma_addr_t yrgb_mst;
 
 	if (!vop_win->enable)
-		return VOP_WIN_GET(vop_win->vop, vop_win->data, enable) == 0;
+		return VOP_WIN_GET(vop_win->vop, vop_win, enable) == 0;
 
-	yrgb_mst = VOP_WIN_GET_YRGBADDR(vop_win->vop, vop_win->data);
+	yrgb_mst = VOP_WIN_GET_YRGBADDR(vop_win->vop, vop_win);
 
 	return yrgb_mst == vop_win->yrgb_mst;
 }
@@ -1112,7 +1131,7 @@ static void vop_handle_vblank(struct vop *vop)
 	unsigned long flags;
 	int i;
 
-	for (i = 0; i < vop->data->win_size; i++) {
+	for (i = 0; i < vop->num_wins; i++) {
 		if (!vop_win_pending_is_complete(&vop->win[i]))
 			return;
 	}
@@ -1176,15 +1195,35 @@ static irqreturn_t vop_isr(int irq, void *data)
 	return ret;
 }
 
+static int vop_plane_init(struct vop *vop, struct vop_win *win,
+			  unsigned long possible_crtcs)
+{
+	struct drm_plane *share = NULL;
+	int ret;
+
+	if (win->parent)
+		share = &win->parent->base;
+
+	ret = drm_share_plane_init(vop->drm_dev, &win->base, share,
+				   possible_crtcs, &vop_plane_funcs,
+				   win->data_formats, win->nformats, win->type);
+	if (ret) {
+		DRM_ERROR("failed to initialize plane\n");
+		return ret;
+	}
+	drm_plane_helper_add(&win->base, &plane_helper_funcs);
+
+	return 0;
+}
+
 static int vop_create_crtc(struct vop *vop)
 {
-	const struct vop_data *vop_data = vop->data;
 	struct device *dev = vop->dev;
 	struct drm_device *drm_dev = vop->drm_dev;
 	struct drm_plane *primary = NULL, *cursor = NULL, *plane, *tmp;
 	struct drm_crtc *crtc = &vop->crtc;
 	struct device_node *port;
-	int ret;
+	int ret = 0;
 	int i;
 
 	/*
@@ -1192,30 +1231,22 @@ static int vop_create_crtc(struct vop *vop)
 	 * to pass them to drm_crtc_init_with_planes, which sets the
 	 * "possible_crtcs" to the newly initialized crtc.
 	 */
-	for (i = 0; i < vop_data->win_size; i++) {
-		struct vop_win *vop_win = &vop->win[i];
-		const struct vop_win_data *win_data = vop_win->data;
+	for (i = 0; i < vop->num_wins; i++) {
+		struct vop_win *win = &vop->win[i];
 
-		if (win_data->type != DRM_PLANE_TYPE_PRIMARY &&
-		    win_data->type != DRM_PLANE_TYPE_CURSOR)
+		if (win->type != DRM_PLANE_TYPE_PRIMARY &&
+		    win->type != DRM_PLANE_TYPE_CURSOR)
 			continue;
 
-		ret = drm_universal_plane_init(vop->drm_dev, &vop_win->base,
-					       0, &vop_plane_funcs,
-					       win_data->phy->data_formats,
-					       win_data->phy->nformats,
-					       win_data->type, NULL);
-		if (ret) {
-			DRM_ERROR("failed to initialize plane\n");
+		if (vop_plane_init(vop, win, 0))
 			goto err_cleanup_planes;
-		}
 
-		plane = &vop_win->base;
-		drm_plane_helper_add(plane, &plane_helper_funcs);
+		plane = &win->base;
 		if (plane->type == DRM_PLANE_TYPE_PRIMARY)
 			primary = plane;
 		else if (plane->type == DRM_PLANE_TYPE_CURSOR)
 			cursor = plane;
+
 	}
 
 	ret = drm_crtc_init_with_planes(drm_dev, crtc, primary, cursor,
@@ -1229,25 +1260,15 @@ static int vop_create_crtc(struct vop *vop)
 	 * Create drm_planes for overlay windows with possible_crtcs restricted
 	 * to the newly created crtc.
 	 */
-	for (i = 0; i < vop_data->win_size; i++) {
-		struct vop_win *vop_win = &vop->win[i];
-		const struct vop_win_data *win_data = vop_win->data;
+	for (i = 0; i < vop->num_wins; i++) {
+		struct vop_win *win = &vop->win[i];
 		unsigned long possible_crtcs = 1 << drm_crtc_index(crtc);
 
-		if (win_data->type != DRM_PLANE_TYPE_OVERLAY)
+		if (win->type != DRM_PLANE_TYPE_OVERLAY)
 			continue;
 
-		ret = drm_universal_plane_init(vop->drm_dev, &vop_win->base,
-					       possible_crtcs,
-					       &vop_plane_funcs,
-					       win_data->phy->data_formats,
-					       win_data->phy->nformats,
-					       win_data->type, NULL);
-		if (ret) {
-			DRM_ERROR("failed to initialize overlay plane\n");
+		if (vop_plane_init(vop, win, possible_crtcs))
 			goto err_cleanup_crtc;
-		}
-		drm_plane_helper_add(&vop_win->base, &plane_helper_funcs);
 	}
 
 	port = of_get_child_by_name(dev->of_node, "port");
@@ -1362,8 +1383,8 @@ static int vop_initial(struct vop *vop)
 	for (i = 0; i < vop_data->table_size; i++)
 		vop_writel(vop, init_table[i].offset, init_table[i].value);
 
-	for (i = 0; i < vop_data->win_size; i++) {
-		const struct vop_win_data *win = &vop_data->win[i];
+	for (i = 0; i < vop->num_wins; i++) {
+		struct vop_win *win = &vop->win[i];
 
 		VOP_WIN_SET(vop, win, enable, 0);
 	}
@@ -1405,14 +1426,34 @@ err_unprepare_dclk:
 static void vop_win_init(struct vop *vop)
 {
 	const struct vop_data *vop_data = vop->data;
-	unsigned int i;
+	unsigned int i, j;
+	unsigned int num_wins = 0;
 
 	for (i = 0; i < vop_data->win_size; i++) {
-		struct vop_win *vop_win = &vop->win[i];
+		struct vop_win *vop_win = &vop->win[num_wins];
 		const struct vop_win_data *win_data = &vop_data->win[i];
 
-		vop_win->data = win_data;
+		vop_win->phy = win_data->phy;
+		vop_win->offset = win_data->base;
+		vop_win->type = win_data->type;
+		vop_win->data_formats = win_data->phy->data_formats;
+		vop_win->nformats = win_data->phy->nformats;
 		vop_win->vop = vop;
+		num_wins++;
+
+		for (j = 0; j < win_data->area_size; j++) {
+			struct vop_win *vop_area = &vop->win[num_wins];
+			const struct vop_win_phy *area = win_data->area[j];
+
+			vop_area->parent = vop_win;
+			vop_area->offset = vop_win->offset;
+			vop_area->phy = area;
+			vop_area->type = DRM_PLANE_TYPE_OVERLAY;
+			vop_area->data_formats = vop_win->data_formats;
+			vop_area->nformats = vop_win->nformats;
+			vop_area->vop = vop;
+			num_wins++;
+		}
 	}
 }
 
@@ -1424,14 +1465,21 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
 	struct vop *vop;
 	struct resource *res;
 	size_t alloc_size;
-	int ret, irq;
+	int ret, irq, i;
+	int num_wins = 0;
 
 	vop_data = of_device_get_match_data(dev);
 	if (!vop_data)
 		return -ENODEV;
 
+	for (i = 0; i < vop_data->win_size; i++) {
+		const struct vop_win_data *win_data = &vop_data->win[i];
+
+		num_wins += win_data->area_size + 1;
+	}
+
 	/* Allocate vop struct and its vop_win array */
-	alloc_size = sizeof(*vop) + sizeof(*vop->win) * vop_data->win_size;
+	alloc_size = sizeof(*vop) + sizeof(*vop->win) * num_wins;
 	vop = devm_kzalloc(dev, alloc_size, GFP_KERNEL);
 	if (!vop)
 		return -ENOMEM;
@@ -1439,6 +1487,7 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
 	vop->dev = dev;
 	vop->data = vop_data;
 	vop->drm_dev = drm_dev;
+	vop->num_wins = num_wins;
 	dev_set_drvdata(dev, vop);
 
 	vop_win_init(vop);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index 071ff0b..def5afa 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -122,8 +122,10 @@ struct vop_win_phy {
 
 struct vop_win_data {
 	uint32_t base;
-	const struct vop_win_phy *phy;
 	enum drm_plane_type type;
+	const struct vop_win_phy *phy;
+	const struct vop_win_phy **area;
+	unsigned int area_size;
 };
 
 struct vop_data {
-- 
1.9.1




From mark.yao at rock-chips.com  Tue Jul 26 00:46:34 2016
From: mark.yao at rock-chips.com (Mark Yao)
Date: Tue, 26 Jul 2016 15:46:34 +0800
Subject: [PATCH 3/3] drm/rockchip: vop: rk3288: add area plane
In-Reply-To: <1469519194-23133-1-git-send-email-mark.yao@rock-chips.com>
References: <1469519194-23133-1-git-send-email-mark.yao@rock-chips.com>
Message-ID: <1469519194-23133-3-git-send-email-mark.yao@rock-chips.com>

Change-Id: Iac8fde019020d8f1a671d52c1a4d91ad2d050d43
Signed-off-by: Mark Yao <mark.yao at rock-chips.com>
---
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 52 ++++++++++++++++++++++++-----
 1 file changed, 44 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 919992c..0f53ebd 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -103,7 +103,7 @@ static const struct vop_win_phy rk3288_win01_data = {
 static const struct vop_win_phy rk3288_win23_data = {
 	.data_formats = formats_win_lite,
 	.nformats = ARRAY_SIZE(formats_win_lite),
-	.enable = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 0),
+	.enable = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 4),
 	.format = VOP_REG(RK3288_WIN2_CTRL0, 0x7, 1),
 	.rb_swap = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 12),
 	.dsp_info = VOP_REG(RK3288_WIN2_DSP_INFO0, 0x0fff0fff, 0),
@@ -114,6 +114,36 @@ static const struct vop_win_phy rk3288_win23_data = {
 	.dst_alpha_ctl = VOP_REG(RK3288_WIN2_DST_ALPHA_CTRL, 0xff, 0),
 };
 
+static const struct vop_win_phy rk3288_area1_data = {
+	.enable = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 5),
+	.dsp_info = VOP_REG(RK3288_WIN2_DSP_INFO1, 0x0fff0fff, 0),
+	.dsp_st = VOP_REG(RK3288_WIN2_DSP_ST1, 0x1fff1fff, 0),
+	.yrgb_mst = VOP_REG(RK3288_WIN2_MST1, 0xffffffff, 0),
+	.yrgb_vir = VOP_REG(RK3288_WIN2_VIR0_1, 0x1fff, 16),
+};
+
+static const struct vop_win_phy rk3288_area2_data = {
+	.enable = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 6),
+	.dsp_info = VOP_REG(RK3288_WIN2_DSP_INFO2, 0x0fff0fff, 0),
+	.dsp_st = VOP_REG(RK3288_WIN2_DSP_ST2, 0x1fff1fff, 0),
+	.yrgb_mst = VOP_REG(RK3288_WIN2_MST2, 0xffffffff, 0),
+	.yrgb_vir = VOP_REG(RK3288_WIN2_VIR2_3, 0x1fff, 0),
+};
+
+static const struct vop_win_phy rk3288_area3_data = {
+	.enable = VOP_REG(RK3288_WIN2_CTRL0, 0x1, 7),
+	.dsp_info = VOP_REG(RK3288_WIN2_DSP_INFO3, 0x0fff0fff, 0),
+	.dsp_st = VOP_REG(RK3288_WIN2_DSP_ST3, 0x1fff1fff, 0),
+	.yrgb_mst = VOP_REG(RK3288_WIN2_MST3, 0xffffffff, 0),
+	.yrgb_vir = VOP_REG(RK3288_WIN2_VIR2_3, 0x1fff, 16),
+};
+
+static const struct vop_win_phy *rk3288_area_data[] = {
+	&rk3288_area1_data,
+	&rk3288_area2_data,
+	&rk3288_area3_data
+};
+
 static const struct vop_ctrl rk3288_ctrl_data = {
 	.standby = VOP_REG(RK3288_SYS_CTRL, 0x1, 22),
 	.gate_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 23),
@@ -141,12 +171,14 @@ static const struct vop_reg_data rk3288_init_reg_table[] = {
 	{RK3288_DSP_CTRL0, 0x00000000},
 	{RK3288_WIN0_CTRL0, 0x00000080},
 	{RK3288_WIN1_CTRL0, 0x00000080},
-	/* TODO: Win2/3 support multiple area function, but we haven't found
-	 * a suitable way to use it yet, so let's just use them as other windows
-	 * with only area 0 enabled.
+	/*
+	 * Bit[0] is win2/3 gate en bit, there is no power consume with this
+	 * bit enable. the bit's function similar with area plane enable bit,
+	 * So default enable this bit, then We can control win2/3 area plane
+	 * with its enable bit.
 	 */
-	{RK3288_WIN2_CTRL0, 0x00000010},
-	{RK3288_WIN3_CTRL0, 0x00000010},
+	{RK3288_WIN2_CTRL0, 0x00000001},
+	{RK3288_WIN3_CTRL0, 0x00000001},
 };
 
 /*
@@ -161,9 +193,13 @@ static const struct vop_win_data rk3288_vop_win_data[] = {
 	{ .base = 0x40, .phy = &rk3288_win01_data,
 	  .type = DRM_PLANE_TYPE_OVERLAY },
 	{ .base = 0x00, .phy = &rk3288_win23_data,
-	  .type = DRM_PLANE_TYPE_OVERLAY },
+	  .type = DRM_PLANE_TYPE_OVERLAY,
+	  .area = rk3288_area_data,
+	  .area_size = ARRAY_SIZE(rk3288_area_data), },
 	{ .base = 0x50, .phy = &rk3288_win23_data,
-	  .type = DRM_PLANE_TYPE_CURSOR },
+	  .type = DRM_PLANE_TYPE_CURSOR,
+	  .area = rk3288_area_data,
+	  .area_size = ARRAY_SIZE(rk3288_area_data), },
 };
 
 static const int rk3288_vop_intrs[] = {
-- 
1.9.1




From daniel at ffwll.ch  Tue Jul 26 01:26:35 2016
From: daniel at ffwll.ch (Daniel Vetter)
Date: Tue, 26 Jul 2016 10:26:35 +0200
Subject: [PATCH 1/3] drm: introduce share plane
In-Reply-To: <1469519194-23133-1-git-send-email-mark.yao@rock-chips.com>
References: <1469519194-23133-1-git-send-email-mark.yao@rock-chips.com>
Message-ID: <20160726082635.GA31475@phenom.ffwll.local>

On Tue, Jul 26, 2016 at 03:46:32PM +0800, Mark Yao wrote:
> What is share plane:
> Plane hardware only be used when the display scanout run into plane active
> scanout, that means we can reuse the plane hardware resources on plane
> non-active scanout.
> 
>      --------------------------------------------------
>     |  scanout                                       |
>     |         ------------------                     |
>     |         | parent plane   |                     |
>     |         | active scanout |                     |
>     |         |                |   ----------------- |
>     |         ------------------   | share plane 1 | |
>     |  -----------------           |active scanout | |
>     |  | share plane 0 |           |               | |
>     |  |active scanout |           ----------------- |
>     |  |               |                             |
>     |  -----------------                             |
>     --------------------------------------------------
> One plane hardware can be reuse for multi-planes, we assume the first
> plane is parent plane, other planes share the resource with first one.
>     parent plane
>         |---share plane 0
>         |---share plane 1
>         ...
> 
> Because resource share, There are some limit on share plane: one group
> of share planes need use same zpos, can not overlap, etc.
> 
> We assume share plane is a universal plane with some limit flags.
> people who use the share plane need know the limit, should call the ioctl
> DRM_CLIENT_CAP_SHARE_PLANES, and judge the planes limit before use it.
> 
> A group of share planes would has same shard id, so userspace can
> group them, judge share plane's limit.
> 
> Signed-off-by: Mark Yao <mark.yao at rock-chips.com>

This seems extremely hw specific, why exactly do we need to add a new
relationship on planes? What does this buy on _other_ drivers?

Imo this should be solved by virtualizing planes in the driver. Start out
by assigning planes, and if you can reuse one for sharing then do that,
otherwise allocate a new one. If there's not enough real planes, fail the
atomic_check.

This seems way to hw specific to be useful as a generic concept.
-Daniel

> ---
>  drivers/gpu/drm/drm_crtc.c  | 110 ++++++++++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/drm_ioctl.c |   5 ++
>  include/drm/drmP.h          |   5 ++
>  include/drm/drm_crtc.h      |  14 ++++++
>  include/uapi/drm/drm.h      |   7 +++
>  5 files changed, 141 insertions(+)
> 
> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
> index 9d3f80e..3a8257e 100644
> --- a/drivers/gpu/drm/drm_crtc.c
> +++ b/drivers/gpu/drm/drm_crtc.c
> @@ -1426,6 +1426,96 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
>  EXPORT_SYMBOL(drm_plane_init);
>  
>  /**
> + * drm_share_plane_init - Initialize a share plane
> + * @dev: DRM device
> + * @plane: plane object to init
> + * @parent: this plane share some resources with parent plane.
> + * @possible_crtcs: bitmask of possible CRTCs
> + * @funcs: callbacks for the new plane
> + * @formats: array of supported formats (%DRM_FORMAT_*)
> + * @format_count: number of elements in @formats
> + * @type: type of plane (overlay, primary, cursor)
> + *
> + * With this API, the plane can share hardware resources with other planes.
> + *
> + *   --------------------------------------------------
> + *   |  scanout                                       |
> + *   |         ------------------                     |
> + *   |         |  parent plane  |                     |
> + *   |         | active scanout |                     |
> + *   |         |                |   ----------------- |
> + *   |         ------------------   | share plane 1 | |
> + *   |  -----------------           |active scanout | |
> + *   |  | share plane 0 |           |               | |
> + *   |  |active scanout |           ----------------- |
> + *   |  |               |                             |
> + *   |  -----------------                             |
> + *   --------------------------------------------------
> + *
> + *    parent plane
> + *        |---share plane 0
> + *        |---share plane 1
> + *        ...
> + *
> + * The plane hardware is used when the display scanout run into plane active
> + * scanout, that means we can reuse the plane hardware resources on plane
> + * non-active scanout.
> + *
> + * Because resource share, There are some limit on share plane: one group
> + * of share planes need use same zpos, can't not overlap, etc.
> + *
> + * Here assume share plane is a universal plane with some limit flags.
> + * people who use the share plane need know the limit, should call the ioctl
> + * DRM_CLIENT_CAP_SHARE_PLANES, and judge the planes limit before use it.
> + *
> + * Returns:
> + * Zero on success, error code on failure.
> + */
> +
> +int drm_share_plane_init(struct drm_device *dev, struct drm_plane *plane,
> +			 struct drm_plane *parent,
> +			 unsigned long possible_crtcs,
> +			 const struct drm_plane_funcs *funcs,
> +			 const uint32_t *formats, unsigned int format_count,
> +			 enum drm_plane_type type)
> +{
> +	struct drm_mode_config *config = &dev->mode_config;
> +	int ret;
> +	int share_id;
> +
> +	/*
> +	 * TODO: only verified on ATOMIC drm driver.
> +	 */
> +	if (!drm_core_check_feature(dev, DRIVER_ATOMIC))
> +		return -EINVAL;
> +
> +	ret = drm_universal_plane_init(dev, plane, possible_crtcs, funcs,
> +				       formats, format_count, type, NULL);
> +	if (ret)
> +		return ret;
> +
> +	if (parent) {
> +		/*
> +		 * Can't support more than two level plane share.
> +		 */
> +		WARN_ON(parent->parent);
> +		share_id = parent->base.id;
> +		plane->parent = parent;
> +
> +		config->num_share_plane++;
> +		if (plane->type == DRM_PLANE_TYPE_OVERLAY)
> +			config->num_share_overlay_plane++;
> +	} else {
> +		share_id = plane->base.id;
> +	}
> +
> +	drm_object_attach_property(&plane->base,
> +				   config->prop_share_id, share_id);
> +	return 0;
> +}
> +EXPORT_SYMBOL(drm_share_plane_init);
> +
> +/**
>   * drm_plane_cleanup - Clean up the core plane usage
>   * @plane: plane to cleanup
>   *
> @@ -1452,6 +1542,11 @@ void drm_plane_cleanup(struct drm_plane *plane)
>  	dev->mode_config.num_total_plane--;
>  	if (plane->type == DRM_PLANE_TYPE_OVERLAY)
>  		dev->mode_config.num_overlay_plane--;
> +	if (plane->parent) {
> +		dev->mode_config.num_share_plane--;
> +		if (plane->type == DRM_PLANE_TYPE_OVERLAY)
> +			dev->mode_config.num_share_overlay_plane--;
> +	}
>  	drm_modeset_unlock_all(dev);
>  
>  	WARN_ON(plane->state && !plane->funcs->atomic_destroy_state);
> @@ -1600,6 +1695,13 @@ static int drm_mode_create_standard_properties(struct drm_device *dev)
>  		return -ENOMEM;
>  	dev->mode_config.plane_type_property = prop;
>  
> +	prop = drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE,
> +					 "SHARE_ID", 0, UINT_MAX);
> +	if (!prop)
> +		return -ENOMEM;
> +
> +	dev->mode_config.prop_share_id = prop;
> +
>  	prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
>  			"SRC_X", 0, UINT_MAX);
>  	if (!prop)
> @@ -2431,6 +2533,12 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data,
>  		num_planes = config->num_total_plane;
>  	else
>  		num_planes = config->num_overlay_plane;
> +	if (!file_priv->share_planes) {
> +		if (file_priv->universal_planes)
> +			num_planes -= config->num_share_plane;
> +		else
> +			num_planes -= config->num_share_overlay_plane;
> +	}
>  
>  	/*
>  	 * This ioctl is called twice, once to determine how much space is
> @@ -2449,6 +2557,8 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data,
>  			if (plane->type != DRM_PLANE_TYPE_OVERLAY &&
>  			    !file_priv->universal_planes)
>  				continue;
> +			if (plane->parent && !file_priv->share_planes)
> +				continue;
>  
>  			if (put_user(plane->base.id, plane_ptr + copied))
>  				return -EFAULT;
> diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
> index 33af4a5..8b0120d 100644
> --- a/drivers/gpu/drm/drm_ioctl.c
> +++ b/drivers/gpu/drm/drm_ioctl.c
> @@ -294,6 +294,11 @@ drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
>  			return -EINVAL;
>  		file_priv->universal_planes = req->value;
>  		break;
> +	case DRM_CLIENT_CAP_SHARE_PLANES:
> +		if (req->value > 1)
> +			return -EINVAL;
> +		file_priv->share_planes = req->value;
> +		break;
>  	case DRM_CLIENT_CAP_ATOMIC:
>  		if (!drm_core_check_feature(dev, DRIVER_ATOMIC))
>  			return -EINVAL;
> diff --git a/include/drm/drmP.h b/include/drm/drmP.h
> index c2fe2cf..285d177 100644
> --- a/include/drm/drmP.h
> +++ b/include/drm/drmP.h
> @@ -314,6 +314,11 @@ struct drm_file {
>  	/* true if client understands atomic properties */
>  	unsigned atomic:1;
>  	/*
> +	 * true if client understands share planes and
> +	 * hardware support share planes.
> +	 */
> +	unsigned share_planes:1;
> +	/*
>  	 * This client is the creator of @master.
>  	 * Protected by struct drm_device::master_mutex.
>  	 */
> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
> index 9e6ab4a..a3fe9b0 100644
> --- a/include/drm/drm_crtc.h
> +++ b/include/drm/drm_crtc.h
> @@ -1660,6 +1660,7 @@ enum drm_plane_type {
>  /**
>   * struct drm_plane - central DRM plane control structure
>   * @dev: DRM device this plane belongs to
> + * @parent: this plane share some resources with parent plane.
>   * @head: for list management
>   * @name: human readable name, can be overwritten by the driver
>   * @base: base mode object
> @@ -1679,6 +1680,7 @@ enum drm_plane_type {
>   */
>  struct drm_plane {
>  	struct drm_device *dev;
> +	struct drm_plane *parent;
>  	struct list_head head;
>  
>  	char *name;
> @@ -2408,6 +2410,8 @@ struct drm_mode_config {
>  	 */
>  	int num_overlay_plane;
>  	int num_total_plane;
> +	int num_share_plane;
> +	int num_share_overlay_plane;
>  	struct list_head plane_list;
>  
>  	int num_crtc;
> @@ -2428,6 +2432,9 @@ struct drm_mode_config {
>  
>  	struct mutex blob_lock;
>  
> +	/* pointers to share properties */
> +	struct drm_property *prop_share_id;
> +
>  	/* pointers to standard properties */
>  	struct list_head property_blob_list;
>  	struct drm_property *edid_property;
> @@ -2636,6 +2643,13 @@ extern int drm_plane_init(struct drm_device *dev,
>  			  const struct drm_plane_funcs *funcs,
>  			  const uint32_t *formats, unsigned int format_count,
>  			  bool is_primary);
> +extern int drm_share_plane_init(struct drm_device *dev, struct drm_plane *plane,
> +				struct drm_plane *parent,
> +				unsigned long possible_crtcs,
> +				const struct drm_plane_funcs *funcs,
> +				const uint32_t *formats,
> +				unsigned int format_count,
> +				enum drm_plane_type type);
>  extern void drm_plane_cleanup(struct drm_plane *plane);
>  
>  /**
> diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h
> index 452675f..01979a4 100644
> --- a/include/uapi/drm/drm.h
> +++ b/include/uapi/drm/drm.h
> @@ -677,6 +677,13 @@ struct drm_get_cap {
>   */
>  #define DRM_CLIENT_CAP_ATOMIC	3
>  
> +/**
> + * DRM_CLIENT_CAP_SHARE_PLANES
> + *
> + * If set to 1, the DRM core will expose share planes to userspace.
> + */
> +#define DRM_CLIENT_CAP_SHARE_PLANES	4
> +
>  /** DRM_IOCTL_SET_CLIENT_CAP ioctl argument type */
>  struct drm_set_client_cap {
>  	__u64 capability;
> -- 
> 1.9.1
> 
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


From john at metanate.com  Tue Jul 26 02:00:07 2016
From: john at metanate.com (John Keeping)
Date: Tue, 26 Jul 2016 10:00:07 +0100
Subject: [PATCH 1/4] iio: adc: rockchip_saradc: reset saradc controller
 before programming it
In-Reply-To: <1469513510-1516-1-git-send-email-wxt@rock-chips.com>
References: <1469513510-1516-1-git-send-email-wxt@rock-chips.com>
Message-ID: <20160726100007.5166e7f9.john@metanate.com>

On Tue, 26 Jul 2016 14:11:47 +0800, Caesar Wang wrote:

> SARADC controller needs to be reset before programming it, otherwise
> it will not function properly.
> 
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
> Cc: Jonathan Cameron <jic23 at kernel.org>
> Cc: Heiko Stuebner <heiko at sntech.de>
> Cc: Rob Herring <robh+dt at kernel.org>
> Cc: linux-iio at vger.kernel.org
> Cc: linux-rockchip at lists.infradead.org
> ---
> 
> diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c
> index f9ad6c2..2f0e110 100644
> --- a/drivers/iio/adc/rockchip_saradc.c
> +++ b/drivers/iio/adc/rockchip_saradc.c
> @@ -218,6 +231,13 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
>  	if (IS_ERR(info->regs))
>  		return PTR_ERR(info->regs);
>  
> +	info->reset = devm_reset_control_get(&pdev->dev, "saradc-apb");
> +	if (IS_ERR(info->reset)) {
> +		ret = PTR_ERR(info->reset);
> +		dev_err(&pdev->dev, "failed to get saradc reset: %d\n", ret);
> +		return ret;
> +	}

Does this need to handle ENOENT so as to avoid failing with old device
tree blobs?


From thierry.reding at gmail.com  Tue Jul 26 02:02:26 2016
From: thierry.reding at gmail.com (Thierry Reding)
Date: Tue, 26 Jul 2016 11:02:26 +0200
Subject: [PATCH 2/2] dt-bindings: add simple-panel-dsi and simple-panel
In-Reply-To: <5796C47C.5040208@rock-chips.com>
References: <1468984730-23186-1-git-send-email-mark.yao@rock-chips.com>
 <1468984730-23186-2-git-send-email-mark.yao@rock-chips.com>
 <20160725152125.GS21170@ulmo.ba.sec>
 <5796C47C.5040208@rock-chips.com>
Message-ID: <20160726090226.GB2433@ulmo.ba.sec>

On Tue, Jul 26, 2016 at 10:01:32AM +0800, Mark yao wrote:
> On 2016?07?25? 23:21, Thierry Reding wrote:
> 
>     On Wed, Jul 20, 2016 at 11:18:50AM +0800, Mark Yao wrote:
> 
>         Allow user add display timing on device tree with simple-panel-dsi
>         or simple-panel.
> 
>         Cc: Thierry Reding <thierry.reding at gmail.com>
>         Cc: Rob Herring <robh+dt at kernel.org>
>         Cc: Mark Rutland <mark.rutland at arm.com>
> 
>         Signed-off-by: Mark Yao <mark.yao at rock-chips.com>
>         ---
>          .../bindings/display/panel/simple-panel.txt        | 48 ++++++++++++++++++++++
>          1 file changed, 48 insertions(+)
> 
>     Sorry, not going to happen. Read this for an explanation of why not:
> 
>             https://sietch-tagr.blogspot.de/2016/04/display-panels-are-not-special.html
> 
>     Thierry
> 
> 
> Hi Thierry
> 
> The blog actually not persuade me why can't use display timing on
> device tree.

Okay, perhaps read it again, it addresses most of your points below.

> 1, Binding panel as a simple string on device tree seems simple on device tree,
> but it's complex on kernel code, and kernel code would became bigger and
> bigger.

I don't think the video timings in the simple-panel driver are very
complex. They also don't use very much space. And if you're really
concerned about space you can always use conditional compilation and
Kconfig symbols to remove timings for panels that you don't use.

Also, panels are characterized by much more than just video timings.
There were attempts, way back, to fully describe panels in device tree
and that failed. What you propose here is a partial solution to a much
more complex problem.

This is all explained in the blog post.

> 2, Our customer always ask me, where is the display timing? They only find a
> simple panel string on device tree,? need search the kernel code to find the
> actually timing. They are used to find all device info on device tree, but
> panel timing info is not, this would confuse them. They don't want to know how
> code work, just want a easier interface.

That's not a very good argument. There's plenty of data that's not in
device tree for other devices, why should panels be different? Also, I
would hope that any customer of yours knows their way around kernel
code and can therefore easily add video timings for new panels. It's
quite trivial to do, and there are many examples on how to do it.

> 3, I think device tree not only can use for kernel, other module also can use
> it. on our project, we use uboot + kernel, the uboot support fdt, that function
> can parsing device tree. So if describe the display timing on device tree, both
> uboot and kernel can share same display timing, not need to describe twice, it
> would save work and not easy to make mistake.

That's a bit of a stretch. Video timings is fairly straightforward data
and can be easily added to any other piece of code that you want to run.
Yes you will have to duplicate the data, but how is that different from
duplicating all the driver code?

> 4, For differentiation product, we face many different panel, every once in a
> while, need to add a new panel, we can't convert all the panel , code the panel
> on kernel seems too bad, and the kernel image became bigger and bigger.

Why can't you convert all the panels? We already support a bunch of them
and haven't yet run into any problems. If you do encounter any issues
trying to port panels to the DRM panel infrastructure, please let me
know and I can help sort them out.

The kernel image size isn't a problem either. In any modern kernel the
video timing data in the panel driver is tiny compared to the rest.

> Generally, Our customer don't want to do any modify on kernel, they just modify
> device tree to bring up their device. Describe the panel timing on device tree,
> would make customer easy to use and reuse it.

Yes, that would perhaps make it easier for them to bring up the device.
But soon after they'll notice that there are glitches when turning the
panel on and off, and then they'll realize that they can't fix that
using their simple device tree.

All of that said, if you do think this is valuable to your customers,
please feel free to ship these patches in your downstream tree.

Also, though this is a bit off topic, I'm sure that your customers'
customers would be very happy to get all the security and bug fixes that
would automatically be delivered with the frequent kernel updates that
bring in support for new panels.

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160726/b075159b/attachment-0001.sig>

From heiko at sntech.de  Tue Jul 26 02:17:34 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Tue, 26 Jul 2016 11:17:34 +0200
Subject: [PATCH 1/4] iio: adc: rockchip_saradc: reset saradc controller
 before programming it
In-Reply-To: <1469513510-1516-1-git-send-email-wxt@rock-chips.com>
References: <1469513510-1516-1-git-send-email-wxt@rock-chips.com>
Message-ID: <12149198.2BnIdx96cc@diego>

Am Dienstag, 26. Juli 2016, 14:11:47 schrieb Caesar Wang:
> SARADC controller needs to be reset before programming it, otherwise
> it will not function properly.
> 
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
> Cc: Jonathan Cameron <jic23 at kernel.org>
> Cc: Heiko Stuebner <heiko at sntech.de>
> Cc: Rob Herring <robh+dt at kernel.org>
> Cc: linux-iio at vger.kernel.org
> Cc: linux-rockchip at lists.infradead.org
> ---
> 
>  .../bindings/iio/adc/rockchip-saradc.txt           |  5 +++++
>  drivers/iio/adc/Kconfig                            |  1 +
>  drivers/iio/adc/rockchip_saradc.c                  | 22
> ++++++++++++++++++++++ 3 files changed, 28 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt
> b/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt index
> bf99e2f..d2258be 100644
> --- a/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt
> +++ b/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt
> @@ -13,6 +13,9 @@ Required properties:
>  - clocks: Must contain an entry for each entry in clock-names.
>  - clock-names: Shall be "saradc" for the converter-clock, and "apb_pclk"
> for the peripheral clock.
> +- resets: Must contain an entry for each entry in reset-names.
> +	  See ../reset/reset.txt for details.
> +- reset-names: Must include the name "saradc-apb".

as pointed out by John, this should be an optional property, as it should work 
with old devicetrees as well.



From mark.yao at rock-chips.com  Tue Jul 26 02:51:46 2016
From: mark.yao at rock-chips.com (Mark yao)
Date: Tue, 26 Jul 2016 17:51:46 +0800
Subject: [PATCH 1/3] drm: introduce share plane
In-Reply-To: <20160726082635.GA31475@phenom.ffwll.local>
References: <1469519194-23133-1-git-send-email-mark.yao@rock-chips.com>
 <20160726082635.GA31475@phenom.ffwll.local>
Message-ID: <579732B2.80600@rock-chips.com>

On 2016?07?26? 16:26, Daniel Vetter wrote:
> On Tue, Jul 26, 2016 at 03:46:32PM +0800, Mark Yao wrote:
>> >What is share plane:
>> >Plane hardware only be used when the display scanout run into plane active
>> >scanout, that means we can reuse the plane hardware resources on plane
>> >non-active scanout.
>> >
>> >      --------------------------------------------------
>> >     |  scanout                                       |
>> >     |         ------------------                     |
>> >     |         | parent plane   |                     |
>> >     |         | active scanout |                     |
>> >     |         |                |   ----------------- |
>> >     |         ------------------   | share plane 1 | |
>> >     |  -----------------           |active scanout | |
>> >     |  | share plane 0 |           |               | |
>> >     |  |active scanout |           ----------------- |
>> >     |  |               |                             |
>> >     |  -----------------                             |
>> >     --------------------------------------------------
>> >One plane hardware can be reuse for multi-planes, we assume the first
>> >plane is parent plane, other planes share the resource with first one.
>> >     parent plane
>> >         |---share plane 0
>> >         |---share plane 1
>> >         ...
>> >
>> >Because resource share, There are some limit on share plane: one group
>> >of share planes need use same zpos, can not overlap, etc.
>> >
>> >We assume share plane is a universal plane with some limit flags.
>> >people who use the share plane need know the limit, should call the ioctl
>> >DRM_CLIENT_CAP_SHARE_PLANES, and judge the planes limit before use it.
>> >
>> >A group of share planes would has same shard id, so userspace can
>> >group them, judge share plane's limit.
>> >
>> >Signed-off-by: Mark Yao<mark.yao at rock-chips.com>
> This seems extremely hw specific, why exactly do we need to add a new
> relationship on planes? What does this buy on_other_  drivers?
Yes, now it's plane hardware specific, maybe others have same design, 
because this design
would save hardware resource to support multi-planes.

> Imo this should be solved by virtualizing planes in the driver. Start out
> by assigning planes, and if you can reuse one for sharing then do that,
> otherwise allocate a new one. If there's not enough real planes, fail the
> atomic_check.
I think that is too complex, trying with atomic_check I think it's not a 
good idea, userspace try planes every commit would be a heavy work.

Userspace need  know all planes relationship, group them, some display 
windows can put together, some can't,
too many permutation and combination, I think can't just commit with try.

example:
userspace:
windows 1: pos(0, 0)  size(1024, 100)
windows 2: pos(0, 50) size(400, 500)
windows 3: pos(0, 200) size(800, 300)

drm plane resources:
plane 0 and plane 1 is a group of share planes
plane 2 is common plane.

if userspace know the relationship, then they can assign windows 1 and 
window 3 to plane0 and plane 1. that would be success.
but if they don't know, assign window 1/2 to plane 0/1, failed, assign 
window 2/3 to plane 0/1, failed. mostly would get failed.

>
> This seems way to hw specific to be useful as a generic concept.

We want to change the drm_mode_getplane_res behavior, if userspace call 
DRM_CLIENT_CAP_SHARE_PLANES, that means userspace know hardware limit,
then we return full planes support to userspace, if don't, just make a 
group of share planes as one plane.
this work is on generic place.

> -Daniel
>
>

-- 
?ark Yao




From wxt at rock-chips.com  Tue Jul 26 03:49:56 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Tue, 26 Jul 2016 18:49:56 +0800
Subject: [PATCH 1/4] iio: adc: rockchip_saradc: reset saradc controller
 before programming it
In-Reply-To: <20160726100007.5166e7f9.john@metanate.com>
References: <1469513510-1516-1-git-send-email-wxt@rock-chips.com>
 <20160726100007.5166e7f9.john@metanate.com>
Message-ID: <57974054.8040700@rock-chips.com>


On 2016?07?26? 17:00, John Keeping wrote:
> On Tue, 26 Jul 2016 14:11:47 +0800, Caesar Wang wrote:
>
>> SARADC controller needs to be reset before programming it, otherwise
>> it will not function properly.
>>
>> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
>> Cc: Jonathan Cameron <jic23 at kernel.org>
>> Cc: Heiko Stuebner <heiko at sntech.de>
>> Cc: Rob Herring <robh+dt at kernel.org>
>> Cc: linux-iio at vger.kernel.org
>> Cc: linux-rockchip at lists.infradead.org
>> ---
>>
>> diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c
>> index f9ad6c2..2f0e110 100644
>> --- a/drivers/iio/adc/rockchip_saradc.c
>> +++ b/drivers/iio/adc/rockchip_saradc.c
>> @@ -218,6 +231,13 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
>>   	if (IS_ERR(info->regs))
>>   		return PTR_ERR(info->regs);
>>   
>> +	info->reset = devm_reset_control_get(&pdev->dev, "saradc-apb");
>> +	if (IS_ERR(info->reset)) {
>> +		ret = PTR_ERR(info->reset);
>> +		dev_err(&pdev->dev, "failed to get saradc reset: %d\n", ret);
>> +		return ret;
>> +	}
> Does this need to handle ENOENT so as to avoid failing with old device
> tree blobs?

I'm no sure why we have to support the old device tree,
We can apply this series patches if we need to support the reset property.
---

Maybe, I can follow you suggestion to handle the ENOENT, as following:

+ /*
+ * The reset should be an optional property, as it should work
+ * with old devicetrees as well
+ */
+ info->reset = devm_reset_control_get(&pdev->dev, "saradc-apb");
+ if (IS_ERR(info->reset)) {
+ if (PTR_ERR(info->reset) == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ return ret;
+ }
+ dev_dbg(&pdev->dev, "no reset control found\n");
+ info->reset = NULL;
+ }
...

+ if (info->reset)
+ rockchip_saradc_reset_controller(info->reset);
+

How about it?


-
Caesar
>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip


-- 
caesar wang | software engineer | wxt at rock-chip.com




From john at metanate.com  Tue Jul 26 04:16:40 2016
From: john at metanate.com (John Keeping)
Date: Tue, 26 Jul 2016 12:16:40 +0100
Subject: [PATCH 1/4] iio: adc: rockchip_saradc: reset saradc controller
 before programming it
In-Reply-To: <57974054.8040700@rock-chips.com>
References: <1469513510-1516-1-git-send-email-wxt@rock-chips.com>
 <20160726100007.5166e7f9.john@metanate.com>
 <57974054.8040700@rock-chips.com>
Message-ID: <20160726121640.2d5d9045.john@metanate.com>

On Tue, 26 Jul 2016 18:49:56 +0800, Caesar Wang wrote:

> 
> On 2016?07?26? 17:00, John Keeping wrote:
> > On Tue, 26 Jul 2016 14:11:47 +0800, Caesar Wang wrote:
> >
> >> SARADC controller needs to be reset before programming it, otherwise
> >> it will not function properly.
> >>
> >> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
> >> Cc: Jonathan Cameron <jic23 at kernel.org>
> >> Cc: Heiko Stuebner <heiko at sntech.de>
> >> Cc: Rob Herring <robh+dt at kernel.org>
> >> Cc: linux-iio at vger.kernel.org
> >> Cc: linux-rockchip at lists.infradead.org
> >> ---
> >>
> >> diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c
> >> index f9ad6c2..2f0e110 100644
> >> --- a/drivers/iio/adc/rockchip_saradc.c
> >> +++ b/drivers/iio/adc/rockchip_saradc.c
> >> @@ -218,6 +231,13 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
> >>   	if (IS_ERR(info->regs))
> >>   		return PTR_ERR(info->regs);
> >>   
> >> +	info->reset = devm_reset_control_get(&pdev->dev, "saradc-apb");
> >> +	if (IS_ERR(info->reset)) {
> >> +		ret = PTR_ERR(info->reset);
> >> +		dev_err(&pdev->dev, "failed to get saradc reset: %d\n", ret);
> >> +		return ret;
> >> +	}
> > Does this need to handle ENOENT so as to avoid failing with old device
> > tree blobs?
> 
> I'm no sure why we have to support the old device tree,
> We can apply this series patches if we need to support the reset property.
> ---
> 
> Maybe, I can follow you suggestion to handle the ENOENT, as following:
> 
> + /*
> + * The reset should be an optional property, as it should work
> + * with old devicetrees as well
> + */
> + info->reset = devm_reset_control_get(&pdev->dev, "saradc-apb");
> + if (IS_ERR(info->reset)) {
> + if (PTR_ERR(info->reset) == -EPROBE_DEFER) {
> + ret = -EPROBE_DEFER;
> + return ret;
> + }
> + dev_dbg(&pdev->dev, "no reset control found\n");
> + info->reset = NULL;
> + }
> ...
> 
> How about it?

I think it's better to handle ENOENT specifically, we still want to fail
if some other errors like EINVAL is returned.

Something like this, perhaps?

    if (IS_ERR(info->reset)) {
        ret = PTR_ERR(info->reset);
        if (ret != -ENOENT)
            return ret;

        dev_dbg(&pdev->dev, "no reset control found\n");
        info->reset = NULL;
    }

Although I do wonder if a devm_reset_control_get_optional() helper would
be useful (this isn't the first time I've seen reset control added to
existing drivers).


From wxt at rock-chips.com  Tue Jul 26 05:13:13 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Tue, 26 Jul 2016 20:13:13 +0800
Subject: [PATCH v2 2/4] arm64: dts: rockchip: add the saradc for rk3399
In-Reply-To: <1469535195-5227-1-git-send-email-wxt@rock-chips.com>
References: <1469535195-5227-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1469535195-5227-2-git-send-email-wxt@rock-chips.com>

This patch adds saradc needed information on rk3399 SoCs.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>
---

Changes in v2: None

 arch/arm64/boot/dts/rockchip/rk3399.dtsi | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index 4c84229..b81f84b 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -299,6 +299,18 @@
 		};
 	};
 
+	saradc: saradc at ff100000 {
+		compatible = "rockchip,rk3399-saradc";
+		reg = <0x0 0xff100000 0x0 0x100>;
+		interrupts = <GIC_SPI 62 IRQ_TYPE_LEVEL_HIGH>;
+		#io-channel-cells = <1>;
+		clocks = <&cru SCLK_SARADC>, <&cru PCLK_SARADC>;
+		clock-names = "saradc", "apb_pclk";
+		resets = <&cru SRST_P_SARADC>;
+		reset-names = "saradc-apb";
+		status = "disabled";
+	};
+
 	i2c1: i2c at ff110000 {
 		compatible = "rockchip,rk3399-i2c";
 		reg = <0x0 0xff110000 0x0 0x1000>;
-- 
1.9.1



From wxt at rock-chips.com  Tue Jul 26 05:13:12 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Tue, 26 Jul 2016 20:13:12 +0800
Subject: [PATCH v2 1/4] iio: adc: rockchip_saradc: reset saradc controller
 before programming it
Message-ID: <1469535195-5227-1-git-send-email-wxt@rock-chips.com>

SARADC controller needs to be reset before programming it, otherwise
it will not function properly.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>
Cc: Jonathan Cameron <jic23 at kernel.org>
Cc: Heiko Stuebner <heiko at sntech.de>
Cc: Rob Herring <robh+dt at kernel.org>
Cc: linux-iio at vger.kernel.org
Cc: linux-rockchip at lists.infradead.org

---

Changes in v2:
- Make the reset as an optional property, since it should work
with old devicetrees as well.

 .../bindings/iio/adc/rockchip-saradc.txt           |  7 +++++
 drivers/iio/adc/Kconfig                            |  1 +
 drivers/iio/adc/rockchip_saradc.c                  | 31 ++++++++++++++++++++++
 3 files changed, 39 insertions(+)

diff --git a/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt b/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt
index bf99e2f..205593f 100644
--- a/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt
+++ b/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt
@@ -16,6 +16,11 @@ Required properties:
 - vref-supply: The regulator supply ADC reference voltage.
 - #io-channel-cells: Should be 1, see ../iio-bindings.txt
 
+Optional properties:
+- resets: Must contain an entry for each entry in reset-names if need support
+	  this option. See ../reset/reset.txt for details.
+- reset-names: Must include the name "saradc-apb".
+
 Example:
 	saradc: saradc at 2006c000 {
 		compatible = "rockchip,saradc";
@@ -23,6 +28,8 @@ Example:
 		interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
 		clocks = <&cru SCLK_SARADC>, <&cru PCLK_SARADC>;
 		clock-names = "saradc", "apb_pclk";
+		resets = <&cru SRST_SARADC>;
+		reset-names = "saradc-apb";
 		#io-channel-cells = <1>;
 		vref-supply = <&vcc18>;
 	};
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 1de31bd..7675772 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -389,6 +389,7 @@ config QCOM_SPMI_VADC
 config ROCKCHIP_SARADC
 	tristate "Rockchip SARADC driver"
 	depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST)
+	depends on RESET_CONTROLLER
 	help
 	  Say yes here to build support for the SARADC found in SoCs from
 	  Rockchip.
diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c
index f9ad6c2..dc1379f 100644
--- a/drivers/iio/adc/rockchip_saradc.c
+++ b/drivers/iio/adc/rockchip_saradc.c
@@ -21,6 +21,8 @@
 #include <linux/of_device.h>
 #include <linux/clk.h>
 #include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/reset.h>
 #include <linux/regulator/consumer.h>
 #include <linux/iio/iio.h>
 
@@ -53,6 +55,7 @@ struct rockchip_saradc {
 	struct clk		*clk;
 	struct completion	completion;
 	struct regulator	*vref;
+	struct reset_control	*reset;
 	const struct rockchip_saradc_data *data;
 	u16			last_val;
 };
@@ -190,6 +193,16 @@ static const struct of_device_id rockchip_saradc_match[] = {
 };
 MODULE_DEVICE_TABLE(of, rockchip_saradc_match);
 
+/**
+ * Reset SARADC Controller.
+ */
+static void rockchip_saradc_reset_controller(struct reset_control *reset)
+{
+	reset_control_assert(reset);
+	usleep_range(10, 20);
+	reset_control_deassert(reset);
+}
+
 static int rockchip_saradc_probe(struct platform_device *pdev)
 {
 	struct rockchip_saradc *info = NULL;
@@ -218,6 +231,21 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
 	if (IS_ERR(info->regs))
 		return PTR_ERR(info->regs);
 
+	/*
+	 * The reset should be an optional property, as it should work
+	 * with old devicetrees as well
+	 */
+	info->reset = devm_reset_control_get_optional(&pdev->dev,
+			"saradc-apb");
+	if (IS_ERR(info->reset)) {
+		ret = PTR_ERR(info->reset);
+		if (ret != -ENOENT)
+			return ret;
+
+		dev_dbg(&pdev->dev, "no reset control found\n");
+		info->reset = NULL;
+	}
+
 	init_completion(&info->completion);
 
 	irq = platform_get_irq(pdev, 0);
@@ -252,6 +280,9 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
 		return PTR_ERR(info->vref);
 	}
 
+	if (info->reset)
+		rockchip_saradc_reset_controller(info->reset);
+
 	/*
 	 * Use a default value for the converter clock.
 	 * This may become user-configurable in the future.
-- 
1.9.1



From wxt at rock-chips.com  Tue Jul 26 05:13:14 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Tue, 26 Jul 2016 20:13:14 +0800
Subject: [PATCH v2 3/4] arm64: dts: rockchip: add reset saradc node for rk3368
 SoCs
In-Reply-To: <1469535195-5227-1-git-send-email-wxt@rock-chips.com>
References: <1469535195-5227-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1469535195-5227-3-git-send-email-wxt@rock-chips.com>

SARADC controller needs to be reset before programming it, otherwise
it will not function properly.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>
---

Changes in v2: None

 arch/arm64/boot/dts/rockchip/rk3368.dtsi | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3368.dtsi b/arch/arm64/boot/dts/rockchip/rk3368.dtsi
index d02a9003..4f44d11 100644
--- a/arch/arm64/boot/dts/rockchip/rk3368.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3368.dtsi
@@ -270,6 +270,8 @@
 		#io-channel-cells = <1>;
 		clocks = <&cru SCLK_SARADC>, <&cru PCLK_SARADC>;
 		clock-names = "saradc", "apb_pclk";
+		resets = <&cru SRST_SARADC>;
+		reset-names = "saradc-apb";
 		status = "disabled";
 	};
 
-- 
1.9.1



From wxt at rock-chips.com  Tue Jul 26 05:13:15 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Tue, 26 Jul 2016 20:13:15 +0800
Subject: [PATCH v2 4/4] arm: dts: rockchip: add reset node for the exist
 saradc SoCs
In-Reply-To: <1469535195-5227-1-git-send-email-wxt@rock-chips.com>
References: <1469535195-5227-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1469535195-5227-4-git-send-email-wxt@rock-chips.com>

SARADC controller needs to be reset before programming it, otherwise
it will not function properly.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>
---

Changes in v2: None

 arch/arm/boot/dts/rk3066a.dtsi | 2 ++
 arch/arm/boot/dts/rk3288.dtsi  | 2 ++
 arch/arm/boot/dts/rk3xxx.dtsi  | 2 ++
 3 files changed, 6 insertions(+)

diff --git a/arch/arm/boot/dts/rk3066a.dtsi b/arch/arm/boot/dts/rk3066a.dtsi
index c0ba86c..0d0dae3 100644
--- a/arch/arm/boot/dts/rk3066a.dtsi
+++ b/arch/arm/boot/dts/rk3066a.dtsi
@@ -197,6 +197,8 @@
 		clock-names = "saradc", "apb_pclk";
 		interrupts = <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
 		#io-channel-cells = <1>;
+		resets = <&cru SRST_SARADC>;
+		reset-names = "saradc-apb";
 		status = "disabled";
 	};
 
diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi
index cd33f01..91c4b3c 100644
--- a/arch/arm/boot/dts/rk3288.dtsi
+++ b/arch/arm/boot/dts/rk3288.dtsi
@@ -279,6 +279,8 @@
 		#io-channel-cells = <1>;
 		clocks = <&cru SCLK_SARADC>, <&cru PCLK_SARADC>;
 		clock-names = "saradc", "apb_pclk";
+		resets = <&cru SRST_SARADC>;
+		reset-names = "saradc-apb";
 		status = "disabled";
 	};
 
diff --git a/arch/arm/boot/dts/rk3xxx.dtsi b/arch/arm/boot/dts/rk3xxx.dtsi
index 99bbcc2..e2cd683 100644
--- a/arch/arm/boot/dts/rk3xxx.dtsi
+++ b/arch/arm/boot/dts/rk3xxx.dtsi
@@ -399,6 +399,8 @@
 		#io-channel-cells = <1>;
 		clocks = <&cru SCLK_SARADC>, <&cru PCLK_SARADC>;
 		clock-names = "saradc", "apb_pclk";
+		resets = <&cru SRST_SARADC>;
+		reset-names = "saradc-apb";
 		status = "disabled";
 	};
 
-- 
1.9.1



From linux at roeck-us.net  Tue Jul 26 06:39:32 2016
From: linux at roeck-us.net (Guenter Roeck)
Date: Tue, 26 Jul 2016 06:39:32 -0700
Subject: [PATCH v2 1/4] iio: adc: rockchip_saradc: reset saradc controller
 before programming it
In-Reply-To: <1469535195-5227-1-git-send-email-wxt@rock-chips.com>
References: <1469535195-5227-1-git-send-email-wxt@rock-chips.com>
Message-ID: <57976814.3040605@roeck-us.net>

On 07/26/2016 05:13 AM, Caesar Wang wrote:
> SARADC controller needs to be reset before programming it, otherwise
> it will not function properly.
>
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
> Cc: Jonathan Cameron <jic23 at kernel.org>
> Cc: Heiko Stuebner <heiko at sntech.de>
> Cc: Rob Herring <robh+dt at kernel.org>
> Cc: linux-iio at vger.kernel.org
> Cc: linux-rockchip at lists.infradead.org
>
> ---
>
> Changes in v2:
> - Make the reset as an optional property, since it should work
> with old devicetrees as well.
>
>   .../bindings/iio/adc/rockchip-saradc.txt           |  7 +++++
>   drivers/iio/adc/Kconfig                            |  1 +
>   drivers/iio/adc/rockchip_saradc.c                  | 31 ++++++++++++++++++++++
>   3 files changed, 39 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt b/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt
> index bf99e2f..205593f 100644
> --- a/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt
> +++ b/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt
> @@ -16,6 +16,11 @@ Required properties:
>   - vref-supply: The regulator supply ADC reference voltage.
>   - #io-channel-cells: Should be 1, see ../iio-bindings.txt
>
> +Optional properties:
> +- resets: Must contain an entry for each entry in reset-names if need support
> +	  this option. See ../reset/reset.txt for details.
> +- reset-names: Must include the name "saradc-apb".
> +
>   Example:
>   	saradc: saradc at 2006c000 {
>   		compatible = "rockchip,saradc";
> @@ -23,6 +28,8 @@ Example:
>   		interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
>   		clocks = <&cru SCLK_SARADC>, <&cru PCLK_SARADC>;
>   		clock-names = "saradc", "apb_pclk";
> +		resets = <&cru SRST_SARADC>;
> +		reset-names = "saradc-apb";
>   		#io-channel-cells = <1>;
>   		vref-supply = <&vcc18>;
>   	};
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 1de31bd..7675772 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -389,6 +389,7 @@ config QCOM_SPMI_VADC
>   config ROCKCHIP_SARADC
>   	tristate "Rockchip SARADC driver"
>   	depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST)
> +	depends on RESET_CONTROLLER
>   	help
>   	  Say yes here to build support for the SARADC found in SoCs from
>   	  Rockchip.
> diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c
> index f9ad6c2..dc1379f 100644
> --- a/drivers/iio/adc/rockchip_saradc.c
> +++ b/drivers/iio/adc/rockchip_saradc.c
> @@ -21,6 +21,8 @@
>   #include <linux/of_device.h>
>   #include <linux/clk.h>
>   #include <linux/completion.h>
> +#include <linux/delay.h>
> +#include <linux/reset.h>
>   #include <linux/regulator/consumer.h>
>   #include <linux/iio/iio.h>
>
> @@ -53,6 +55,7 @@ struct rockchip_saradc {
>   	struct clk		*clk;
>   	struct completion	completion;
>   	struct regulator	*vref;
> +	struct reset_control	*reset;
>   	const struct rockchip_saradc_data *data;
>   	u16			last_val;
>   };
> @@ -190,6 +193,16 @@ static const struct of_device_id rockchip_saradc_match[] = {
>   };
>   MODULE_DEVICE_TABLE(of, rockchip_saradc_match);
>
> +/**
> + * Reset SARADC Controller.
> + */
> +static void rockchip_saradc_reset_controller(struct reset_control *reset)
> +{
> +	reset_control_assert(reset);
> +	usleep_range(10, 20);
> +	reset_control_deassert(reset);
> +}
> +
>   static int rockchip_saradc_probe(struct platform_device *pdev)
>   {
>   	struct rockchip_saradc *info = NULL;
> @@ -218,6 +231,21 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
>   	if (IS_ERR(info->regs))
>   		return PTR_ERR(info->regs);
>
> +	/*
> +	 * The reset should be an optional property, as it should work
> +	 * with old devicetrees as well
> +	 */
> +	info->reset = devm_reset_control_get_optional(&pdev->dev,
> +			"saradc-apb");

Does anyone know what the _optional API is for ? It seems to be exactly the same
as devm_reset_control_get().

Guenter

> +	if (IS_ERR(info->reset)) {
> +		ret = PTR_ERR(info->reset);
> +		if (ret != -ENOENT)
> +			return ret;
> +
> +		dev_dbg(&pdev->dev, "no reset control found\n");
> +		info->reset = NULL;
> +	}
> +
>   	init_completion(&info->completion);
>
>   	irq = platform_get_irq(pdev, 0);
> @@ -252,6 +280,9 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
>   		return PTR_ERR(info->vref);
>   	}
>
> +	if (info->reset)
> +		rockchip_saradc_reset_controller(info->reset);
> +
>   	/*
>   	 * Use a default value for the converter clock.
>   	 * This may become user-configurable in the future.
>



From wxt at rock-chips.com  Tue Jul 26 07:47:16 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Tue, 26 Jul 2016 22:47:16 +0800
Subject: [PATCH v2 1/4] iio: adc: rockchip_saradc: reset saradc controller
 before programming it
In-Reply-To: <57976814.3040605@roeck-us.net>
References: <1469535195-5227-1-git-send-email-wxt@rock-chips.com>
 <57976814.3040605@roeck-us.net>
Message-ID: <579777F4.2090002@rock-chips.com>


On 2016?07?26? 21:39, Guenter Roeck wrote:
>
>> static int rockchip_saradc_probe(struct platform_device *pdev)
>> {
>> struct rockchip_saradc *info = NULL;
>> @@ -218,6 +231,21 @@ static int rockchip_saradc_probe(struct 
>> platform_device *pdev)
>> if (IS_ERR(info->regs))
>> return PTR_ERR(info->regs);
>>
>> + /*
>> + * The reset should be an optional property, as it should work
>> + * with old devicetrees as well
>> + */
>> + info->reset = devm_reset_control_get_optional(&pdev->dev,
>> + "saradc-apb");
>
> Does anyone know what the _optional API is for ? It seems to be 
> exactly the same
> as devm_reset_control_get().

?
As far as I see, the difference is WARN_ON(1)
when CONFIG_RESET_CONTROLLER is not defined.


The _optional functions were introduced by the following commit:

----------------->8-----------------
commit b424080a9e086e683ad5fdc624a7cf3c024e0c0f
Author: Philipp Zabel <p.zabel at xxxxxxxxxxxxxx>
Date: Fri Mar 7 15:18:47 2014 +0100

reset: Add optional resets and stubs

This patch adds device_reset_optional and (devm_)reset_control_get_optional
variants that drivers can use to indicate they can function without control
over the reset line. For those functions, stubs are added so the drivers 
can
be compiled with CONFIG_RESET_CONTROLLER disabled.
Also, device_reset is annotated with __must_check. Drivers
ignoring the return
value should use device_reset_optional instead.

Signed-off-by: Philipp Zabel <p.zabel at xxxxxxxxxxxxxx>
------------------8<-----------------------------
?

for more information on 
http://www.spinics.net/lists/kernel/msg2306677.html :-)

>
> Guenter
>
>> + if (IS_ERR(info->reset)) {
>> + ret = PTR_ERR(info->reset);
>> + if (ret != -ENOENT)
>> + return ret;
>> +
>> + dev_dbg(&pdev->dev, "no reset control found\n");
>> + info->reset = NULL;
>> + }
>> +
>> init_completion(&info->completion);
>>
>> irq = platform_get_irq(pdev, 0);
>> @@ -252,6 +280,9 @@ static int rockchip_saradc_probe(struct 
>> platform_device *pdev)
>> return PTR_ERR(info->vref);
>> }
>>
>> + if (info->reset)
>> + rockchip_saradc_reset_controller(info->reset);
>> +
>> /*
>> * Use a default value for the converter clock.
>> * This may become user-configurable in the future.
>>
>
>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip


-- 
caesar wang | software engineer | wxt at rock-chip.com




From ville.syrjala at linux.intel.com  Tue Jul 26 09:07:01 2016
From: ville.syrjala at linux.intel.com (ville.syrjala at linux.intel.com)
Date: Tue, 26 Jul 2016 19:07:01 +0300
Subject: [PATCH 6/9] drm/rockchip: Use drm_plane_state.{src,dst}
In-Reply-To: <1469549224-1860-1-git-send-email-ville.syrjala@linux.intel.com>
References: <1469549224-1860-1-git-send-email-ville.syrjala@linux.intel.com>
Message-ID: <1469549224-1860-7-git-send-email-ville.syrjala@linux.intel.com>

From: Ville Syrj?l? <ville.syrjala at linux.intel.com>

Replace the private drm_rects in vop_plane_state with
the ones now living in drm_plane_state.

Cc: Yao <mark.yao at rock-chips.com>
Cc: linux-rockchip at lists.infradead.org
Signed-off-by: Ville Syrj?l? <ville.syrjala at linux.intel.com>
---
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 91305eb7d312..c566c740ab49 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -87,8 +87,6 @@
 struct vop_plane_state {
 	struct drm_plane_state base;
 	int format;
-	struct drm_rect src;
-	struct drm_rect dest;
 	dma_addr_t yrgb_mst;
 	bool enable;
 };
@@ -595,8 +593,8 @@ static int vop_plane_atomic_check(struct drm_plane *plane,
 	const struct vop_win_data *win = vop_win->data;
 	bool visible;
 	int ret;
-	struct drm_rect *dest = &vop_plane_state->dest;
-	struct drm_rect *src = &vop_plane_state->src;
+	struct drm_rect *dest = &state->dst;
+	struct drm_rect *src = &state->src;
 	struct drm_rect clip;
 	int min_scale = win->phy->scl ? FRAC_16_16(1, 8) :
 					DRM_PLANE_HELPER_NO_SCALING;
@@ -694,8 +692,8 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
 	unsigned int actual_w, actual_h;
 	unsigned int dsp_stx, dsp_sty;
 	uint32_t act_info, dsp_info, dsp_st;
-	struct drm_rect *src = &vop_plane_state->src;
-	struct drm_rect *dest = &vop_plane_state->dest;
+	struct drm_rect *src = &state->src;
+	struct drm_rect *dest = &state->dst;
 	struct drm_gem_object *obj, *uv_obj;
 	struct rockchip_gem_object *rk_obj, *rk_uv_obj;
 	unsigned long offset;
-- 
2.7.4



From ville.syrjala at linux.intel.com  Tue Jul 26 09:07:02 2016
From: ville.syrjala at linux.intel.com (ville.syrjala at linux.intel.com)
Date: Tue, 26 Jul 2016 19:07:02 +0300
Subject: [PATCH 7/9] drm/rockchip: Use drm_plane_helper_check_state()
In-Reply-To: <1469549224-1860-1-git-send-email-ville.syrjala@linux.intel.com>
References: <1469549224-1860-1-git-send-email-ville.syrjala@linux.intel.com>
Message-ID: <1469549224-1860-8-git-send-email-ville.syrjala@linux.intel.com>

From: Ville Syrj?l? <ville.syrjala at linux.intel.com>

Replace the use of drm_plane_helper_check_update() with
drm_plane_helper_check_state() since we have a plane state.

Rockchip looks to handling plane clipping rather well already
(unlikje most arm drm drivers) so there are no function changes
here.

Cc: Yao <mark.yao at rock-chips.com>
Cc: linux-rockchip at lists.infradead.org
Signed-off-by: Ville Syrj?l? <ville.syrjala at linux.intel.com>
---
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 25 +++++--------------------
 1 file changed, 5 insertions(+), 20 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index c566c740ab49..31744fe99b38 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -591,10 +591,7 @@ static int vop_plane_atomic_check(struct drm_plane *plane,
 	struct vop_win *vop_win = to_vop_win(plane);
 	struct vop_plane_state *vop_plane_state = to_vop_plane_state(state);
 	const struct vop_win_data *win = vop_win->data;
-	bool visible;
 	int ret;
-	struct drm_rect *dest = &state->dst;
-	struct drm_rect *src = &state->src;
 	struct drm_rect clip;
 	int min_scale = win->phy->scl ? FRAC_16_16(1, 8) :
 					DRM_PLANE_HELPER_NO_SCALING;
@@ -608,30 +605,18 @@ static int vop_plane_atomic_check(struct drm_plane *plane,
 	if (WARN_ON(!crtc_state))
 		return -EINVAL;
 
-	src->x1 = state->src_x;
-	src->y1 = state->src_y;
-	src->x2 = state->src_x + state->src_w;
-	src->y2 = state->src_y + state->src_h;
-	dest->x1 = state->crtc_x;
-	dest->y1 = state->crtc_y;
-	dest->x2 = state->crtc_x + state->crtc_w;
-	dest->y2 = state->crtc_y + state->crtc_h;
-
 	clip.x1 = 0;
 	clip.y1 = 0;
 	clip.x2 = crtc_state->adjusted_mode.hdisplay;
 	clip.y2 = crtc_state->adjusted_mode.vdisplay;
 
-	ret = drm_plane_helper_check_update(plane, crtc, state->fb,
-					    src, dest, &clip,
-					    state->rotation,
-					    min_scale,
-					    max_scale,
-					    true, true, &visible);
+	ret = drm_plane_helper_check_state(state, &clip,
+					   min_scale, max_scale,
+					   true, true);
 	if (ret)
 		return ret;
 
-	if (!visible)
+	if (!state->visible)
 		goto out_disable;
 
 	vop_plane_state->format = vop_convert_format(fb->pixel_format);
@@ -642,7 +627,7 @@ static int vop_plane_atomic_check(struct drm_plane *plane,
 	 * Src.x1 can be odd when do clip, but yuv plane start point
 	 * need align with 2 pixel.
 	 */
-	if (is_yuv_support(fb->pixel_format) && ((src->x1 >> 16) % 2))
+	if (is_yuv_support(fb->pixel_format) && ((state->src.x1 >> 16) % 2))
 		return -EINVAL;
 
 	vop_plane_state->enable = true;
-- 
2.7.4



From linux at roeck-us.net  Tue Jul 26 10:00:24 2016
From: linux at roeck-us.net (Guenter Roeck)
Date: Tue, 26 Jul 2016 10:00:24 -0700
Subject: [PATCH v2 1/4] iio: adc: rockchip_saradc: reset saradc
 controller before programming it
In-Reply-To: <579777F4.2090002@rock-chips.com>
References: <1469535195-5227-1-git-send-email-wxt@rock-chips.com>
 <57976814.3040605@roeck-us.net> <579777F4.2090002@rock-chips.com>
Message-ID: <20160726170023.GA6590@roeck-us.net>

On Tue, Jul 26, 2016 at 10:47:16PM +0800, Caesar Wang wrote:
> 
> On 2016?07?26? 21:39, Guenter Roeck wrote:
> >
> >>static int rockchip_saradc_probe(struct platform_device *pdev)
> >>{
> >>struct rockchip_saradc *info = NULL;
> >>@@ -218,6 +231,21 @@ static int rockchip_saradc_probe(struct
> >>platform_device *pdev)
> >>if (IS_ERR(info->regs))
> >>return PTR_ERR(info->regs);
> >>
> >>+ /*
> >>+ * The reset should be an optional property, as it should work
> >>+ * with old devicetrees as well
> >>+ */
> >>+ info->reset = devm_reset_control_get_optional(&pdev->dev,
> >>+ "saradc-apb");
> >
> >Does anyone know what the _optional API is for ? It seems to be exactly
> >the same
> >as devm_reset_control_get().
> 
> ?
> As far as I see, the difference is WARN_ON(1)
> when CONFIG_RESET_CONTROLLER is not defined.
> 
> 
> The _optional functions were introduced by the following commit:
> 
> ----------------->8-----------------
> commit b424080a9e086e683ad5fdc624a7cf3c024e0c0f
> Author: Philipp Zabel <p.zabel at xxxxxxxxxxxxxx>
> Date: Fri Mar 7 15:18:47 2014 +0100
> 
> reset: Add optional resets and stubs
> 
> This patch adds device_reset_optional and (devm_)reset_control_get_optional
> variants that drivers can use to indicate they can function without control
> over the reset line. For those functions, stubs are added so the drivers can
> be compiled with CONFIG_RESET_CONTROLLER disabled.
> Also, device_reset is annotated with __must_check. Drivers
> ignoring the return
> value should use device_reset_optional instead.
> 

Is that really what we are looking for here ? CONFIG_RESET_CONTROLLER
is required for other functions of rk3399, isn't it ?

Guenter

> Signed-off-by: Philipp Zabel <p.zabel at xxxxxxxxxxxxxx>
> ------------------8<-----------------------------
> ?
> 
> for more information on http://www.spinics.net/lists/kernel/msg2306677.html
> :-)
> 
> >
> >Guenter
> >
> >>+ if (IS_ERR(info->reset)) {
> >>+ ret = PTR_ERR(info->reset);
> >>+ if (ret != -ENOENT)
> >>+ return ret;
> >>+
> >>+ dev_dbg(&pdev->dev, "no reset control found\n");
> >>+ info->reset = NULL;
> >>+ }
> >>+
> >>init_completion(&info->completion);
> >>
> >>irq = platform_get_irq(pdev, 0);
> >>@@ -252,6 +280,9 @@ static int rockchip_saradc_probe(struct
> >>platform_device *pdev)
> >>return PTR_ERR(info->vref);
> >>}
> >>
> >>+ if (info->reset)
> >>+ rockchip_saradc_reset_controller(info->reset);
> >>+
> >>/*
> >>* Use a default value for the converter clock.
> >>* This may become user-configurable in the future.
> >>
> >
> >
> >_______________________________________________
> >Linux-rockchip mailing list
> >Linux-rockchip at lists.infradead.org
> >http://lists.infradead.org/mailman/listinfo/linux-rockchip
> 
> 
> -- 
> caesar wang | software engineer | wxt at rock-chip.com
> 
> 


From mark.rutland at arm.com  Tue Jul 26 11:29:33 2016
From: mark.rutland at arm.com (Mark Rutland)
Date: Tue, 26 Jul 2016 19:29:33 +0100
Subject: [PATCH v3 1/7] firmware: rockchip: sip: Add rockchip SIP runtime
 service
In-Reply-To: <1469178440-4668-2-git-send-email-hl@rock-chips.com>
References: <1469178440-4668-1-git-send-email-hl@rock-chips.com>
 <1469178440-4668-2-git-send-email-hl@rock-chips.com>
Message-ID: <20160726182933.GA2288@leverpostej>

On Fri, Jul 22, 2016 at 05:07:14PM +0800, Lin Huang wrote:
> +config ROCKCHIP_SIP
> +	bool "Rockchip SIP interface"
> +	depends on ARM64 && ARM_PSCI_FW
> +	help
> +	  Say Y here if you want to enable SIP callbacks for Rockchip platforms
> +	  This option enables support for communicating with the ATF.

[...]

> +typedef unsigned long (psci_fn)(unsigned long, unsigned long,
> +				unsigned long, unsigned long);
> +asmlinkage psci_fn __invoke_psci_fn_smc;

Use the SMCCC wrappers (see include/linux/arm-smccc.h).

This is *not* PSCI, even if the same ATF FW provides both services.

NAK for using the PSCI functions.

Thanks,
Mark.


From linux at roeck-us.net  Tue Jul 26 15:41:15 2016
From: linux at roeck-us.net (Guenter Roeck)
Date: Tue, 26 Jul 2016 15:41:15 -0700
Subject: [PATCH v2 1/4] iio: adc: rockchip_saradc: reset saradc
 controller before programming it
In-Reply-To: <1469535195-5227-1-git-send-email-wxt@rock-chips.com>
References: <1469535195-5227-1-git-send-email-wxt@rock-chips.com>
Message-ID: <20160726224115.GA25307@roeck-us.net>

On Tue, Jul 26, 2016 at 08:13:12PM +0800, Caesar Wang wrote:
> SARADC controller needs to be reset before programming it, otherwise
> it will not function properly.
> 
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
> Cc: Jonathan Cameron <jic23 at kernel.org>
> Cc: Heiko Stuebner <heiko at sntech.de>
> Cc: Rob Herring <robh+dt at kernel.org>
> Cc: linux-iio at vger.kernel.org
> Cc: linux-rockchip at lists.infradead.org

Tested-by: Guenter Roeck <linux at roeck-us.net>

> 
> ---
> 
> Changes in v2:
> - Make the reset as an optional property, since it should work
> with old devicetrees as well.
> 
>  .../bindings/iio/adc/rockchip-saradc.txt           |  7 +++++
>  drivers/iio/adc/Kconfig                            |  1 +
>  drivers/iio/adc/rockchip_saradc.c                  | 31 ++++++++++++++++++++++
>  3 files changed, 39 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt b/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt
> index bf99e2f..205593f 100644
> --- a/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt
> +++ b/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt
> @@ -16,6 +16,11 @@ Required properties:
>  - vref-supply: The regulator supply ADC reference voltage.
>  - #io-channel-cells: Should be 1, see ../iio-bindings.txt
>  
> +Optional properties:
> +- resets: Must contain an entry for each entry in reset-names if need support
> +	  this option. See ../reset/reset.txt for details.
> +- reset-names: Must include the name "saradc-apb".
> +
>  Example:
>  	saradc: saradc at 2006c000 {
>  		compatible = "rockchip,saradc";
> @@ -23,6 +28,8 @@ Example:
>  		interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
>  		clocks = <&cru SCLK_SARADC>, <&cru PCLK_SARADC>;
>  		clock-names = "saradc", "apb_pclk";
> +		resets = <&cru SRST_SARADC>;
> +		reset-names = "saradc-apb";
>  		#io-channel-cells = <1>;
>  		vref-supply = <&vcc18>;
>  	};
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 1de31bd..7675772 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -389,6 +389,7 @@ config QCOM_SPMI_VADC
>  config ROCKCHIP_SARADC
>  	tristate "Rockchip SARADC driver"
>  	depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST)
> +	depends on RESET_CONTROLLER
>  	help
>  	  Say yes here to build support for the SARADC found in SoCs from
>  	  Rockchip.
> diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c
> index f9ad6c2..dc1379f 100644
> --- a/drivers/iio/adc/rockchip_saradc.c
> +++ b/drivers/iio/adc/rockchip_saradc.c
> @@ -21,6 +21,8 @@
>  #include <linux/of_device.h>
>  #include <linux/clk.h>
>  #include <linux/completion.h>
> +#include <linux/delay.h>
> +#include <linux/reset.h>
>  #include <linux/regulator/consumer.h>
>  #include <linux/iio/iio.h>
>  
> @@ -53,6 +55,7 @@ struct rockchip_saradc {
>  	struct clk		*clk;
>  	struct completion	completion;
>  	struct regulator	*vref;
> +	struct reset_control	*reset;
>  	const struct rockchip_saradc_data *data;
>  	u16			last_val;
>  };
> @@ -190,6 +193,16 @@ static const struct of_device_id rockchip_saradc_match[] = {
>  };
>  MODULE_DEVICE_TABLE(of, rockchip_saradc_match);
>  
> +/**
> + * Reset SARADC Controller.
> + */
> +static void rockchip_saradc_reset_controller(struct reset_control *reset)
> +{
> +	reset_control_assert(reset);
> +	usleep_range(10, 20);
> +	reset_control_deassert(reset);
> +}
> +
>  static int rockchip_saradc_probe(struct platform_device *pdev)
>  {
>  	struct rockchip_saradc *info = NULL;
> @@ -218,6 +231,21 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
>  	if (IS_ERR(info->regs))
>  		return PTR_ERR(info->regs);
>  
> +	/*
> +	 * The reset should be an optional property, as it should work
> +	 * with old devicetrees as well
> +	 */
> +	info->reset = devm_reset_control_get_optional(&pdev->dev,
> +			"saradc-apb");
> +	if (IS_ERR(info->reset)) {
> +		ret = PTR_ERR(info->reset);
> +		if (ret != -ENOENT)
> +			return ret;
> +
> +		dev_dbg(&pdev->dev, "no reset control found\n");
> +		info->reset = NULL;
> +	}
> +
>  	init_completion(&info->completion);
>  
>  	irq = platform_get_irq(pdev, 0);
> @@ -252,6 +280,9 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
>  		return PTR_ERR(info->vref);
>  	}
>  
> +	if (info->reset)
> +		rockchip_saradc_reset_controller(info->reset);
> +
>  	/*
>  	 * Use a default value for the converter clock.
>  	 * This may become user-configurable in the future.
> -- 
> 1.9.1
> 


From wxt at rock-chips.com  Tue Jul 26 17:42:10 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Wed, 27 Jul 2016 08:42:10 +0800
Subject: [PATCH v2 1/4] iio: adc: rockchip_saradc: reset saradc controller
 before programming it
In-Reply-To: <20160726170023.GA6590@roeck-us.net>
References: <1469535195-5227-1-git-send-email-wxt@rock-chips.com>
 <57976814.3040605@roeck-us.net> <579777F4.2090002@rock-chips.com>
 <20160726170023.GA6590@roeck-us.net>
Message-ID: <57980362.3000406@rock-chips.com>


On 2016?07?27? 01:00, Guenter Roeck wrote:
> On Tue, Jul 26, 2016 at 10:47:16PM +0800, Caesar Wang wrote:
>> On 2016?07?26? 21:39, Guenter Roeck wrote:
>>>> static int rockchip_saradc_probe(struct platform_device *pdev)
>>>> {
>>>> struct rockchip_saradc *info = NULL;
>>>> @@ -218,6 +231,21 @@ static int rockchip_saradc_probe(struct
>>>> platform_device *pdev)
>>>> if (IS_ERR(info->regs))
>>>> return PTR_ERR(info->regs);
>>>>
>>>> + /*
>>>> + * The reset should be an optional property, as it should work
>>>> + * with old devicetrees as well
>>>> + */
>>>> + info->reset = devm_reset_control_get_optional(&pdev->dev,
>>>> + "saradc-apb");
>>> Does anyone know what the _optional API is for ? It seems to be exactly
>>> the same
>>> as devm_reset_control_get().
>> ?
>> As far as I see, the difference is WARN_ON(1)
>> when CONFIG_RESET_CONTROLLER is not defined.
>>
>>
>> The _optional functions were introduced by the following commit:
>>
>> ----------------->8-----------------
>> commit b424080a9e086e683ad5fdc624a7cf3c024e0c0f
>> Author: Philipp Zabel <p.zabel at xxxxxxxxxxxxxx>
>> Date: Fri Mar 7 15:18:47 2014 +0100
>>
>> reset: Add optional resets and stubs
>>
>> This patch adds device_reset_optional and (devm_)reset_control_get_optional
>> variants that drivers can use to indicate they can function without control
>> over the reset line. For those functions, stubs are added so the drivers can
>> be compiled with CONFIG_RESET_CONTROLLER disabled.
>> Also, device_reset is annotated with __must_check. Drivers
>> ignoring the return
>> value should use device_reset_optional instead.
>>
> Is that really what we are looking for here ? CONFIG_RESET_CONTROLLER
> is required for other functions of rk3399, isn't it ?

Right, as the DRM and thermal are depend on the CONFIG_RESET_CONTROLLER 
.....


>
> Guenter
>
>> Signed-off-by: Philipp Zabel <p.zabel at xxxxxxxxxxxxxx>
>> ------------------8<-----------------------------
>> ?
>>
>> for more information on http://www.spinics.net/lists/kernel/msg2306677.html
>> :-)
>>
>>> Guenter
>>>
>>>> + if (IS_ERR(info->reset)) {
>>>> + ret = PTR_ERR(info->reset);
>>>> + if (ret != -ENOENT)
>>>> + return ret;
>>>> +
>>>> + dev_dbg(&pdev->dev, "no reset control found\n");
>>>> + info->reset = NULL;
>>>> + }
>>>> +
>>>> init_completion(&info->completion);
>>>>
>>>> irq = platform_get_irq(pdev, 0);
>>>> @@ -252,6 +280,9 @@ static int rockchip_saradc_probe(struct
>>>> platform_device *pdev)
>>>> return PTR_ERR(info->vref);
>>>> }
>>>>
>>>> + if (info->reset)
>>>> + rockchip_saradc_reset_controller(info->reset);
>>>> +
>>>> /*
>>>> * Use a default value for the converter clock.
>>>> * This may become user-configurable in the future.
>>>>
>>>
>>> _______________________________________________
>>> Linux-rockchip mailing list
>>> Linux-rockchip at lists.infradead.org
>>> http://lists.infradead.org/mailman/listinfo/linux-rockchip
>>
>> -- 
>> caesar wang | software engineer | wxt at rock-chip.com
>>
>>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip


-- 
caesar wang | software engineer | wxt at rock-chip.com




From mark.yao at rock-chips.com  Tue Jul 26 18:08:49 2016
From: mark.yao at rock-chips.com (Mark yao)
Date: Wed, 27 Jul 2016 09:08:49 +0800
Subject: [PATCH 6/9] drm/rockchip: Use drm_plane_state.{src,dst}
In-Reply-To: <1469549224-1860-7-git-send-email-ville.syrjala@linux.intel.com>
References: <1469549224-1860-1-git-send-email-ville.syrjala@linux.intel.com>
 <1469549224-1860-7-git-send-email-ville.syrjala@linux.intel.com>
Message-ID: <579809A1.1040002@rock-chips.com>

On 2016?07?27? 00:07, ville.syrjala at linux.intel.com wrote:
> From: Ville Syrj?l? <ville.syrjala at linux.intel.com>
>
> Replace the private drm_rects in vop_plane_state with
> the ones now living in drm_plane_state.
>
> Cc: Yao <mark.yao at rock-chips.com>
> Cc: linux-rockchip at lists.infradead.org
> Signed-off-by: Ville Syrj?l? <ville.syrjala at linux.intel.com>
Good cleanup,

Acked-by: Mark Yao <mark.yao at rock-chips.com>

> ---
>   drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 10 ++++------
>   1 file changed, 4 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> index 91305eb7d312..c566c740ab49 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> @@ -87,8 +87,6 @@
>   struct vop_plane_state {
>   	struct drm_plane_state base;
>   	int format;
> -	struct drm_rect src;
> -	struct drm_rect dest;
>   	dma_addr_t yrgb_mst;
>   	bool enable;
>   };
> @@ -595,8 +593,8 @@ static int vop_plane_atomic_check(struct drm_plane *plane,
>   	const struct vop_win_data *win = vop_win->data;
>   	bool visible;
>   	int ret;
> -	struct drm_rect *dest = &vop_plane_state->dest;
> -	struct drm_rect *src = &vop_plane_state->src;
> +	struct drm_rect *dest = &state->dst;
> +	struct drm_rect *src = &state->src;
>   	struct drm_rect clip;
>   	int min_scale = win->phy->scl ? FRAC_16_16(1, 8) :
>   					DRM_PLANE_HELPER_NO_SCALING;
> @@ -694,8 +692,8 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
>   	unsigned int actual_w, actual_h;
>   	unsigned int dsp_stx, dsp_sty;
>   	uint32_t act_info, dsp_info, dsp_st;
> -	struct drm_rect *src = &vop_plane_state->src;
> -	struct drm_rect *dest = &vop_plane_state->dest;
> +	struct drm_rect *src = &state->src;
> +	struct drm_rect *dest = &state->dst;
>   	struct drm_gem_object *obj, *uv_obj;
>   	struct rockchip_gem_object *rk_obj, *rk_uv_obj;
>   	unsigned long offset;


-- 
?ark Yao




From mark.yao at rock-chips.com  Tue Jul 26 18:09:18 2016
From: mark.yao at rock-chips.com (Mark yao)
Date: Wed, 27 Jul 2016 09:09:18 +0800
Subject: [PATCH 7/9] drm/rockchip: Use drm_plane_helper_check_state()
In-Reply-To: <1469549224-1860-8-git-send-email-ville.syrjala@linux.intel.com>
References: <1469549224-1860-1-git-send-email-ville.syrjala@linux.intel.com>
 <1469549224-1860-8-git-send-email-ville.syrjala@linux.intel.com>
Message-ID: <579809BE.8080808@rock-chips.com>

On 2016?07?27? 00:07, ville.syrjala at linux.intel.com wrote:
> From: Ville Syrj?l?<ville.syrjala at linux.intel.com>
>
> Replace the use of drm_plane_helper_check_update() with
> drm_plane_helper_check_state() since we have a plane state.
>
> Rockchip looks to handling plane clipping rather well already
> (unlikje most arm drm drivers) so there are no function changes
> here.
>
> Cc: Yao<mark.yao at rock-chips.com>
> Cc:linux-rockchip at lists.infradead.org
> Signed-off-by: Ville Syrj?l?<ville.syrjala at linux.intel.com>
Acked-by: Mark Yao <mark.yao at rock-chips.com>

Thanks.

-- 
?ark Yao




From linux at roeck-us.net  Tue Jul 26 19:00:18 2016
From: linux at roeck-us.net (Guenter Roeck)
Date: Tue, 26 Jul 2016 19:00:18 -0700
Subject: [PATCH v2 1/4] iio: adc: rockchip_saradc: reset saradc controller
 before programming it
In-Reply-To: <57980362.3000406@rock-chips.com>
References: <1469535195-5227-1-git-send-email-wxt@rock-chips.com>
 <57976814.3040605@roeck-us.net> <579777F4.2090002@rock-chips.com>
 <20160726170023.GA6590@roeck-us.net> <57980362.3000406@rock-chips.com>
Message-ID: <579815B2.5010205@roeck-us.net>

On 07/26/2016 05:42 PM, Caesar Wang wrote:
>
> On 2016?07?27? 01:00, Guenter Roeck wrote:
>> On Tue, Jul 26, 2016 at 10:47:16PM +0800, Caesar Wang wrote:
>>> On 2016?07?26? 21:39, Guenter Roeck wrote:
>>>>> static int rockchip_saradc_probe(struct platform_device *pdev)
>>>>> {
>>>>> struct rockchip_saradc *info = NULL;
>>>>> @@ -218,6 +231,21 @@ static int rockchip_saradc_probe(struct
>>>>> platform_device *pdev)
>>>>> if (IS_ERR(info->regs))
>>>>> return PTR_ERR(info->regs);
>>>>>
>>>>> + /*
>>>>> + * The reset should be an optional property, as it should work
>>>>> + * with old devicetrees as well
>>>>> + */
>>>>> + info->reset = devm_reset_control_get_optional(&pdev->dev,
>>>>> + "saradc-apb");
>>>> Does anyone know what the _optional API is for ? It seems to be exactly
>>>> the same
>>>> as devm_reset_control_get().
>>> ?
>>> As far as I see, the difference is WARN_ON(1)
>>> when CONFIG_RESET_CONTROLLER is not defined.
>>>
>>>
>>> The _optional functions were introduced by the following commit:
>>>
>>> ----------------->8-----------------
>>> commit b424080a9e086e683ad5fdc624a7cf3c024e0c0f
>>> Author: Philipp Zabel <p.zabel at xxxxxxxxxxxxxx>
>>> Date: Fri Mar 7 15:18:47 2014 +0100
>>>
>>> reset: Add optional resets and stubs
>>>
>>> This patch adds device_reset_optional and (devm_)reset_control_get_optional
>>> variants that drivers can use to indicate they can function without control
>>> over the reset line. For those functions, stubs are added so the drivers can
>>> be compiled with CONFIG_RESET_CONTROLLER disabled.
>>> Also, device_reset is annotated with __must_check. Drivers
>>> ignoring the return
>>> value should use device_reset_optional instead.
>>>
>> Is that really what we are looking for here ? CONFIG_RESET_CONTROLLER
>> is required for other functions of rk3399, isn't it ?
>
> Right, as the DRM and thermal are depend on the CONFIG_RESET_CONTROLLER .....
>

Since "optional" doesn't really mean "the reset property is optional"
but "CONFIG_RESET_CONTROLLER is optional", I would suggest to use
devm_reset_control_get().

Guenter



From wxt at rock-chips.com  Tue Jul 26 19:11:30 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Wed, 27 Jul 2016 10:11:30 +0800
Subject: [PATCH v2 1/4] iio: adc: rockchip_saradc: reset saradc controller
 before programming it
In-Reply-To: <579815B2.5010205@roeck-us.net>
References: <1469535195-5227-1-git-send-email-wxt@rock-chips.com>
 <57976814.3040605@roeck-us.net> <579777F4.2090002@rock-chips.com>
 <20160726170023.GA6590@roeck-us.net> <57980362.3000406@rock-chips.com>
 <579815B2.5010205@roeck-us.net>
Message-ID: <57981852.1040006@rock-chips.com>


On 2016?07?27? 10:00, Guenter Roeck wrote:
> On 07/26/2016 05:42 PM, Caesar Wang wrote:
>>
>> On 2016?07?27? 01:00, Guenter Roeck wrote:
>>> On Tue, Jul 26, 2016 at 10:47:16PM +0800, Caesar Wang wrote:
>>>> On 2016?07?26? 21:39, Guenter Roeck wrote:
>>>>>> static int rockchip_saradc_probe(struct platform_device *pdev)
>>>>>> {
>>>>>> struct rockchip_saradc *info = NULL;
>>>>>> @@ -218,6 +231,21 @@ static int rockchip_saradc_probe(struct
>>>>>> platform_device *pdev)
>>>>>> if (IS_ERR(info->regs))
>>>>>> return PTR_ERR(info->regs);
>>>>>>
>>>>>> + /*
>>>>>> + * The reset should be an optional property, as it should work
>>>>>> + * with old devicetrees as well
>>>>>> + */
>>>>>> + info->reset = devm_reset_control_get_optional(&pdev->dev,
>>>>>> + "saradc-apb");
>>>>> Does anyone know what the _optional API is for ? It seems to be 
>>>>> exactly
>>>>> the same
>>>>> as devm_reset_control_get().
>>>> ?
>>>> As far as I see, the difference is WARN_ON(1)
>>>> when  is not defined.
>>>>
>>>>
>>>> The _optional functions were introduced by the following commit:
>>>>
>>>> ----------------->8-----------------
>>>> commit b424080a9e086e683ad5fdc624a7cf3c024e0c0f
>>>> Author: Philipp Zabel <p.zabel at xxxxxxxxxxxxxx>
>>>> Date: Fri Mar 7 15:18:47 2014 +0100
>>>>
>>>> reset: Add optional resets and stubs
>>>>
>>>> This patch adds device_reset_optional and
>>>> variants that drivers can use to indicate they can function without 
>>>> control
>>>> over the reset line. For those functions, stubs are added so the 
>>>> drivers can
>>>> be compiled with CONFIG_RESET_CONTROLLER disabled.
>>>> Also, device_reset is annotated with __must_check. Drivers
>>>> ignoring the return
>>>> value should use device_reset_optional instead.
>>>>
>>> Is that really what we are looking for here ? CONFIG_RESET_CONTROLLER
>>> is required for other functions of rk3399, isn't it ?
>>
>> Right, as the DRM and thermal are depend on the 
>> CONFIG_RESET_CONTROLLER .....
>>
>
> Since "optional" doesn't really mean "the reset property is optional"
> but "CONFIG_RESET_CONTROLLER is optional", I would suggest to use
> devm_reset_control_get().

Agree, free riding.
Maybe the API (_optional) will be changed in later.
As someone make a point present an idea on 
http://www.spinics.net/lists/kernel/msg2306677.html

Okay,  devm_reset_control_get() will be better, anyway the driver has 
been depend on the CONFIG_RESET_CONTROLLER.
--

I will send a new patch for upstream if nobody object it on today.


Sorry for noisy!


>
> Guenter
>
>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip


-- 
caesar wang | software engineer | wxt at rock-chip.com




From mark.yao at rock-chips.com  Tue Jul 26 20:03:05 2016
From: mark.yao at rock-chips.com (Mark yao)
Date: Wed, 27 Jul 2016 11:03:05 +0800
Subject: [PATCH 2/2] dt-bindings: add simple-panel-dsi and simple-panel
In-Reply-To: <20160726090226.GB2433@ulmo.ba.sec>
References: <1468984730-23186-1-git-send-email-mark.yao@rock-chips.com>
 <1468984730-23186-2-git-send-email-mark.yao@rock-chips.com>
 <20160725152125.GS21170@ulmo.ba.sec> <5796C47C.5040208@rock-chips.com>
 <20160726090226.GB2433@ulmo.ba.sec>
Message-ID: <57982469.6030201@rock-chips.com>

On 2016?07?26? 17:02, Thierry Reding wrote:
> On Tue, Jul 26, 2016 at 10:01:32AM +0800, Mark yao wrote:
>> On 2016?07?25? 23:21, Thierry Reding wrote:
>>
>>      On Wed, Jul 20, 2016 at 11:18:50AM +0800, Mark Yao wrote:
>>
>>          Allow user add display timing on device tree with simple-panel-dsi
>>          or simple-panel.
>>
>>          Cc: Thierry Reding <thierry.reding at gmail.com>
>>          Cc: Rob Herring <robh+dt at kernel.org>
>>          Cc: Mark Rutland <mark.rutland at arm.com>
>>
>>          Signed-off-by: Mark Yao <mark.yao at rock-chips.com>
>>          ---
>>           .../bindings/display/panel/simple-panel.txt        | 48 ++++++++++++++++++++++
>>           1 file changed, 48 insertions(+)
>>
>>      Sorry, not going to happen. Read this for an explanation of why not:
>>
>>              https://sietch-tagr.blogspot.de/2016/04/display-panels-are-not-special.html
>>
>>      Thierry
>>
>>
>> Hi Thierry
>>
>> The blog actually not persuade me why can't use display timing on
>> device tree.
> Okay, perhaps read it again, it addresses most of your points below.
>
>> 1, Binding panel as a simple string on device tree seems simple on device tree,
>> but it's complex on kernel code, and kernel code would became bigger and
>> bigger.
> I don't think the video timings in the simple-panel driver are very
> complex. They also don't use very much space. And if you're really
> concerned about space you can always use conditional compilation and
> Kconfig symbols to remove timings for panels that you don't use.
>
> Also, panels are characterized by much more than just video timings.
> There were attempts, way back, to fully describe panels in device tree
> and that failed. What you propose here is a partial solution to a much
> more complex problem.
>
> This is all explained in the blog post.
>
>> 2, Our customer always ask me, where is the display timing? They only find a
>> simple panel string on device tree,  need search the kernel code to find the
>> actually timing. They are used to find all device info on device tree, but
>> panel timing info is not, this would confuse them. They don't want to know how
>> code work, just want a easier interface.
> That's not a very good argument. There's plenty of data that's not in
> device tree for other devices, why should panels be different? Also, I
> would hope that any customer of yours knows their way around kernel
> code and can therefore easily add video timings for new panels. It's
> quite trivial to do, and there are many examples on how to do it.
>
>> 3, I think device tree not only can use for kernel, other module also can use
>> it. on our project, we use uboot + kernel, the uboot support fdt, that function
>> can parsing device tree. So if describe the display timing on device tree, both
>> uboot and kernel can share same display timing, not need to describe twice, it
>> would save work and not easy to make mistake.
> That's a bit of a stretch. Video timings is fairly straightforward data
> and can be easily added to any other piece of code that you want to run.
> Yes you will have to duplicate the data, but how is that different from
> duplicating all the driver code?
>
>> 4, For differentiation product, we face many different panel, every once in a
>> while, need to add a new panel, we can't convert all the panel , code the panel
>> on kernel seems too bad, and the kernel image became bigger and bigger.
> Why can't you convert all the panels? We already support a bunch of them
> and haven't yet run into any problems. If you do encounter any issues
> trying to port panels to the DRM panel infrastructure, please let me
> know and I can help sort them out.
>
> The kernel image size isn't a problem either. In any modern kernel the
> video timing data in the panel driver is tiny compared to the rest.
>
>> Generally, Our customer don't want to do any modify on kernel, they just modify
>> device tree to bring up their device. Describe the panel timing on device tree,
>> would make customer easy to use and reuse it.
> Yes, that would perhaps make it easier for them to bring up the device.
> But soon after they'll notice that there are glitches when turning the
> panel on and off, and then they'll realize that they can't fix that
> using their simple device tree.
About the panel on and off, I don't think the panel-simple do the good 
enough.

panel-simple only have one gpio and one regulator, and their sequence is 
hard code, Why not a panel have two gpio or two regulator? On our 
project, we find many customer don't use the RC to do panel reset, they 
directly use gpio reset, so they need a gpio to do panel reset.

the device tree panel's on and off function is what the next step I want 
to upstream, on our downstream kernel, we do like that:

panel {
     power_ctrl {
           power0 {
                   gpios = <xxx>;
                   delay,ms = <3>;
           }
           power1 {
                   regulator = <xxx>;
                   delay,ms = <3>;
           }
           power2 {
                   backlight = <xxx>;
                   delay,ms = <3>;
           }
     }
}
and on driver, power on sequence with power0->power1->power2, power down 
with power2->power1->power0.
if user want to swap the power, can easy do that by  adjust dts power 
sequence.

this method can easy order the gpio, regulator, backlight sequence, 
judge the delay time and add new regulator or gpio.
I think this panel power on and off method is better than panel-simple 
driver currently using.

> All of that said, if you do think this is valuable to your customers,
> please feel free to ship these patches in your downstream tree.
>
> Also, though this is a bit off topic, I'm sure that your customers'
> customers would be very happy to get all the security and bug fixes that
> would automatically be delivered with the frequent kernel updates that
> bring in support for new panels.
>
> Thierry


-- 
?ark Yao




From dianders at chromium.org  Tue Jul 26 21:07:59 2016
From: dianders at chromium.org (Doug Anderson)
Date: Tue, 26 Jul 2016 21:07:59 -0700
Subject: [PATCH v2 2/4] arm64: dts: rockchip: add the saradc for rk3399
In-Reply-To: <1469535195-5227-2-git-send-email-wxt@rock-chips.com>
References: <1469535195-5227-1-git-send-email-wxt@rock-chips.com>
 <1469535195-5227-2-git-send-email-wxt@rock-chips.com>
Message-ID: <CAD=FV=W=rNP2h_8Z1TGK0A_KEhL19TKtWz-u0-_ZGH8jF5Zn9Q@mail.gmail.com>

Hi,


On Tue, Jul 26, 2016 at 5:13 AM, Caesar Wang <wxt at rock-chips.com> wrote:
> This patch adds saradc needed information on rk3399 SoCs.
>
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
> ---
>
> Changes in v2: None
>
>  arch/arm64/boot/dts/rockchip/rk3399.dtsi | 12 ++++++++++++
>  1 file changed, 12 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> index 4c84229..b81f84b 100644
> --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> @@ -299,6 +299,18 @@
>                 };
>         };
>
> +       saradc: saradc at ff100000 {
> +               compatible = "rockchip,rk3399-saradc";
> +               reg = <0x0 0xff100000 0x0 0x100>;
> +               interrupts = <GIC_SPI 62 IRQ_TYPE_LEVEL_HIGH>;
> +               #io-channel-cells = <1>;
> +               clocks = <&cru SCLK_SARADC>, <&cru PCLK_SARADC>;
> +               clock-names = "saradc", "apb_pclk";
> +               resets = <&cru SRST_P_SARADC>;
> +               reset-names = "saradc-apb";
> +               status = "disabled";
> +       };
> +

Seems reasonable to me.  I'll let Heiko comment if he cares about the
sort ordering.  I can never quite figure out what it should be so I've
sorta given up on it...  ;)

Reviewed-by: Douglas Anderson <dianders at chromium.org>

-Doug


From zyw at rock-chips.com  Wed Jul 27 04:24:54 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Wed, 27 Jul 2016 19:24:54 +0800
Subject: [PATCH] linux-firmware: rockchip: update dptx firmware to v2.6
Message-ID: <1469618694-10413-1-git-send-email-zyw@rock-chips.com>

Update firmware to v2.6, it support standby, controlled by mailbox
commend. And update the licence.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
---
 LICENCE.rockchip  |  41 +++++++++++++++++++++++++++++++++++++++++
 WHENCE            |  13 ++-----------
 rockchip/dptx.bin | Bin 98320 -> 98320 bytes
 3 files changed, 43 insertions(+), 11 deletions(-)
 create mode 100644 LICENCE.rockchip

diff --git a/LICENCE.rockchip b/LICENCE.rockchip
new file mode 100644
index 0000000..d23b4c4
--- /dev/null
+++ b/LICENCE.rockchip
@@ -0,0 +1,41 @@
+Copyright (c) 2016, Fuzhou Rockchip Electronics Co.Ltd
+All rights reserved.
+
+Redistribution.  Redistribution and use in binary form, without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions must reproduce the above copyright notice and the
+  following disclaimer in the documentation and/or other materials
+  provided with the distribution.
+
+* Neither the name of Fuzhou Rockchip Electronics Co.Ltd, its products
+  nor the names of its suppliers may be used to endorse or promote products
+  derived from this Software without specific prior written permission.
+
+* No reverse engineering, decompilation, or disassembly of this software
+  is permitted.
+
+Limited patent license. Fuzhou Rockchip Electronics Co.Ltd grants a world-wide,
+royalty-free, non-exclusive license under patents it now or hereafter
+owns or controls to make, have made, use, import, offer to sell and
+sell ("Utilize") this software, but solely to the extent that any
+such patent is necessary to Utilize the software alone, or in
+combination with an operating system licensed under an approved Open
+Source license as listed by the Open Source Initiative at
+http://opensource.org/licenses.  The patent license shall not apply to
+any other combinations which include this software.  No hardware per
+se is licensed hereunder.
+
+DISCLAIMER.  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
diff --git a/WHENCE b/WHENCE
index 307d239..b45e964 100644
--- a/WHENCE
+++ b/WHENCE
@@ -3306,15 +3306,6 @@ OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
 Driver: rk3399-dptx - ROCKCHIP rk3399 dptx firmware
 
 File: rockchip/dptx.bin
+Version: 2.6
 
-Licence: Redistributable.
-
-Rockchip Inc. grants permission to use and redistribute aforementioned firmware
-files for the use with devices containing Rockchip chipsets, but not as part of
-the Linux kernel or in any other form which would require these files themselves
-to be covered by the terms of the GNU General Public License or the GNU Lesser
-General Public License.
-
-These firmware files are distributed in the hope that they will be useful, but
-are provided WITHOUT ANY WARRANTY, INCLUDING BUT NOT LIMITED TO IMPLIED WARRANTY
-OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+Licence: Redistributable. See LICENCE.rockchip for details.
diff --git a/rockchip/dptx.bin b/rockchip/dptx.bin
index 042ec48d748a4a1e9b8174fa0d3fcc64bf2c562f..1f22d2c25d3fe1f5b242c73c363601f1ee0020f6 100644
GIT binary patch
literal 98320
zcmeFad0dlM+Aw_XI}0Q^Dw`>iP(w7hVGFo#QwS6ZXa!oSOKlYlBiKc)Gp$=2VkZUb
zOd)kDSUZ!%rUI4 at q>CW64QWe3Q6X)oV70}xu^@_JQ`X$?b?yYU?R?Mse9!OueSbZ)
zXS>e1mUEr!?B|Yt8VdO#@<$=0L5R;o$X_m(1EB3Ogd#}}`NIhP94Om_!UmbtL1xn+
zgE{-?lq^5KuP1-ZkI76yGPKO)lA)A2){j33+O{)xTg==)209 at JAh$la0r{!zEIwq!
z{6wHo=++0WNEr$+P=2aFlo;qIL$m??<%==8{~P|V2L4wA|37HpOO at ZZDw$Ii*{?!#
z5t<&mFg0;gK<!4<4Be9B(!|KFa%2nV#>|OK_)XXM2=}GH?^}V)DTwSBpm_+zG;=g_
z{3GR&va6&|-`;r{Nc{5R<KQO%`QQIFs8F-OuT>@MP(==^Vl=9h0 at X%?YFh;>&hN|r
z7SaI70$GP(TeBc?P!RK~Amy-N<8i^ZldL$uE&coSIhOxrmF$Kpa#R%)qe at 9pZQQ8Z
zwoRqcs0s>H27{{Nzmnyg!0)m^c0*7B{6_^b$%2&if{j}R+x7 at FuL%m?5*SVhDr)|p
zWs%tf_i9lp;xo+eW&7_yVo`$oe}KBehk1yQ52!WFh+UK#HNwc+Ig!zZg&fr;1LwcP
z`2U+SG5<sP$G9IFha%^eu=7 at o3~^a-9*=pb2wVv`1Gsm<Suu(XDo2q9U*@3-NRxso
z-tZlO6 at eqL4*`}E$UO83q<vvF^0gx50{sdg9EOn9wF at EUCKQG=a{=7z5Dx%174XRn
zN+wthLK6oO;=GK|1t`}R1F((YAZ$~DiwBp0P&Jg_)rU|Ok)<ArkWTh6IK)Q}DJP(x
z0saNZ@<}L){F%rJAy>dW<eCI9$kV)8gi5BONSOjfP6rnWZXviBa4$l+Mkr&2&@cg5
z13iO|LIsd60rw#|+HV|^gZl!?4L~|x#60u|O%uJ2vm5a>ZImnqp>gz7^dY|sZKorU
zmG4XOG%<7 at 3PypD7XwbCG4R46Yyuj4k%8w!2 at s83C=_5aP667e(a_`QDToGod!a0!
z3P2o<3(bZ6PlZ+#fiym9?hM333%Q5TOZ1AFC^Bv?ij0_rBA<vvk at Y~g1!ThzC;#<5
zh$2fKfI5S#ia?PcLb(!fJHbtXx@>_E$q_mPp$1$GI0e8XpnSa$>;Y&rfolQR2Cg04
zJ9ALv0MtDZoC}-*T)|=#nGNAa2(9BlXAs{44%s?I9|COe63{oe#~?lep%vVU2Xa3S
z170gI=wc~~%mY^dt_Yj~TnV@@Ag>8r8`SHucoew>+%|A8g42M at 1D68)8WJG<9HAf$
zAAK=_d8j=AYysexARYlQhH*19Ap9BNJP4P7bE#-F9YO at o2OI{+e8w;uu&bF6-w60I
z(?PZr&`|*&`GR}?5FfR@!AE}6z?NYgsuCge2Eas+H&M?=q2OfT@&XYGhH}%v#f;-1
z7r?CG+Q1Efiv)NyxOw0n2Im6T3$7oW6?iWNXMl3Sf9InL2oC{|!y8a!1<1tQ2F^Db
zX0jVWr$eY at 6LcqkfWHjz9~mZ+5Bl;WeUw7&5dI9#1@#Vo5=Ba%LXlI!%>}m<Tzm$K
zbb)Reuz4$lbJfsJ(5E+oyFL;65I7C=pNMP}nFuZg+!}Bj!PV!WNI&3(LYRkS;3C1f
zB+NtGLcvW2JP4f-PK7c-5Ke>81z`%5G030~L7WTWbbwnS{5z!UA^Zm1W+=0CJBl;|
zEC$@RFy^5b)8_{K6Z)hE;)eiM0HFcgJK(I~8o{-LivW7pA#_b+9+E=)jX)ehI2~Lh
zxVhjkxIV}ahHxsll$pR2oKX+<2jNk0DF8P>SOG2-;!O~K1FjWZ8 at QjrT?f|=ZUh{6
z7U&UL20 at Po>v$*z+%^(|UK7E60`a}zH1C7q{0r<1+z6zD0S^$6FND%}!PekU3h^K>
zd?D0F2Jv{9ho(cA2w^0IVK55#fc3(*g#1}ke3^&VfU~{_G=N_TgnK!lH;6w5WB>eI
zl>4%KnXAng<-X(zLtTIfn+g9t?x;QPNOm0(yW1_Eiki4+(=PY+UG5h?j?Uv3iC at do
zlv=0da0I@|c?)uU=q*SPtgZJoyc2uyny5_RD+m@$7cA7)`=t-LUzp$cZNFgCR`>R;
z?%b{J!l&KFr`=olf8Qb4l;z%@<<89_solJ9|16jvi=wjJS`NBC5T<rl(WY5Qd=VwT
zRZO=DQ59VxN)KU-8{JrRGQ`*Tlsk8W8>GilTO0~?5#a$pcb!{rK!<}x#>d^2Yuvf%
zqzJ^Tga`Ljrn|+nP at 4!!vt?r+btiITlm at E|E#i7%QIEJ6aw6pfHl3Eq`dFRJ--OEP
z(;VjrQ%;*l8OST8jt2%7#=A`hSCN3ETX8T1`3-l=t3RT0m$?DB$QL|S at V1%$Y4(`3
zFISZF)PsbEDHWOQF{U#s%>&+~+#VyhrF^nKJn+fzDKBTux$jcEaW+&}(GQHV$l1q~
z^RP(W#~7!%0p_6rBK3Krp)T=Get6J6?H6vbuOMsgcg9e_R(Ath#9K}mAu>ztVstkd
z at jJevL*foW*3=J-L4?CiHq+ciii#9njP5EUenpTKbjUc4WZz%~Fkgpk>3a2brtSyE
z$cGGbCo7Ty0y^S^`5L2ZVT_1iTw{xxJ0L?ktmt48?2K_}6r^eQuz-M;CYyg at _>GLQ
z4>Fur2 at Q!F0;r_oDpU6*W4u8yoTTV1=g%xf_0Np%GsgG}!T5<}tXD$-7)}S1P|YA{
zE8~^X8UBi&n5avmcp{$bQBxj=v_cDb#@5lb_3A4Oo`!XvyxM<{Ld4>atTlcll8Ol2
zA8jm?c?uM<$gPmr)nIJ!7IuEmq#0Z-Y}RGSQk^q?J_<k&3iw)ol#)jXYFUCk{>arA
z3&|cf>F`Hcf3q5^M$Ii=6ywK)vfdvViV2oJ6&WkMycFLuSXsrg`W~`ooHdq>0t}*H
zMc at Omja>xE!6xngsOf9M%FZTj{wN)f7~dXsHhWQWj}pQlk%l2yAd&G+FDvzV<}M?=
zuJo+&^-+L9sXMH;{f1TZuUTCmHflx{XUAA+ at 311;Eb<3tpQyqo$xIDy at sTh~!>IT*
zd6sGl<bj-4>xU$giW0c!%AW`()TQk*5fbWR%<&4T_<}KJk2=4&D;CrR2WuF at g48C%
zThLs4Z*6j*HcyNK3`+esYm+7Y)VOL?{pnp4;3T`lCvi-m8V0{`kBuouchxIu?jl*B
zfqrdVhGRr)9x3!-3zMwIIit>#%=U$Zqz7*IN885g4nWY4tdZ0Z^kbav<*fdIX=e at N
z2gt}iZ<LRkKe)>mD5s2B6Xa{5#a|(OwX9+E0N+(s9AFF at RaD$X`Y+geO#>eR8lX)6
zSoKdZ1FR4~;`bq^<AM>5Do)&`XaX_HNF&~<Z^0oVhMpAzq{<3ztY-;a!ZiS|ZVYu@
zxjh0gkmXMEC%nw$_7Wh-eaCS5yBX;7cw$uB2%dXS3$%@_t#_L4?d6vUss(K9*A4NX
zdwG+$u=?%a_40-h00JpbT|oH%doTZVL~+E6V*G?q*5BL9PkMQo-(*@?C0vFQ#>x at A
zqTw9C?q#*{Hv>v9(IOZ;_zvjhUBkwAM$B)Fu{!oUR!6<86b1LPIy{1(XgCM3ds%J!
z4Xf_ovC1ED9%N1nrAdHhVP$}76%_1*Bs6SzMo75+zT^7v=2r<*8;nU~#tWUhdp`k!
z%&snC(GIZaKY3+S>lm$VNZhhAT+fxN4(mhJD=Cu at rO!;T7_LBBV8y4A%|J$P8%&}G
zGGsuzI<<@)@b<+!Y`4UZB=^{^r%&)T8H|<LBZ^#R>F&xcBW4X_GJcEqC`j0XCFYkQ
zY{e1<2`@tWCB|g<7_V2n#3&LdEiirY5tHE~n2+{BMpfmS5%pe(RKs+$htc^W7D>Ga
zKp$16jhJ_nLKb6;f<&q-SB;o=0o^wB?y&|*CRMz*?ZY!9;rd`{@=5*Fh;gJo_h^aF
zMN20D>|P}SHE(5~XN-$RoI9DyMI+~gg|kPD^9gOx2l!fOhOyQQ&zKah7fV-ur)Tx-
zk1EC#uH%rhJz$A<hO?>OCQv-f7^jXnpS^1kg%d}9O;sAMpCnC!<^>wH!6Tr5YYpK5
ztpm*qkt+cscG7tX&<@S(=cTRQ!WhSoD6;R;&UFui6nA=bcm_)wTAt9Zq()32^w?DF
zq1`+ at Y`aHO#-U-t*&mrR7~{aOI`b~=?q7x-#K^ppqnCE#t1NBfwP8K!h(tuNSP$*m
zpNE0coyr)0_R at Af!N5q_IBec<m%1HB<U1Ao(pylyj=>ps$K)C3jh_tz40w1(3}CtK
zH)C=x>04U1?RsD^oNP5(h82(BMF9<9rEE+G#uH=JeH3P$yR-GKv&MIZ&5zzigPQ->
z%YrPa)%f>ebqcfnEke>GpBCgBvmO9~KzHZ6176OG6-*I3RFOqdc7ySiVSwDBV1qHS
zk!ajwjuvRY4 at nKdHb@z$VG@~jUKn;J-bMN^*m{+T-v;c~^TzD4>c=r9tPmG*DdZHN
zF=h;#<L*+lgBZV_KCNF6G44o}C21U+wmt=T$AwUr*3bqqXo`dmSlt^dvxe2nVC;Yn
zzZAOiw|J|Xge?{$axQ^LE8e4q(C~4 at EazgzNmF_lGj@H-jtkC3WL$s+ED5VBRm0|m
zWLSW~WFey)bSn@$s;&FLVt5r;f^Kw;EX!rXiuq)?S~_f=$MW2xAR!s9=CUj$3KEjx
zY7Suy!_^!{F^p_Nbm=|MXcf(JoIPxg@^Xw~_?LnBN0oDi)sZCAV&Xa<B$<_pVa0=t
z?pro@*0B14vE12g?)}5+*$n@@8}?S7y-h4K3J9#ycX+n2;*_6}H>{Y&7`elW2nLTF
z#~e(Dy6|fir(_iseMa|?dIn=09a7I^4A!|>2d~HgI5?!34t;5i-dGqrZustg3^bC+
z#q@%B3aCL*;BVv)SMrC|qs-9*z|?a5d-Di$@S at 4kWHIefKy)T4N4P`TSoctY++Q}u
zkR%66(*X#3TYzsG7%E8fPkGdkvl`$k-WXiE1WRSIo$US&HWd;qRg5wu6L9bnDQ5GW
z!?5ANOE6Zmks6GhLj*Dm5E7A!Fg6boNHWF>HnJWd0|c at aAZ%n06uwO$uL1fk5_t<C
zYy|i?*{$9ZDBRDoC;J(Y&C9<Rii2!e-OCui8p6c(7g6BlQLoMngs_T|)Id;8U`5p7
zvQD55$gt;wNDc5~w|T<=ArT_36Lx$aaoNav5SQKS5pmhb9)Q5M&m%6`=CR^-GPNZ`
zIJ6!Eqt*!U7Da%vJi7Ae5w?71k15Xa&IxgHISsqM*N&+hNoqZ$eqoZ2sn3$sR?s-?
z_&DY>B((=pPA2&sa|21eR!);mAJ<t=Qg1=(r#m{48For%!6pt^mwGgyeQa!BU#J}d
zuzr$no)QiMuuyS7!a^q;$GNc3OahRzaMztOqGGm`SpucZT(*>g1Z>+EJ~t%Ox(cQn
zfMRrjKr)AfX at c;;AP)fi>GH*Lxt)>I?obyb<#f=P-EeZB8j>kpns^pF{>~Z0x*?g|
z70Dhl08N4u;2Q~cN{$;qw%oM4SU6D0g>Iny!W}bZzn5-Q(p`LcfQZGxoZR at kSkZTQ
zSxlec at y&l2l9^mhWkd|eIstwV!k#29_-RR`9vwpPGS-P~wKBFv8*(7MYo_E42=wd7
z at r5?T_thE_v4!}4wdB^?w-=q>b;CGwsB-!c{+nZ^ta?=aD62AJ1BLQ?bVhT-AN33g
zgs4f$z1#0ogNei`tah|P0!a~J_ZZQ~mbnMAq7x9r(t-$AWsu;mPKE!eo;Wa<dl&9G
z&LOhoGJ|(o0CT33I^Q|#(V8?1s`%wJj2)q_+WtYA#f8UPYlxOQMBq8r6Ir6y`{E4{
z+LH-##`?Qj+6w7iOH9YwW6GM%FL5h>8EpP(5dXuY99Ai&b28`MaTEJ)|DH&*jjeG$
zA*W at l8~vCs{v)Jz#ItSk7-610*JGy_28sRU5uvoc+{Q%bLKGh2Q7AirLLD(T>dQgM
zSH=<a862W2Qui<`fyx8MbXQn3h-Z3_tE7pLmERCmGl&D^aSJ at W3hv4G9A`y+z~*~p
z$-5_ix(ELRn?KKkuem3Gp(p<sn?K)^|KdIQYuJ4A--y)^S&IifgEYj`jD2Bm4o1B>
zm`E?=q{M5Xq>3}0R)V7CBrzk<i*s-=&vrDpBtTF5>XT5Ott5b)2a2j~SlU2Fd_s5s
zpq5tLdQoCB7AP{gc!q^mlmtj>EJ_7zDNIV=VkoE>_Kqu{%c)_19>kogfN14Zn+Ta`
z-vv*Hh=snQ1LBs+`ZI3vjPZ+1(aMNu<!t9|z%mpBT^hva+b;V3B9!%<8xda+YtIdc
z*Z7Z>Qj>8!YX+?#rL6@)qSEvvp^5f0DZ{UZ&~UYT4o(&a0`i_4SmnU}2N=gH)E?0C
zh@?hxx%Ckf9WH4+KI=!_Ok^#Xnl)*@jrMz2I^{H7&Z!S*dAfduUgUqVQNIEe1>~tD
zWs<yn6_k+Fd^Ph&K>aEtXpu>M1Nb)XYEQP);nyy=CIfuHR1|Gstb-=;>w!?sXr&@A
zpsct!TI-9nN?#Qy1Wx!uT_ZC}l7X6 at jyU>>gZ3}zvR=E^k?cWGDt+y=zg-zYO83y=
zuyY(58idRUpr2<d3NYkZ^G)I+uBW(Ofb>dVejj6kw%C0?w1ql}Yo`_U0g}shrNV}h
zwIV2ey#UDSrXmMDThx{QfbW<JZGbu%_0SJwO<skAi=b&9C|Oi$(yG9AK*0ARv~)xd
z?cbiP0V%qOh!KlSEl_N>eH^lr8YNK3l#HC07(M-fs5rghY6&5-C at Iusp=W1F0;>XS
zqM$6Ph at glFN<stUYy};di3j{iH3yPG3xvM91%S@)!1MI8*lPngkStb2 at t#2%?5 at x`
zfPH67%2#StQ?)8ZRrI0;ttz6rv?QQ(kybT3zts8l9sKPVN!k1Im0Rx9E>~!mPhAtO
zT^_OMtF^}y15^vN%V*2sIx1p;W4f#$2nS?M@|Q1vz{K}0J+4haYmRTZPe&mO-*?^m
zwZ{)ml(VJf%Y7SsJOeGrf+yjK+|LJ^YX`bN8^GfotL8aY&Fz>_HLfJsT4NWnD~7Ux
zaSp at gHb(ozk_yHSoow(-%ec17T8Cj at +hvk&y@*#pP$l$ijH=0$QA~#P;&sH^okzy$
zyu9UGXmvHI8Sov`k9i8reD|b1aCjnhbO3X-^!y_FU`w=e{=s~+jafv)e4PmWZUGKD
zI8;SPD;G4rrl at 69G!8K*AQ->s%B%tLt`Jd!7)$UxI~*duEyvH{gUts9!WT)yRWCUj
zhQ*Ie&dMG?K*rJ1AHi$5pg0XeIZQ}P&npsT1^YxR=V2*aBV&x$XGvcq1<6ikZ9_>Y
z1U)kYF?Kcexm-J4ePKOHUz^geZPf3Vadbi&`uG#NE(Jj;`^><ZBTY?x_Er9**EL$L
zJNf-g9?P`_@(1f3^jxsyzu{=*+$us>wN+2gE2YzC+$O_O2&lzIPyP4_x^6YXt0141
zVbCLkiYRv<CB>?B7TULmp1LKv$h2|I&W$lUt;LxVrbjuoz`E0>3}4VW(N53CtF(q;
z&1-(HVVUT%t72zU)f)w_okal^&0MVjHTAyX(;j^Ei9A)v()#Sn)PY7lMv_MTN+h|w
z;O(A`O at FeU^@rmbyK(H0CMN*cr{x4(f2yrd2GO3JfI10MKk0j#w0wyJI&kiT1IHKA
z38jqUI4BpO%A5evKYg~YX{#m}mYjfu38<b`$0bOs;7qTDO<jgm={aZ*+SldW?@$uc
zQLU?nK8DBb9_^Wc+B^pmIRQ9M at wo44vd%4a at NbjVo<1i)yleEbVE5oTN(<u#Op7 at I
zlE&LdyLB$6iY5O&-UZ;|vx;LLu~@BEH=LdTml~jVGtE8CtNRJw7JyA+Q7vbnnI5=;
z2D;t1b4P9)Z{IE)x?Mb^&GGBW at pt3|*mDAHIYIKAiI$|zrZY>`-FN2KsIJ>Mcp+JL
z=aZGv^f!Dmd?3sNRo@=7{q4@%IJxLP0v1%{5w+wC at I(j_1u5|)sS>;!_(!b?rO_xW
zaJ=alGMEY2lpRoqk<Ey#Uy`IPJK)_pfszc-u`-hlE`?c$B<uK_-7~+oUuNv%7>(x`
zB0B)@huro_3&!6bAZ&Xc4D3-NiTu?GH(4>NVKqSNJt_6m+k0!)5K*@9DzeCCJ(~xf
zQZcE36>M({T#zK;H<jv1(MDKfL`KUZWqmRKwxV${GHa#J)BaG}p_pXw94DzKd5 at D6
zlPWxS3hGIo0^7z4K++D>J%X~iHpL{3=bV5|Mnmo#Qr&3vBuRYMp~AfmNc1Qs$;K$M
zvYRo|3I#5`KN7 at fWR)@*S#U$0!t}T0@{OK&>g%^-U%xF^Z%pG&Oyr7Ir=$dh2VkwC
zUGo>hARoG(-cVogcakV33v<Aa(%zP5Zt}$OD^V+M7rq1yELU;0DqdE=eHiv2>Zz3X
zxWxoBC|KoLvZ=tcoN}s#J&Cw%GmtyK2C#0{zM`JFJ!6VD4h7GuWmMr7psH2z*}8=N
z5ljVm+l>VS8BrOxdnSy(l6l+Z+T3=tIfIkp`r(b%IrSM@&LcKV+2S5slcD4!r}1d~
z*OZjGDe;+UajQ!*(m070A;%=7D?SWss_`?t6NXndmHEMu8D0XOg_C58TT_%_$n(<+
zI9OQlCRZP~_QecqP38_CS{oOX78jt5n`nviH^l{Rdx5(nZr!%5%#1kkd)(8)in9fh
zn#e|jX@$hJVv=b^h-pQzmNT)UV&|(%9D2^$qXIi;)xpy^1kW-DP}1yT=p9%KNr~B?
zdYeulPk)GRjSt=~oOxSXlrJ!!VLK4O7Aj*Ew~xlH$<P`!X*`58G^_otPNZ<!3}_Hc
z>qfn}QPSJkM3%LrjV9g3JG^nD<aXi2+c+^_p#E}<rcr#m(C;>$o-a^*F$Rmm_uva|
zV~ZCbx?BcLFZeru`J0 at vqXQ!ACUjd8oyMOSDckwD#b2f^*qCR?dr=}km<Oi-k4N9Q
zx9po~RBGQXnY<uHZb<vGZ{ZHPt*`OX$MyYVg at 7Jt!)H_Ht at l3H@>je%S7K69O8yF^
z66yIX56=rQDLHBUm1#<voLuZsQgs8&4kd>_%-}${bmpGp!3q*iIrvZ9N*ZUOsm47x
znoJwi!3e^_&R_A}TuJm8Ne6%BFY{n+Ge%PWYm%O}z(Fk;#&fqUus9mWt*GEOS%aGF
zLBJ-;cFV$<So-|Ovm`AUrq8E(OMJ*}`XZ?5-$8KdK(dd0yks5__!J0G_Hn$s1iG2h
zuOXvqEMp80P8|Txl<9}*qf5$Z!?l9QE<^iElCtpo6Agx)Cy!jmm)`px3k??wA}<?S
z_Cdz%>7I<ETki$i&y9-9eb}51P-?Qe6XEOfxF;H*rA-`v{HLrOIqc!#>iP^13*lZ^
zoWujmws9VVKCqSp1`kFa>XIf)Z+sVmPafgo6MPC~LAVt%fYYf0Nm*o#L7p5ePY#hM
zPm(7~9Gr>3 at R!AowZKHqc|szub1*nW-Nqa&!y$^x%)!NYYMm265hCgIe2&6#XWbL^
z?5#Vt7Pa*j?(y24cna^}=7SF(3 at 0rvew3TK`IeHJs^x8nrewF)%y0aTJT^GTb$&~Z
zw4kxERLfg|g|E(;Y*SJe-U<tt4sYe*xxqFi$HrS}1Ji+aCf0D81ICqbn*p0T$K?;Q
z{Ri&D({2r9C=D9 at 1g?Do>X|?{Rd5TmK~1&Xf&yrHZZYL*<L3LvFn)?#OjImu1N%Ro
z^}qnJfxAc_atm}pO<!^g4uLV;gXHWPGmG^D8ODXT^b>gY3DkeZ#(Wbjr9H#=z%BiF
zo_##!7*99-mU at iaFW<kdjqAMbHJ+nOpn--8A{oQL>tG?1(>xX;mA-f4j*a}4n9gjk
z>5%dsiz47<Vh<SX<~8S6NV*h at EOAfNQOM5O0Gp7w4aA5XU_>uwJ&SS33X$}|L5})M
zXv};L&ejvVVdsd!{yXg$b*mrK-Zl;E?Jw-@#}amXT|EhtMN;V`Ej`g>Xcy&aG(iUM
z<Zm`G&E5T#-Te+BRomS!>Z-?Wr^%FEjipJYU=W@@8ucU4mdEi4dBPVLrC#ihz1lB_
z*;p-6s_)l6OlCUI8YVCG>;Bju|6^X*xqdunksNat!Jbk5HwL;zArqaE5uK6g8Bf3L
zmuF^pc#P3AZvm#jb1Xd;#(vemkemJ!FTw{ePBYTEC;LTuEY9YNC*2qOWq+-$zqz*m
z%4hwBwfz<wytE0y6DqC!(QqU8!GvhrfOx;Owzj|dv;Hfm`@3uUrQa+Mcp}=?CVo^F
zZODy`wu%D<O2e7u0dU^iUDaPi)`WvIz+1bBs8R*qmC}AkHkq)S-|e?&he4$Fc)u-M
z4w1 at Z{g&)05ZRGE75t2BMW`$6cz+_HGJ`<Y&IPYD9z4fpLL$uAFW*c^^lT>h_RTEv
zW)^v~0wA`{_rur_b{J?x9qtFfqW?tZ|4IY47>T~QdHwA6NE*k!1uDKn36Jj}B3U-e
znl*%neREjt%OqmkEH~~W5!3Gk!-?9>V$jhGzt_=~=LrhvsChd}Ejz4s8%r%)Uiln}
z{7y#;iGqqr-Pe+n&C)pV?3qMCj!dE*WhT*%EtAzwCaaxHP_6ON{t{y0P$6MGOM;vM
zuU!h>AlW5ao|c6 at Mi|PU4!e>@B95o!&8Z|}f10KBG)wDgqG_CDdKxNJnAHCpQed<z
z!V)}WWZe&wNDt798>6TE5A-VkK!BE~?<Yn(AI{Dn3B%9>v@{R%vx3LB(BYvgGL*b*
z`Z8bVhf3t(i<e}D%qXINDdymrrShzh&?@@UYc`tOL1jt(7s<(f6q9pu(`Y*dE8O%P
zK?|J~O6#VeY<iKrh2B1;U;3z&i!b1Vc%n(3AuahXgyv5{;v>Ep85#DBOgyfY#(Rv_
zeXlN~>l7$AxWDP`f&;|{0jiTD{{4s_L-ChT{P`6B0jf?x>G%|X7$y9K_+O#A&k(-~
z@&AG9^oV~DCA at -kTahjk@gGMCNhl!}@gtB<hU$WmE)a#!q;=mjMRe9ATGt8(E!=Q8
zUiuC;)VgmOi-9>ALhCLwL?r0%8JYZxOgm}8h9B^32wKm>`39RN+vT-z4`wpKorSuJ
zDZJ6A&zQUj2Bso5zg4;gN|;OtZ_RH|f6NqK?SqXOYYhz-A!pH<!kRwMC at 3(|L553Q
z at wg;_cTo7PYC)Yp%45lp{c%TqcSYX-x#4d9wh#ZIV}gU8*fI{?AA`O$iW&jjFwR6z
z1kK|z^)!xjS22~q-dF>vb)^DarU>UOq~O6>3rq2`bQYjgpL1Syi-o;LTc1TfS#iz1
z79{twls*In3J>%R)&p6Gd}EP7)Fmo27=px~###*RaAQRX!2W2App?jP&ipWgB at yF$
zgp|ohix}5c*s*HNB}`!%(TuK&xmzPBaGIy8&w%`QnZjd4GgYi`-{V1`ReaV$PsH3$
zzAxv-JXRoEZLsiyZM+c0zEKlTqUTN0@`Brn{R{GBdR|CHnSWEUKU_^ia^@jPyjG<F
z<d~S9C+AvCMh;9Pg$MiiKi;8#4d|DiWm`@0HdCnU<KIyXCcE#3b0yKv0M0UQ!oho5
zt1zW+ at Jxns1GH3;z;K-_ek2mg;-d`$i&CRIhT2)_f54{!+Is#!OTYbpApIex at F}9b
zm7KbZcdAf!w+hYzrttB;x?9YK3Esx=hy|4}^EoSd3FnvrRy1O+5%)J-$WT53(m}(B
zMO at 7Njeh_5-;yx*j(#OI(2(i_%m$8eL7ypOGXEzAbNA63XejYE|Kx`Y%DJAFQ*Roz
z at FXVQBt5iT8EGvOv|NpUQ<*<(ydZgYwSKuq5-)inE8eC<2)0Aeb~xGm3R5U2_20mO
z=Z|Abeg^7dGWrlb2iWL9n=;p0;}7isTdQ2A at V-8N@0h7W!Rm9yE4_sQeI-N<xxBt&
zI8+wr`5FvdNS6ld>4|7k4C*(E$ToG**F^i9%STCfdS*03sm)OC&rrUSq5MmRQfGqQ
z{VeEuJ+xR$`)ie&0>&R6<v5hN at QkH!xEI7sVB9PpwuhhQ;<d$R(v*J2{$BN;nYVqQ
znY at jJlYto)R-emI`km&QcS0sSHceA%Oz)!8JoTTL?H#=dLw5j*oiKd#@_)IfE%CL}
zJQxZtIJYr{ZM_L?OvFr+Qj_~bFA&Zbfd2nO_fh?in)hjDCCAv%OPY5vW2onfm!1M1
z)q)O6*Y18bhdeH-7DO!JFJtV6`lgFyY-shKorr2}z0I|~S3c|QKHZyJ(`&5mEv)X<
z=lE%J{NbF;krQao39{u(l;?;oNn1>3Vif1ug&kSP`9^)%D>pFmxR<n<VK7MnEUL0s
zp1IN+f4}$6ni9{+Q{tJ0WxcVby*mW2WN@)5TedOJIxA0`J$a3CM|{{K?#9*Dw0M~Z
z7yCl#fA&Vb-HW4VaG<Oyd$Kl8rWd at Tk0Y$ub>u{>+H|-#cVe%yu7q8b6t|dY1eGhT
z2d}tg2mc!gchyJJus{9g=ubx*(&B at c3M*gh#n=|lZHTwUV at p<a=~Mu(g?)NjJU1;K
zL!$YW-YffidkmT<I9mR<S`cpPV0_0;`{qPlpXaPC_Jv+Nr(M~+vv+-0^fUK6o(Z)9
zXKRpc<-satMOCaNURI(s#fKFI6co(U$IJ5qG<mVw_$g}w(&DF*Tf2MKr1+^T8NE0z
z`q}$?o(;9rsHrK){>Z_4W$cr^*7{gmysS!Ti4Q9YFc{{U;^jpFcuql4tUi89UV!!v
zEj;ImaqY_3)ZXMS`{pFaW>tLvAi`m5iL#>1YAA^XS>S3ZPg#&>)yB)yXy3H>DPB47
z9J^s7oL|N&d$<0-#P+nqeT~}GMZJl<$atBGm-=9Dqf*Oz$!E*`Sphsf at 1^^)C!&Cu
zb++ggFISfJpyNjNWYe^DI%!7MW~Sb?lQEd09IWgf9eLoJP?W3}hs!@V6w#O~ZV;t?
z{*EYLoQKK#+l%Ai{cZYIp=ru|r<E386p+ZLv}H;Z#z&W&xi<5XeVk0vXnzD*<SIDd
zKDqQ7l(2E?aj5w-n%s({WzNTw#40|3PgBXYTbrFf(h#>$+^i5 at _!2rRS=CJS;-&aF
z{`8j<bb=hcdxGYtqRQhv<+S~5WRGZ~Rs^GttVbE97lrhQf_swW`lKmmYi5;w_ty0W
zdPioIBQqLEDlP*%SdB@{-_dxYSSWv+r_7uWLxl5>Zp)NN at kL2FPqEdVO)L8NV8W)%
zg>aG8%V*E4O;Z-Cf24EUdXATzD(69Iry6eS at jvn3@UC~)kGr}xxric{RB`8ZPUh+?
zI$;iacL}ZAjn45ZzwS{ggOy6WR;g5S+o`33V!3ua1%|Ha+usv+&M_Xf?r_94I+QX?
zW at H1M@GW!oHCne4oqV58_zT+0L<sXJX`kGevitfteEAk%j@}og`EqT(v}QGBjkjuw
zVYaYpy8Pj-914jkkjv_6bkm5qF<M_<TU#N@^Vh7FrCgRZ{ov>7ifn82gOhq-bc&JQ
zPgJTRtaN`tg-Jc^!mB7B6qc45Rd9$~@bDW-rL3-%xhjE!di3rrT361PtK2QrGNEZ4
zGR4vL6r3ztty67roGQv9qAEV%TH at gGh+aSezCMo1qqM$oO2{36Ej_0vj at J&`b*>|h
z-$D+5>~TJXfR8QCmtgZPaRT+}Q4@!DCX9L(&Wf8Ou;qpow7F{p&m<8AL>9A<8zUwT
z$H^Tv$sehE6&~@cS3sj&-9y*Kqjz7U`TLo6s%Jc<9S_%%GEJVp15OH*VH!r(GNGyE
zPj;oeYC=UtzePDEZvuXn3>C_$0~30b3Vmj at BQr{OlzbK-F at H#>ANdvw64Dub<2%q+
zGCEmK>xyBULv*C)T+%CL8!-P6!jI#GtI0&2 at 2;ooG8xO1c}0|=#+v?pq*lPS2{;x3
zt!eZ}HU17I7jD7eI3J7h<vjvq6Hu)?G%#yIvnY=~J+Pyl8XCUV=@`$0+uRnVq~^ON
zq}${wd$gI6=Xm at -GgsfDVflXYI9;b=DkGsM%=%t0dRd<&gL_{v`yORn(+)6t&V`K3
zsJ1dcQ``m6hVAT7`oRkWly(AgOrSs^nl71^oayjL{4&HjaOy&=g`q%8uFJC4@%$*p
zW;p)U3A!!{os6LQ4={QIb9feAHw)ly>sf31(`PLHoP)z9RM}aF5P=oLJ+l0(BR%Eg
z6g%AsVGO<m*!-8!$@gg8{Y(jYv!?i~n=fG;XGhcjQKx0KlxIzE54$4jS-<i(s_Ge2
zRleLJplkx9+TqBI1T}&JtCgUOIgI`6?etI^OW1kPUB_k8m(*4ccCS@}np=0&Q$6pZ
zK{;Fz+n|GGBqsDQ$>;2bcfXtUD6}Jg$X!HXkzIhGZ??*{0*+RR?E<h~x?zXrGAyBG
z#mXajN2`i<<WWc7I`A62p at 4hi0i}b+lA58eUzRwbVW5-$&<&lpZUW5p=BDqh>7msF
zKo%68FHci)9TPYXCDyWsT0oX4ST?9U`cnOl66(y^vJ)mD*R%rZ3CY7tI)GsF*={>&
zY2C2<Y7MPhilpPAi`JcYJ2h_7Pehg^#8%jRVANCbz|ndN3oV$oVUFUH5lDnsFxQ58
ziW-ubX~BHxX6jQUvDkurZCGHg9wE4tvuUL8$?n{WZnpF1FX~S1W;a!9C&MV%La8Qe
z#|e$4-5SkYSDydaF_dE*1(zYBuC}%yup%f}`={=X3HEVRJtm)@eq{7q3uC^~Z9ChS
zE-hijROh(sj=DEs7*mwWFc_ILGNUu%E;$6WLn*flICkX}i-2oUPOW>G={eh-{;zjD
zXsvGk>wsp<j3zj7mpu5tN8|5dVE%$TxmUWI_jg~(?e2cLJ9l5VaZh*Qp6>r at eYJ3G
zeHHaWH=<*rIcTXRGP2Sq6Hb$sn!IbD+!j~iwr<Q-syMT`dA at ps2I^hhg2{ozqI&%N
zK~uQWH$06eJ7!g=Xo);ad)=t+W*5FHvdUJ45N{^&a)`H^c#DabM7#sUJ2J*Ay&E3Z
zBy!X|y6}|CWYDmU!`?*BXdok_VuZp;&IN9~&I!+jbIdNdtAx5oC3cf at KlmTX<3M;a
z*L&viSfqFmYI#rE1K$J-ZhS(WMT-~nvCuOIoBv4TAU(wmo7)Xj{Tg3(suz$?ZI+0#
zR14sYJcgbvRA-LD75WO365(<HDQUhBYTRX`IayK~9{*vXo#tSvJr1&AhyWCLMYkF5
z;ep#z^m`X^h<C!@NIPJ*e;^o+!<W!il+B;xe3B3L>(9XvxLmOeP}G~)xm&S_CesL7
zCUg~WeV!}S(wy|F0pP{|nVd{6 at L(&}Lq)vUYB;}uPql>RdGs<NR^UaLpOJ+0v0Ock
zl&KtX2N%Ypm@=6u4)G~e&Q~PE8xIOE7f<3 at I4Gq?29p_N0D;+jJxz<Vc{UE()+n>F
zU}&*y6<Jci`lQ?(P1n^iEtF#%YP-U1JM at NL2^S>D=a~|Cdhjrl(7~`nuVz12L-I&H
zrafndN>AtO+F at X{2~p)kF6)<EE6Juot|HO$oVS3-S{47Q_bv-;``@nSZ(Ucuc6B$q
zaxb`yjjqCTuHumVnS+Zvl3Ld_<jmM at Ns34=a|*|78s7Guamkbgns at _QeZu;$7QRPR
z=tnnAb-`KeDE<K-!h5A9skJVAK^9#w*o<FKnPb4b#>G{fIs`kfwH)b at S8Fl`w|7*w
z7B(15YYR`hDqbr%l4tNm{6JLqHKqH4(!EdB9j5ADq3ZrjC2Xb=R#Uo%sDuY7{zNK4
zNY&j&y6;fk7f4ru_$5g9PsIN#;^!j%vnb(lq>E-xp~$mIc&7Q-C=My*82{;Vil7e9
zjDr`SZ;Xm(x at 9pt;Wg(D>a?&l7Ao)xoi2Rd!r at 73;5yL~$NLyoTwhX9VjCq*ZZ3q!
zc%wp$8>n*HX4vd}WwfXk_-#4}9Mpds6)zq%g at 1+jnCH{t0|OAX)cV<KgY30p=K|VJ
z1%<y&>l&%{SXiC`TX=<WetS80pmvaBKFur>mUGoPRAgJ9G#N-Y|JenMe9P&4%KXwO
z|1)5y&I5USWrm%KE!1mY6M1cKaLZ|{b+fh6V1AyC-Rjz5&{W0Z2MV{kjM*;j8{8hV
z%+;gSXf|u=4Q2l--E at Q*%yq2Vr(Lzzbdh5^^ClP|&}i9YZ4a|nY_6y=T>RdCnNF^;
zt$edS*Rk at j<!NgPS+`2_cH~S?i{lk>^EumK`hpe*d0qy2uBE-f)pGbLDVsH`tyd?~
zb;T5`QF8_J45`ruYGYepbE2z<!-r=rb7LNkYoJ_=A?8B^yu=<Ff||nf+HIpi;=n7~
zIJkdi`R%$0Op6MO&p^YyI4VB#lZWOSPb+a18>y05&-JpV#{X2A=~1qsVsm=u>T)iA
z%e<a?n+MNA>WSdH$T1VV5ALjPRPTiL`H`saF{(7yE~J{8xZ?6BrB8Oi%|aeqm>d9>
z(*u=*_2%uP;+#vs)x_a0opF2-&0hgVfWLRH2I4v{GJ%D_Dcw#JSjb{J;Tfu^lH4+T
zd3rw=1L0S}2)H&ajw at Z(3fW*^PmhYP{X4N4#4sm=7!D!&<rP@}QRF5scI2kkEYoPU
z2jT~~E*DR{>Kh=j1?vBG<-pA%u5sXIMX_Ho*BMH!6cP%{sngu%-kZO&N%Q(qX((X^
z%>}BJj5cT7lyY0hcRYYn`O{6CS!T6q*UmmuQzgPrRD8<aRzns(&>YS<>TT`-tBD__
z=K-PPLR|w>!~$A9dMwFra6UCEK6Qp|5cNY)h0{W>@_G#9e6*ZyzHk#>8Hp%(stEgs
zYmK%I$R>gX27(pV8R%Ah<^)=FB)zoLt1SzxXW?9<Q984f16JiX+C2XzEAQIbp{{n8
zJNb-Ibsp5WW>h?6GgjR+kE5OuvNAaHnGIlQ&iT}7eo9*(={glR$GVO)X%u(d(=b4$
zYI}8~@&23a42ow}hk%Y2)5h61)n_RF-{2{^GZpmx34Dpr?R)b|k+(sB(}Av*@#S20
z{i==vTQEOJ;TiD4)A;61Cl`bPThC=BQ9R(0r1^Cs#@J9{jvU2a8*nC%ipxIsC~y{Q
z(O?B7e7ILkA5Shkc3tc-oPcof3~(W=aE<x_Fc9P3n+hvTzrSjbEzlqdK(EsH=bP#g
zv`=V!?xu4Xd6lDh(W9KH6sV^8nVUH$iAd#i0|m_hs&XzI#qW_z#>D1L>u;XMFl0He
zQQ|Z2-8*(Eu2V2}WxXw82QKn<Y^I6>1=Ke{7`VdSzVc={o%_g5o9WHU<eQ!$=S<D1
z&kn#~leTIfyenC?*BngO=T;eEJSo~<LYh>WGd<5ICQp{{Q(Vc9WV}`f8PHju?6_cF
z1pV(FR!53S-mzI7K!XK{X>mw+o-rSUW(l!F1y+%vLOp*}9AY%iy=m^eqkt_Q1q6B9
zWcIynqL?v?pCMi4SKQ&C&m1)>K5!H|kVgQ25C9Crmo`qmskjQHp<Oaqy6OpctuFYk
z)%lOYX(Z6CVvYBEs1 at AfoCV<{M#aLzEK#S3C90-qqwuD=jU}qy;4vVfS6J at 7!m43T
zUHMyKRafx+a4s1>y5)*^ccDjGKJ-Z~eDKr4*9)oiU3`m>PM7erL7dc|yWk<?C0Lnv
zu`54lDjb|Y17?WaYh6SA?GrRzejO89SfR;tI2bQ*vJ}Hdm^2Iw{J%4mj;`aP-DHqe
z^Qbzr8)ECtVEjBn-MGF>@g}j>6J`#8;6;Muj4h69{CAhrPVDl0m)Z{1G=A0PY(}KZ
znuk$yeHR|T?&C3|Jay7zmg)(k(uesh{aI!=TFY8n9avjh9AB!|z3P_o_#e2Xdx_<O
zoVzaC44**Qa0UjMa=N0DtG-QndK;<2Eb|b8)A=;UIJZmshYpVOHJ~(5I0(q)+#<HN
zty=cw6h52ZV7?9fNDiK*fT%HHGkm`fZ)eS2E6x~9TAWx`43o0JjA^5>3v_D}Qq$hz
z9+a{VN<HUb<e|i67<|<f%01k*qM<;qJ(8D^2p>gw@(cAIS|?s9n-L9rxHKPyotLH*
zmMXC-;%qgoSBh{*`12v*`{}G>((vc~v-Sf_6>&Z57;RFj8pyJBwq2=87-TBzZ>+%V
zIa~uVv(mFf4J4*I%YFk;tJnvXoqq*MzumsME5j_?n4twT+5P9}`{;6R${auSPbm9J
z+9rR~Ccj19Foro$Y3vF<EIub(N$;|U`A7Y+YsHr-Y1)W}b#wAKsqfv;){h)sNW<gr
z>e|x6!cAS8Jnxci9!gLk-BhG|m#X_4#n(}~on*<zm!X8oNOwQ1$S8g&(j`!Jv#Gie
zRChmBH=fc3qq_T00-_ScNOu#}1tI=;#BWD+&4_;%@joY*kqM!Qe*z`I%1(mncCw2*
zf0Q6ZI=HQbrQ&Lokb-n^NVf#l^PuM0WXg|#2_NR at 2!WyXjaK+F;Zi_3*T&&hb-UPI
zKCJDW- at v-iWVl(>-M&Cu%Cz`IY^~;KF8fKk;Fk`HyE_)N`plk?06$YfD=J4|l5mZP
zbB1$YzrmhQ7u6OA53tP0U5_cu56=P`_^<Wd5#u{9=;2_>2aj*MB<on-vA_|>gRe*j
zeH at 0awsU?B-LTD;l%1xX?UXUF3&Z0ybt&l<{$0)zwp%F9ATo(z=oylsbsu9P^odfk
z{A&a;@Dj+w`^?`n{4+$fUq-~jo|ae}M+Lir?+(&+XQ_zu9h^EFWU5<7E)ALA<DC|k
z#5s$o+^ictfj+RsJl3u`U at OS8z4{QH at DQ!%h>AINACvNq5%J<1dX5m%7LvXK>Fat9
z9LmDDx;kqYts9`S^65GsI;)tjn?Prk(slRImWG?@C(@0M8_uI1`9DP|6 at AS=-1yb9
zL~S48r$Q~N3hRB&erK(R37bwYdo<#&tsLE9U}nAuHQCbc)7BA`etu<pt#q6gv???u
za_x!8k;toGV72z!5~->M<WN45E?S^Z<d_oq13oY*4{&=DeVi`T7@{L)jV0<Dgah!Z
zK at K10<nciLn-TGWA598an>+VX;<EKnyRS#YLzw`&vqm at XqqJ0zw*FT7zy>&%7ohM-
zG{2WJzIQ|Mx at XX@KpUZECEl8mX;cCyR-2uc5#u{I)Cp8X#tg7E-4 at F9z*maZRC}R#
z-D=x-ZU%c^*fXAEA5X)H9J=fe$2AACGE(u-<%^BeuHLucYEa|T#zQxXmLH!av~akh
zn)LM#iz6O}s=Q@>pHyW<11I4TP*Xc=bU!R5_v83%>-pNe8xhN#rx;xVgO`InoEj1D
zegx3=s866>@PLpX3rNl{M#N<o@>tXs_Ur9(da&NPYow&Ms2eT>i at S|4+;D1OkOm6<
zZZf|)jY!(Rt)h=C-nZSjXC34GXk;yiKa25F12LpGBHSJI(2R4K5|#y7yGOK=30ZM^
zL>y92x<`%I>eVAKL#Y3a#HWaq5~smiIsSdj>9NX1crJEJ3eCNgXej;lSw$>avI32$
z&u@^H!m|jvPRLZTU^3W6l#$t0{V~{UE#dZ-mz()I;dTMIB@=FnBVKNB1SWm_F2A>d
zMC<vILfhv->Yu2~K5R9xAoso-89CDjeD#lVH^SMPFaL&n-*{+Q*x#+8#6vUdS^k%n
zE&=rFM%d(eEXg$OE9V-rVzQsRm40K|*rMql%V7&t!b^W at 1r*P|xO5E!c#q8(k$(n{
zf`$Hr(M3Z4$T%Nt`Ho{b`=<Iws;CzG4qU-6<uH!ttlCxk^sDx^5T4QqqwBiGps_qN
zy-3)=sel#~Ce*pTYKnLfij)+drr{~XaVq^l5L7)`eSlOwsgzUqi5r+58xbFQ-`IQI
zyon4wJ=YbR*siPAqUNsacs$0V!gU|ROj^<BHF5JRBT{Jtr|vy>J6r1?A~pb*R#=id
z|1l!ISW2{>Jl4jaQnoWU#d{~0bIm7__<)4y9!P@#uz=AW1px+FB4q50Aor at J&?V=?
z*G!2sp!Xbdd(9|<ytKl5_Khz-16o(dQsQ;TK)HAOD?B`eb?Fkh&0lnMDUo13NN}2#
zl-0+YKc`^nUE5NN|Ixv*$MFGAt)%$-+}lmpPt#+X3T8Ex-_UcB?wJtea&dd&cxFAt
zDj6S)5P^bAd_civvQi)Zv+g`rYO+a;cpY{)9DdgQFt*^=Y=(O2O0|}OAArCD>AF%5
zSPZ5UGTgR6uH~3pa>@QE_p2H at Asb28@>3X0OS?Kax^-^jTi4AhvNSq!U7;ckQGDH;
z$ToyC8#Nz-hPb<4KlKqb1{rJijYM2(;OLUvQrd{FD;Sj5J+Q#>E^6_S!cGTnYb1;e
zmet*ZFoE%7-I#`|o1>|^KR{Hs%)S28SwA!^?+Nn*!F}NdMSgywggHk$YJ(mBK+f-|
z^mXu10k2p85$UdsH3X?}Hhjpw<GT3|9`T-Hhxlhf!m*_rI}j=|Mx?>;G?7l2;=Yn}
zz2(Zr593YWk%NYcvxbIB_|(g2S-BVg!Lf25On?eri*cZGV|#4!h22cgkF;ae;fz&p
z+Mcmi5j|r%A}M#lbv-;0-3pzslvBX=LHOk-x=uvuA_O~9yf7kOCuaxig8kg|7ySy}
z;iiZ9`DAaiz%<!ti^G;jebh^NJK{v8aS_WR-Y3Q&Ob7w$F!HOzDE{E6aoP2VoA9ji
z8cOIN1-gBspn$FF$Kl5fU^EZVxya!}R`4XRRT5>uZUe(8R0qFwK<nB^aW#L0VT~sH
z@=_ at jFdpe_I4A9_<|v%NiQhJAR9tt?BrQDkx_YKZg~{wJKbcjbx36`;bb`k_SB at B^
z*UeV|Q`!hE-85>PbX_sc(?Ju7;>i*K)&}s13kHnlfa}gAvNAX!T_OFe0?uvey3a`!
zUQ;BqQ$T{1q-H6SNC$kRg9GQ;UPE5`9LNfJO9t!Ji6mDAb^37B=<Za=JnUlGb{^{d
z)l_a?J(BPw1E?^Ht%LyYX+2{*)(#?!D$)ph_`Qp+PN&2}VwqRs=4+i`E8{yrWISG-
zI-*<27_WAkCwa87c&yYF4=?uMOR;K%AI}&scPj3KQpQW2W-;`ZwYkRcI~8K0&5NDp
zi5~p<sQDYfTvs<LtjaB?ch{V$!I&MsKw0Lb5q>lgY#*x$^CD7LGNZmkCdagq#*n%{
zj~Y*QI{jE;is(B^28lgN{#U0WVI*Qu2ip0xQ$3!DbGp+x4un{{-B{D97{_9&A3)6?
zgBVrYp>@V~{y8;l-&IFKBS|G7PQ<>5znPbTJa!YR_i>am-pf;;y49oXJ$mX(osUOL
zrhOEZ`Z7dTO#5hh>akAaI}m?z+Po<%VELcA4-%o%=5eqV5-)TX=0JM!w0V5;;Cey8
zMDmC`_1Vs-9RR*L?IUUIw$A1H4x9#1b!YB&fbyr!YsEdGu2=PT@=*J*{;X6P>N>9f
zLT-3vY at d?43krr!`zR+>3z03;=Ftf=QR*|Dk6UQnY&7j7I^jXkB+w-C-aqZ5`%<4H
zd3-tK%}>oHarl;W+B}(*h_eX_MNj)EG<73He5TD4$_<)OSJV at o3j!7rd1F_BjQwnd
zHbaT&)8+-s4Q~Nr5~Qw7`zSDXRp+bvZ{#K+z at 7lu)@k$V$W!#xbV!J%ebiI9y7RF9
z1Ne%{nA&+<Uq<Scn#_{=$iFhF^Q8W8-6bY<AwcBQ=Dij{gi3+L5~vCMb{x at b?aEH8
z&6<Awmd?&JKit`5x2Dg&B{#eTKm0VSv&~^mf21GYMs+XjboE%%U++ukVxp9t at J=}W
z^WJ;(CY`dt5|g{E6OP9Z#2dIshf$pf at iBQ3sq;s5Ld5q)2?C at OBKV$)@563pd{G^Z
zU<OIxA$ZbR2iF5254ME5h at 5x_d?5g#G!2{<(%@M2$xRcXwP1gGnl^@YO0l_#9L7UB
z_p57ISn8{OlHAllT8ayQzGgDZWM$3Rx2$;Y;wpi5Ep0gAS5XnvRzqHfS^hPl0)7sw
zOjhaFS?kvc at yhS7S;;3mex(9fGfaTaqSnA=IBTQPKx$5N*oABGI|DZFw@#ZHujv6?
z+?-2y9?6o6X-%A;Ce82hpsAJaYbE?NW1I5*$uV($DRRFUxs1%Je0e(k)is_*@25|P
z>JhnXyfiB1*N#k23!o!o`chWMJ|2WCKKC!7ZD};Y^OQZ!iT&3#EpW_h#!C;xZ=DKK
zp5h*bBfZlcywv#_*LdYx5tl4*K`B6+h)#3?#oCd2|DE(0d at O`~-pIi-#D0v;*PMBX
zSQ%8_4^}#4(s40`g=4x at mKEm-_AZ9+nDW_A+R6Lgs4uVKJ*FwosXvCFiX0CS?*-UN
z1lM1&`>%;xxo`uvC6L^J?GCaujEFlY+Ta7xPsH|yA at PREj)p;Tm$;{4K>W{OZNqKx
zK!~ZKU%XCefvVOtpZX9^IpC*j(r3ZLnCBE9K{d?#Nu91RmQv=*@oU}3uW8?B_BN2#
zp4nVa&79gon=mT4><7<En~3)87-`D<;QX81MtcD+0DQ}hybFLU{-*D^ek_340Nes2
z<-aCaZCvs7;FvPMy-N(%pteST__-BwZw?P^o>ROB!a(TUk5&q$vp}YbVf?)*9vNC<
z?eQ at 3oBuV81^Rf7KAz5f`5L>G)=r<JDcY%z=fTqzZ9KOpu&pPa?+9#bgN1Xv4<Yt0
z0v~>v<P+HU<^qFyAaokrF449|4Sa0_ndWx at JV3w`Y#03O7lIuZruSTkR9`2VPSYKn
z^^MN&*z!gI!CsUyx3DD)kR)VYMC72)97{n${ZEpG*W<l-pJKPCfcnp<a>}(8jk3n(
zGc}g8K}(-77jp^XQBrp4dU$TVPyK?I*z+iN`8CTo92+!4M6>1Wp;MLva+~~R^_wh8
zF+s7&U&cp){!TCbZ3r*D;ce$xWv4#0OKAHe6!=?fnp(o(#gkdjBLCeh+ta{r8{onv
z=)1!u at Z%}aF3>*`ir6!&+`5iU)2=1 at Ca;;cdE(yc9U#aH%?0s(yH_vdseCuikuBog
z_cN^406$=FYr!GEVqfSdxhP6<4F_+E?vVO;Psd=A&u=hIc}ejU+&4LY9FDSgD1lgf
zbK1WqB+ZHavEyKpc|A&iParwur~JZ$r#si6_h!OOzsJd*L{tsyx%9MR9}|~47^3K=
zt2ik&ey}`RgW%>9e*Y+3`XCVQW8T{hI1=+`Fw`hM8iw1~;$b@=zU|`x#QV)R=mAa&
zoTYHgkAcCf;ckLc at XlQE8BBf&K@>hS*>Yx7d{&T^6i{G;Pv?E0E$6%8rWI&ka2Ih-
zcF<pCE7()xD=i&`|L%ZGtrkx1j~&o8dZueyVfE~{jgj?ifoQ0!pdu(sBHp_cue3Ih
z$4x87!OusLM@}3a`Ry6?g<)~oknz0^^<NosbEu}=E5Ufw?=vy%Ekso;Sv-`}t>mb^
z+0>!14KEewXd+8BV<?zwE%q<rq?Gv;9FY}s;K4XlTx320zj?&evqzGbFuAg6C08oD
zE?ErZJhr+K=%0iU$T`hzq&n0Op$GxL%kz#g>R%o5%$H#VgYgaf7Fs%LD>|Cr at 3`__
zM|W9AWobv`F;_)d#h|bwq;+P);^K8Pa-x&wYLli{9`C45QqKk(@{El}bBAII6=rES
zRO~FGfvF`s(2y4k5p8x5^aDfN&K3f8JTuWyPr$Zk;5!KqE)ER)Lr3L1?QodN9!V}D
zd-FwSSu*(o<Le!eES&^iKH1sjXA^H1 at fHzJMZA3QurR;axoa3d&=LTtj!^Ix&Bhpl
zhS|=3Mxke%b;D)>xj!!+R;!2EvAs~&j(xq4O`v4HO3}-xw;)9tJo$1eDD?zVJQF^9
zRDieJ?B`a9*tH8gSAO5l4n=tj!~W6kvn6;sso-?&v<%u=GrS;!Hh(nilL3br!(kgc
zJb3~4W%%GE^AzO#2ns;dDMyNvAPGCa<Yu<-^_&y7mh8O%w0gEkuGre!$3wLBf!e>c
z at 14c|q)qi+V{*sdwd_??@yy&jj}&-u<?;^g^iahVE<WHSKT|+tyrW~GdIQ2iQAzD`
z!)AEBt>&T<4$y_q+`-SgHm&Ho=S%Y>_(uo;FKS)~dAlK%$Az+bs*LI|i9##N3?;(6
zbtd6l_;C^z4t;GcJb?(C(E(44XsJaR3Ox!>h2N^>%T7SyqBG(KZq}lpJfR0kPlw8i
z4MhS~17C!)W(4Jh{2IQt0sb)>_*sa&nZJf-H<$pEu&+9jB?+xM5LsibK41YD;ZlF#
zs+6lbGL|gb{q7`bWVN+=_Z_6&S0w at Ur+l6s2ki@sq?S`Y&(LQ0*pCi%!0MX4v{#1y
zQbX4 at d)}_Z_P0B at QY?t+GRzoXyu;3w#=z&C<BI=kgIJ at X9v2_AK&-}4CDJEPH$jxh
zR(wheG5EsP7tdQ;AI+6Y=0wL!$J!vatsPI&azohyYpM!X%hpy!LvjxMgA4Fb`uRKM
z{@p$Xp8mn!pJU{HGd>v0gP}=)3eUG=Nx>0NBlaz+&fi6rHq}r9+70r3P+?s=o10g?
z_ZnGYc=KYdcj$VULHXF4c06%RrnN<5d}r(@3?V=FgLe4YCf`s^ydjaBc<Ao0H<7&e
z+A$psd87g}-eB!v`|<V-bcT-<K9A*ZBma`b!pz45g%UY at Z9MJG8*&3^*SnU2-z1wY
zgf9PT$keDK?IuoWq|%Vbjbtq>(bM=Aj#PAF+7Z4f4s=NFR%B<b)<&1*8_IIFt*O!9
z)>yAla7|FEEa8 at CJc~Z&7a6S$)3uY|v7+U+Ena at H`~OdS?;h3Ewe}0owN@@j7MFnH
zC0LVSl$$LVy9LxOLA-&sYAaq^-&d$Xw5?mMt<~xlqSb)xZbi3(#l4egG*Ht*YBh?q
zAzA?mEn=&H+%<q!EaD9nS>H2PK<z%?_kL%*=Z`bSIV)qVl{IhAeCBW7=X~ZfAsy<&
zp6o7p3@?=?U+AEsed717-P{pkRQ7w=5Hllq{)};9GhWMD5uP;T7yXP^VbNm+Xbxg|
zrzz!MI?`5m%s=cYU2rLGTE>D%epqZOn||yC<EChFmLmmL^9AMs`*}$n>kfOaS*PCo
zWXZx9{x7~7pY_5dZ7Tgr$$RazTNy2F+*Xyc_hK at xl>%Cnnz~-A2}#}V2_HT+yexf$
zTrobFN_(TDTdT>~3A!*YZVik$7#>CiLx at NrxY`uq61iM8er&Fg=9#Dc0<|3?0$1oD
ztn0P*5bJqQ_(=Gmw?93;<w at G@Yh&B-?v3>6HjS`r<=za;0IcW*)062X>q<R?XU*4U
z&J;M|jgIDZE#8~4V3S|Ma+jH8_xYzjzYBeqgSzg7F{7uC8UIRh?DA}{z#N<gzIvAD
zOs@#JBzBh9jJ<R6NP!mu`!LeIhkw%xl#;+rG2Jm9QOskxeM%lI&mekgBncZd&cHG5
zwd4*yHe~a&V%$Jdm=*Bv;djT-r*V5bQvlpdZ+KCzST)^HwIOkUyOP)8%v74nhy}ei
zUNPsNM*BI-YTC2*tizsom(71BsVCi%-xf?b_~P_Dn~#b2+nxEfDOr8NGo4x|px4Es
zbprP!fk)~F at tS05(gHSfaa=dsX=<R1Vd*BYqa)MW+e?0HzjU*`rM>;GqrLEYJLoUN
zed>stfwfWPaq_tF>8 at aVdI{|>P2ZcoN at 3~eUDnhVDr|T590TXACzyFsKkG=)xMmf>
zJcH#iw6D!RUnS<{d4u{V`12dE?`f+Kb9nH^xf;ETv1YIUZ+}V}XoG>jJGKPfOQ7>F
z!0!S+Tn)VH&b7m!c^u4>`}<|Z+_$Qa4)F;L4^)ZZ*dfZIK=l#m-%w9QE3$4w`V!Gf
zV(r0p=s~fv5cNub8Ve4xeYNCB`fx`rEH8qE{=?T=c{mSSKMbL9IkkQ0FFCBJL39Kg
zP``45o;?kxUl{?eKxg9XF~1Tq=Ov&_?^o36YQ$7pS4}YU$!b1f<0F`WAUjs_0#gWZ
zGOvpGY{6&YGxq)jhVH-s;qUk$ry4#|9)kYDzowMTyaX0WtH#mn8sX#!B4*XAAt5}N
z9Kv=RI90FmZ@!H`T37iyjquJ23>bT}pVBFphqEW+2!Z=m{^pVhcBo<t3 at sOd6-{Ra
zhUG}`N{y=R35>}mYPX5A at A}faLq?~98jy2R$BieIAf`f3RNZ(|hLd9phKUY{A6_3Y
z27|H4(E6lOfX9FIQ%B2_N_IckSEOip;*Z0~<>Alu_(R#ElndJn)Z&cT$hQabY|mTm
z1$uFQfSPo-F9IsNRNuoK-$c=ws%jXVviWSH3gg=8cU6}tuzyE|TPoHbX20*IO>Hm8
z5+Bs;^2b9}%<wy^rvCB>;U=~h*gzO(R}ln<<lx}Ws)x%D4ebrElNi=clNrg94f6#q
zl4L|iOXPER7w+E8I~bd+{+%Y17hX4?zYUU#UIl?r5uIgCsc0{lFJ2NG7VE~=n^0MM
zfm|HuCnufeVoEm>cvf7Xig`NUcAIY43q2XYcV^lUmT%zNaSn663v^P^BlI!uY}yi$
z(hVbJJ}<34ACeKf>wGKE%{W+nepsFuw)(R{13+YGy3A3{=-<Y3nMCMEiwPNOFUSzX
z<jdx1a6F-h>%ClPG6YW3;NB7DJ9~QCSgbYk?TdWl9G{@P`EiG#aN6RKnNQG}<~SlK
z-)tM^I0Hz1b)u3`{XlliH!AbovqN)U(a6$Y?GZoza}81Z1~3WunJBnKpbQj97Phvr
zFk9a7o$V-~XgAtRJI>i=0S*mqHeIUWI}Aj98?0BHp!QsA)9Y7bDFaxdQP0})JU`QZ
zZJW%C(XWn<QTx&vqxIFK{^AfvA%ziR4GIMq_`Wo2n4>75iUs{HOI6=c#ln7r%J4O{
zr=*P at c5q)mdCc!X-sy_lvh=HW?^UZW?#f74yKKpigtAA`yA1nLG0u!kLq4 at jUw8tz
zRpdh{gs`yg^mSXces$GlwM{>yF5h6p_A(+ZuWi15_1w4A8L#UvV{Q#OsF-Li@%-!(
zYqkg|&<z_W$+yKLNm0AR<<z*}ZLTIzF=8TU-<ZpwU2<G84|eRZF)gRfe3{Jn99S|}
zOtxx5)JhLkHqw`PLgrj1%@rzzj?$M5qbIQ at FNGFNlEMt&)9^3K#0v)fh at Nz3Ysp;N
z!%;yNllqdm!L;K5MGt};!6P2F-q2*`5fJ5_iX`EZ<Q4lKIB|h8x2cU5sCg)KV_T-h
z1kl>F#~sJ1q*ydh+NS)tEsIa+`DErSX}&cf%FTXLL6P!lFpo7)BcQIY<gNX%?UJfZ
ztzVrgS3|K19M?PBM4Gi3l|tH0Wrj*^*n(>|K86C~K<~8`E at VqaKGG$&6)wO<BM)Wi
zQggh4nvo~Thrc|Y<7+(Z2boGY`p>k7xthouyb*>|87Atq7$mtrA;-A_kJ#&Icy$bh
zSIy|=1}prRjlX(U0{_|AQZpJ(lEHuaczE^j;Pp5**K16cC}|pDq7>^{6)?VDFlwW~
zM)47eSA20 at Peb#fFfM$gjKsL`SX at +Jf;Tj;&QTc9rsoq`fJ<CRjx`~`b!|w0>B>=V
z+ITz8&_#qzBaO<?QEi1I+oGb*<kJ$p>lVPwo^KNRid26Au at H1766oJHns|oy*^Pa`
z;tru3)%MP}Hf^dQ8|g*@Jm$-g at XrkgP?k0|XYezS2q+xhhLQ1g5A|t_Bpe}R=EX4w
zr8q at AM>08`hnocoQ`mwN1=Lk+vT!nS{Uy!k6`=O^GfHpseCLEOt at w=6J#g6ctF7nM
z=JP`?=0ioRV+-}hfwmaR6b at tflzQig?3M#(_BJ)0nISh<v*mLGb?pcg)@t&rBGo69
z{`?SMnsphX`j~=tokzdxSU*4u8je~Au2a=&`rW!B)koL?_|iEX1NF1wkM;B2f%>_|
zNuvbnMrL)%@w=stR{iSc>uOuc-Y0j=jpVUlceQ9*x5zb>3F(f at oKB!Dks>SZ1NMl|
z!DbT?ETaELd)OLbaZS-$B30zDwM7cGWjQXPUfLksew83|$@cjr`)nn<_LQ)!UREa_
zcN{#l>cf~TP<4-+c&G5`^o^~qYvFWQYy5VIN$r}bj$aQad5RW(*{r`p=9FX&GOfF~
z)Lsks%|M3FMJ6G%_isX#cO>*z?kgBP)q8B^&NN at a=p|9c653bqI*$%2$PdvgN102c
z&=SCg9f<gpNRvuJx=Y5wB-T|I_!}&AN{Uh&LSW^r^c$Bf*MzE^kgPXA{zhV7ky{VB
zi(5w3QVLR|o9~bmRr7Q<R|IFH^Q-J3w)oE$etD}>2sNHCeD=hd2aq at GUdkH-mKDQ!
zqt;c;`@oSH?TBGp>2()8GZrivttPk37r;PElAO9AWIk?KHtfH}ma(IQ-J at dXQ)oVn
z!(f;{V{bR6qXW74Wo62x!Y$;o(XmpwHck(nP*NO0PYTL+%m=x$cd9!HVAZT&U2sZG
zdz{G+7O_Qfoa(R8h7^NpK2;oLzX<0EOFW at CzWIC@3^HKBqDn$Hl`RpiYTE5qzB8m^
z6^R>2DZR9{B!u>eQOdfNaAq9om2wkQU?NNDr{<Gt<!IHX5QVuUB$!sjQZUINwL#l}
zYqss2PYw#6bzn%qq%VVe=>2dWLnujJXz^G$kzTV&P3Ld=cwK4Lnl<+W6F{lffq%3@
z)ux5xgih?@^@{7{o)Cv+kY at N9bAjfg(}U872e`%uV&Z^pVW1<-*4r}HVXJ1kgQtKT
zF4fgwt#!8rPf@)MU9zejmN22OhjEnDk#_S2o$fqDjJNkAhtUf7o1ZcT8*qE<PmCSS
zwxoo!CB=52m-@(WY`zSnXA3#s=9%d3h!92!;y1)6FFbHR$tIP#I%e`?lD5V#h?lSU
z$32$660%U>$g1e74>O4>2hoFaTyyC7{CK-0CwUfRD%PS+r1jhY6|fPf{S|*E!!8l9
z2A&umcFr>^aW-^sBAAmVhN~u#wvw$j`#kVS$J;05K7={C4uvJoDLiq59i!4yy5GmC
zpkHle|Eq(d?OVWX3ySvn0Pn|!1u3B~xyMw?!nJGG!&%)B?E}=V1DSy8#BfD7V*~rS
z>m{vNo5im>7z5i9!wV1f6OYVt68BfL|J^`*E6{|0pT9gO?&xH+%hs!9;X!&h6pG33
z*MTkw%s!|@l7&yK)`qN)m*dk~=X5ZnHz$TC+q`iXZZ6qsu9)KphwcQ<Y$t|S-4m~I
z?CyG~eR8L}WNY`&^SbV6YurV#pkdZM;Y3%MY>qGAM3^r@%kBwha4<|ZtzGpxWj-vW
z-JD3lYHI6GUCnpYw;Cq+ihVsg0Jrujzco9oU?+IOT%E|HM|CI8R?BAlEV`*Xcmvv8
zmZ0JlN|!TWlB!0sby$a#`)-(c3J+BBw;Pa+;v88{sBw}eIGvDKb{0Wt$rI;=o(?`O
z%bNt1i}p1U3QuSUJwvzO(3eOnsSZ&M9bXi0Z*B;wb&b!Tk=qH@#U$+u`^4Z4Y8aR`
zWRECb9$y6Q?_=;(`ekP^G;UJbSLM6{4DTNu^$q}a5d>Dm&Z|D}dG*MUKp1pR$ak0I
zt6_~D226HI(Xm6FbKEA3yV_qzVn{NKq*7zW(c#h<l90m%Z?WQ3IRP7OQa>qpf_fyM
zN=Z{6NV7kd&Q!)sh4ZsS7QUs7tz#8>!&4 at 7#0?D|GU!u$cj*mJoVO|HSB{L=J03f)
zEYiRK3M|b(X3AJ^AB<w_42;zuF-p-54IVw{Q>5ViA(PD-R!Vp2L4lWrHM}pa(1S)d
zvmQ0L8nI3FDk;3nHXXQ)Cq*5bPPsqA6ar-x-zpw3tQUQteAl;%`vV5Q_-{euTQSw|
zHX7Ck at cxCf4a!v0-n2`)S2}3O8L-PW8|9gGWnQ!hwyIQO$|whEIeH`rB6!`D*Wz{q
z*1x{HJ|RM!?KR<aRP$Tnx!U`Nlgu1fd*Hj^T#4`fa;Gfv+%L;Oh7=QdLK@`*1Zhn0
zJ0{X=miz at D6TDn4elxx8pRvGTdr@rWF9k=muy}g0*B&8?J@(~26Dc{7P32(dmb%uj
zdQMUy1d<j%P;7f%Yq&>_!?yuInJ^JhlI(_V5P^q5zg+<z(AtB|U1u6#bS?p8=D619
zKPP1%sm*=#NR2$??y|=&A&rM?$#Hl&8JC4cUMX_Xf?WPGg?1(^ul3r05iMAebr0I*
z6#24gutw<H9V|d(bad(-VTV8 at 7bPb=>?=@+1IJ)~@E8fo%V&X7Kc&;0JD%ppeQTmX
z@?4T{*GLweI*5tUAO=16gmq$3fmx}#$@HE9KJnFYy at 4~n3!z0F at +J4;&cQxj_s^*X
zRfDtWtp#eO$lk2cR>8u+$Y at DXjSI@rfqKqbmq)qvcu9ken+5II2`X5P$ECEWlIOTb
ziuN6!cJMGgNVLEr->p^_;N at G`#HlJ0OLCAXIyH5VIu42Q6Mh9LpME-U<$%U3D!)J@
zK4x(<UmR8N9qi;d8kvMRmk@{2=MA34tGQ8p>U%|*y!k6}bCDIc%Fp!XXPPT|At%)}
zAhyJGzc!^iWy;lqZN=`n6SJ{DT9jW7&bE5S{J|8s9x$XU_Uxz{uVVMB-v<Gquf?=m
zFx3 at ql@uW=IzO|+B|g7-_Z~DV8r{)V-I3mO(Reis+Bva_c73siGt(=?pgeYhoGyjy
zFk_GHBNP5E^m#&Qw_A_9)W~VN`4&Vgv7h<dtvT^`X7Xma%=C&c((bb!m~<0o13lxc
zH|^GZR&11mr;J+GdB&vf<G&w2PY-H7A+C`JyLa8OLf3Gn(pKN64~FKt7iH*>N3>SE
zXEz*X{9x-LmxOC9b~qE^hB%fx$d6CGa#y6t)mQqKt<WZ2>B}0kHR(dHJyNt`=l*Vu
zcZy%`%q(zA!ESn|Ta-w1V1Kt=c)VjnM99twp~qKf<k^0yE41pGKJc}BtX5v#w{FbV
zF!|OE;T{tvctQyUnmhO3!dZuBAe-6o(*Hu-Lw)@Da7~Rr&`Y6qPVg|28u`%?ZdZ=7
z+d?0 at E|V at B*>%)Ma3u=ixQ@>N?jro0{Dn-=hF!+#T<5p-zyyyCkw1r>dx<|ME>LI5
z;h~$KhNLZ{hv&%BiI6W3=ZNDA;bz4Ca$HQ!$fD-J at c)z-Y*nhxLa`e}dq<KHgN>te
zr<&{fw1>6&x;|UrR@`h%5`~|F6Yx;|9VeKCd_mc0i~KA_^JspDh#nbt=ccHKp7Cdb
zcAMjN3tG6xqWB^Wbcj2rfK}Wa$%2J at uPntUFKIr_JAR`1b+N{4=j(75Nb;TI8>T+r
zB9AJfeI*NOMDgKq4Yd$@#$=G8oI at wP`>dbe+;y-r<Ra0eP>Xo+;d4I`Eat_>?#$}M
z1x%PbAp%xqH-r;3x#lD5MsV3eCg at pl5w`t;We!+kx)O&bDL~2}dbcCYj4Zks_jAXg
z1?<hALqNNUml+&if%$em98DpOgDs<TH&tKjE4ZkIQrQ{|1EgO27oz;Dx!n+}WptSw
z##hkD5GozRHkEAKMrn9+HR>zbFo7+`Dl7LtAu4&8wCWLKf3ldoKq99Zu6p#j)rXT;
z2|mI70Ry*#&YinVfVUxDqIGoGa7;^a=>;w5dj3EQ?t1(KEjT^>Z?xv3dt1ndARrY4
znIJu)Tvys<;bl{r!6z1<KFv$<UNn09{vGLqd;B`azx|j0Ts*oaye#&{v(B62EmO1K
zK7XyEJXZ5o%JO}d?3}k3z1OpT`RA&Y%R4@**?#{&`W~+ at +h*DK%kBEw?b&-ZM^2QV
zKD{sH at S+o#;E|qhJI-T*Cu+`>{elUew_MJ?feHSixt?+#6TGqLUf8*4O!0oq`RJ3U
zNYK<}-6USK&Wbaae at J+8=euhT1IIlbyc()(@QtONH+dFs&1g(zaEjle@!L=CNFVJV
z-V^(F()`8aJEqpWegD<I$FXH^S(anA-qyUAvi?Mw<@EAJABCNhF8}=V^z||4r4M(0
zRRz&(wB`y$8?vEWj}Jex`15oqJqcI)EKs196rJWRoCt*~K6WXYVD2wZX}AETG8H;Q
zDl;L=4YHO{X_zgp2^U=^6EnbqSD{nw#1?0S70N~4DT8xWMn7Dm41cIm&`WjaCL2d%
zm!p4hNoMQXs_)>^wJol_OV}1K9X at a8=RRys7Mu7MzZxigs~T3>p-5tN*W;!*S{P`g
z5`6l4s19D&9raDJLg&~3!++HV#yQtXWasI#LMw at iG-wov9C5k~HZs}zm4?#(*7A&Z
zh%)i>LUWXlWubRlKr_Uy_hQZsUUx2J{<%qZBQ(2bn1m6ENj6XjNA-AXKDs)EyzpmK
zcD-wywYgP%bDs5QZK_<WnOa7tHfSww(2RoQF{pCi{6wF$)iU~4k@;F*R?t>SPS*Qd
zP5isHEkMSjV>HaCnj5s$4Slw8TWOKVZu at YnVH5C`u*u_YRZEXY+1Tda#r7#QEr4yC
zpP?=?F!?g^i}^JQ4OQ-zJ+ at 47D<^wUj&IHAt}<(tz2AqU3)6 at a>8vfl`p4L?%C&~l
z6}L23ij9FM$GK&X*?mZxRm)@r{27-tm_6Ai)9%wr)G_Aboz_OC_0=hbg)N#O6aVJ0
zo-vxBi%gHlV}F&FISLvLe`copqulBMF|>+2xWWzd%QJ>%%V58HkTyqc6bBGFt`?;~
zar|kauEiv<h*Z14*X<ETWtps4P)3&v9LvF?-qomt+q8}__Dvm1dStEp1%@#R6z?)7
zq2V1Eg{$6SLU*^A_*g9h4<?*Tn7i_pm^WcCrEg&Lfm3KV#W|v5-sWD~?JDTj06Oud
zXUH9T#zarofy$R+VawK*GOEKJmf-9!!PAIYj(c44X^REa7n%6hA}eTWgH2JzC)g$b
zkx5W2sN}0oVbjHjCfG<+9b~Q?^u{Dri?Qvh2PG;0L+Ev^|9j5Rj<2!lR`=ciU^81Y
zy6c%WmC<%L^|ijXRdlEIFI#s+VJ!u7AhnL!kXi%0_;7Wa?yVL_Jm8>;!!EP13%vNM
zS&VLW3*3 at slvzR*smzrTWTu|2APaFsOKxkFnMRAu<f3|$HUEbDY`|WodV at 9JG6^lA
zhB^5A;7yAR?nM!l3oUR;AgD+&1KUP*z<QX-Fdu(!Y6pcmL7?gYzR$GjUTvW#I%2Wm
zmct}ey~X(UX|1uoou$R(1sSq_Z*3V1*?ihvk%ax^UoZ4)f!IWfL@@f)2?QB%!=&iY
zjv<iVyCYgIjfA3I<2F$I%iM|u5_`E at r4Y&&j;{#-6vB0hSDy5ic(fFXTgu?9ok{4-
zB||dyv#Fu*U(NK0J+3YKcpmKI>vLc-c}dU$;=gVtVVRF*XbWl)U8{93IE%9uYSwD8
zY7ypU#FHJ#&i%Tyzc)igx7V<#-puxqegaQa33}0s$p#+W^E%nJ*A*7bp_r2})ZGK-
z+aPNM7;R+sXMjKul>Wak`|J8W81{h&pG|$^*m<ptfYoJu7x5>IJrFk>FmD|PO{XK6
zfhBVTevgK0()1-Ejv&b5V!k5 at 66^w5DumJ&g%cSi$UF?)Bc%w#rX_rK5C)4DLo0L^
zaf(t at v8>2yOY{*XLkgj=jC1WBQ7!XX25U5Tn;XQ{i9<|8MWI3keW%DosAWTX(u#=;
zro*G6L~`e*hanxqk^c;u^wGFUAC37^;rD2VALh&82OC!~0pckF+4!GT=s<B_0RBGM
z3(9_pm7$iw5C+PRAGVBpiuVFUhF7s+(-WWrB}j%1`Hz7dJ<%Y=hPoc;Bwk~DpK7(S
zzPFE^C8u9xwC`I at L{j3=Vu52a_H*!Iv7kbyV881gOh3n1?pEk<6-^v$n2tSU+_%Ym
zk`lce=+ZxtJ3c^3vLVgK#cG*%mTYjdMx0gNoRv6Co#>@c1ROlOWnN|T{L?yQtK|82
zEbdS2Wi)P~Cki+BsFSu{5Fpx3--sI&#a{`wF_|c^OG={%6Ai6*SsL^;^?l~xt#DP)
z2U|PRAYrSZjqIy_6}Q<9)vLFvMq!@fe(RQbSIfMhy=Gg|d3F6QsiqKu+;r)g*HKv4
zRO43rN?BU5D<+CtbH#o(J?$7Fkz)iloN3FFd7cdL(96WxvizKvn(49 at v&=Jxwojzp
zp<H&$#2vCH{gFTvutr$ZVv~t)j?~DqTP|ueF6jY^X}HM-u^ezOORFdxh}Q50j%h?Q
z4kjCB7=ukwO_1A!4f1~lHpVsL<sU~Jb0q`TN=8={8U|tOx_W0A6`Sc#t__6wPweP=
zttV|2OwXRTanLiXWy7jvo{$((1D?H=HLDYcB}{M2onaE1p~UfUxQwet&~gENM~=|v
z+!a`Xk`0A>0m0$PuNJ5Qr-3gA!J2=;6HkMZNm%j=G-1?^H(8lWT!&TB|3e)%l>V^}
z8x}mT!>Y*xbr^0;d|rp4jd-aJD^ZGHih!OLG<|NY=d#${um(pf)_iPT6RU>L{*UEq
zP`o%dRvjI^3 at gFI<JUA~97BuBd;-=s at W=6Lm5bKG0tRd{1;94x%EV8{WP7K0trRcI
zd9V4XIB%ZMe%_ktTAEr3OJ4zmQC2BTQCdR%6_%hVxj&gL(g>wF>IU+lCN){Dc#CNe
z7{wN%QfLW+V;^Pn;=|)(17Jrhenaix(!JJVp+Au#o|#xS&!~vEE7}R at vPi{9rcALv
z16p5;9ImDW<?yr#CNc{AiTTyR-f4rNk}MV;;?cq!rR=*V<;f<HScqSq>wot|Q>OrL
z@$fdjT5;#mKYP~2AbOPSo2Hg2P4QtqyGEMbDYb39&r{zpTS(r#+Qjm;$&yI9&6f_`
z;-Y_pGAdPFeR({y{$IynNc0_?zxf?k`YJU0*|B0$P>JZtzF)LQkvqOY^vLO7qvRlo
z&q!(ZIG^%S@%myn+U}?$3P(4AYUp at s$6}N`5tM1gn at u9eVdl~oO}2{<^W1!T2PXs$
zO_MayZ+0Yh&!l|%yiM-r${JX(xk~Jqjr6veHd{n;e1LHts1#kC^#<keW at _dYrZ$~U
z+g}${>}E`><ip|Kl1)u{bV8k%;A>PZ7iV`T%;6`T=B;~!s+N0h<oS$zrRAfho8YyE
zw5)Ae+hh{K=Kr2GO+AA&R2L(DJZR^Cx*6A6SP!k`HHIce!`ri}NutRfl+HU4aRE-E
z1-prIa=P!Tnk-6DCvuOr1l{x~I1N&8yM8Yy*i+EOd+aX!jaTF9(;%#Wv%@v{70>?I
z5^!vF%=F=?hALdpL<eX at Ij#~->LF{o2(-Iq(}>Y^J(fPEcOII$k(XQ9A8aNBQ(T2*
zJPQR2sk~EBZFggvdOVD4v3rZMsq2T`%Zjv)UzoemO&dL0f9@%JZKJ!$Uj7=bhs(U)
zfI%^|0Itk_S3;xExCm1Q;Q<@816S%?@WGWmuQlbQStxphr~>&$ql>z79+E_(eBH6L
z657jpeD|_^t>TA1n7V*EplImidC06gHOXO2GZgp96is^DeMK>Jq!EdRvwildI$4wA
zY at cj+6J8$Bz0w3HRm=O*hBW25Bt$*74AGa>Xp6*-Ykei2IG<d5TK?HcPp*E0x^Nlf
zc%cV9&&WKRU<kl-#tcHcWjq||$=jVeL6hMm#H4^1WjN8NBb!tw!0h<84-fBbN0FkG
zxKP4edfHf<HaxV3G_H*=tmLolYXnsf#7*%{S!-#B%EoAfJ~ds%{Ia#1ALoFoEQ2Rg
z*x8sT5`53lA41*_p+B%zzE at 63icSk1g?%M$jhEVhAaJ{>)8u9m8VdSaS{n1*6EbdF
zg!-RubV1MkO<zfUBluYITdTcSGmjnx;*V~SwN2Y<-F=p}gTVYeg}HCf<;J{02_xXL
z3poCLfT8E?08PY8r?)b=^ePJa3M+t(gc0qyC>Q?RSVLC)2IE)b+CqDy;_JSW^Eiem
z$M8^Rx>{_FFy1C=#kH~;5*9XUsFUM1iQw)Q$i6~bBjjM7$TCEmk26qGq%icAoWKME
zTa3Y$G^e%0*jM;9juZMcil#-K$KNRDLEL7D+ahYoZ!9RnWedvRH$c9OC+fsm(LU)K
zl!THzfVGT<g*T_2?;c%ajdJ6ljl)~Zdum2 at m)}>H_il9A=mArLcNxnNXMReG47q)U
zdvKfwYb?A0`sRxJ3T<D>F3jMFtIq0Y<h7(XI{Abfsc6Z|C9nue;8&n01FtiLx;J7u
zU94bDJ{3MF`BZkP7Q5k*9*7(m3`AH`Kui+5Er#tAKWi*RjqwS35rX~3+jWu*6CZX5
z47>Ya`2P1km=*KOyc9?K@&vtsCjKt7qOx!QL9jG@(TA7NY_`F>s#|R-cp68KQW%*$
z!I*=u6!Xf2;3W at 2!F*>|pHV;$dM8jD06TpauhT+Y9r1b&Z87m1sI~7j77JRdiNwZ+
zte-M%KV_Gnil32lEUeVCTY%=5`tJN8Dv#LnR%6%Sok}7=*Taq_=)#*wFb!X9EtrBX
zMB=kipxX3;KY|fF9)DI+s*b)qA)E!^IkrJk4Dg~<+xjd*+V%aQf<WK|8d2byr((cg
z!m4LB*kiTZrX$Rpi6?2Qo{r$860pA&*D-kU6o19WEnrL`R9pIr1twmZmth|czUlEU
zGtBUc`u5*?3NgZpIbegG%S#nGEd58VK>rTxSqj>j++tvn=Xiu2>K^Q}VG}Gufa%G7
zNVDYc>gfoi7{QvV<SVYS;qQS1+ at NE#1>@uywqV~Qx?w0}v%QOxp}2>=J?R1k;x^md
zYFk9>PpNpdi5}(n0{muOeCQL|Z3f~+=IJP5)1B!*g1^{mTX3swJq}NgQe}ca=z)CB
z{$36K8UTQVC%;WEVkEx<+tW|uc?R^YFp7e`+cYApL<;*PgFY2}I at V_!52k)5R0(uo
zruQ{;&9I+N&vuyLtC&!IgVtPrFL&vPN)X-=!F`9o1m4ic(u1X6-c}QSRj`h&Q7iot
z-ebkzaXg2pv?UFbg>ZFa&Ys?gEO~%&Px5g8<4abi>E(e6Ej~1<%iR<i`l;7^R~NHv
z at acJ7KZ?th1>fxsDmry)48}i9=41a3no<f1!zCDQL5_gx2oR&AIw^&7<(U?NC%ika
zSPv2$m3++}$+xhvIu)Hx1fllKz%QYmm;{!zs9(!MYh|#nxK?!pI`*=87Nt>EaLSr*
zfO*`Re6wswYaLVl2IF{_fDxns#7K-VTIwWH66bEPiZ)cj6i@*NJ_NX}Z4h=6D!6c%
zP-LS;x*b<Au8FXg_>5G`15A|Nr9K>Uk0-`uL<6srr>bO`w|EupKQQt7MsYcj7y4K>
zvxIhA7G#y%o>j-9pz*gqr~TEjuuOZ16v at vHU#DGeB#J3bDD4Xg0a?x7Jkf@;k9;!v
z;DpWQ#~=+CO9()41B(+BLx_ZlOf`HTwumbQhPgz-gGWXg+|k5y6ti(>F0 at BU!Y{Nl
z7!a0U$dSy&V}_5w2i#bUK{~Nh at Jy&FNhkVOqZ;^??1Je=@k#=uhdn^yiA>eX(!=Al
zMAXSI7NoV*CoXM(Y(7og{uGEN4;R#U)e0WM1{Hv?SPU03T#dR1dG1I>C2u!$i1TQl
zsJ?mg8)`1ue<}vq$V$H9Tc8Ou2xn}aeAO8+XF{i2IJ3bbg6W0p4utd~m(b}Ac~ra~
zbdy_9rSMX0gD$$kLKs~YJ5h~5@}n}kp#$W+irp{%1R_qNhE5VzYAbo2q(x9eNeua>
zsv at hX$ni7u&Z^C*jB4ehxU*fe#krZf(1r%r{NrjU+ip;{B at M7K3yS8C9m0Z*9yDDe
zNDOR<o at 3qh30!x4d^zoXX7n at Iu+Q>962 at 9C1^H|1wr%;wvH!Ydn}P1i8uA3QJa!Ps
zHr?g1PoCh+88|2)0=m03A$vwNq<$d<@o&)eI<fpTo#~J9H{LC%v|jwN^bC8Hx2D9{
zQtqZ259^&T@~SOBw*Mx9b7CN~aE at p$;XFwXRkX9Yf@$GCA#HPgA%XM6#IGL)d6JL^
zeL%VB<k`32v~lU=IrHw-*X*%h>_`90e?wp2kG||~z3z5Bpp-}2zv#wWdU at oSQG?k6
zBTy_yg}3UV?_0&*n{2c7nq5&Do%>F!mJ$70SAX@)mG&<$OY*@Mu9bY+7usBp!)tF_
zZ#iyK8~%AVVgFN at HKaL45k7$;e6)4fJMr;lWSFbVx{BT!*b1q$mj_L9E2b-XDj|_@
z=2;J~Lm0$7`e!vX0#Gm~xjFbmLR at b)Pa{0I_x+p$*{88XNohU(p^8T&<Ud*aV|^K^
zFTHOWQ66})=rqhHRBxh$z{fR0Sd>UG^*HI!OH~AsaPd*UX^sIA3AY}0 at S3k%6IMU8
zq|GEjG?u3t0!lO<#9c1q*OFCsYl8hgn>kPipw2rp at tLqwFi>MOCE1r?S(oU~7n~>h
zYC?Z8Q!%uU{m3@>I6qPq at L^kT7y|DU6 at I_#7!2>Z2K<gy;CF<>8{WYx{FW;4d$7T)
zZ<k)>g;&CXHoinD5^5W+lghw)nWmoIg_o!NNq#OGy3M|4u%n(wu>0lNSkxtJRb+hl
z*uB>aw_-g>AX*J?khF$(EBmfy-!}FQa?}5{pP{{2sv5kV8uZY~kI#64hRpU#o%?p2
zCY_uCqy3y5uj8-JrsvwNrQ>~zsU`Yd`3eCP<y~et at V6v(Zn!n<Ty)HNE6x9qck-8E
z(VxH;Y%vAxkkk6%FTZPNh at +=lGxy;}0tZ)-Sf^DY@|+WGA&p*D at o;<@R+Jof;dp<1
zq3Z543pr_XLBZRnCQX}^QE<^}A`4^dK?(urf;6w;0i$fbMw3RKR<8x_^?^|#X077R
zGxl&x^87YBgOy##8&2cIr4PqyqCTD8iQD=X=tB)13~UyGM2g}rgSW3>PYHLdvoj4M
zV2d4>m0hEGDFO&QE1A7lb8+%4a+X(EQkXNJAZL_6T!WFxjh8Y<gEJ at n3C^1vX1HP#
zgpxMxyou!^$~K*%pWog2KZkD at j2U;CRgeF_ at ijd9FYvt$QXx)!d&my2s=tf73?FwL
zx}T-w>y<pUQrLAT4XZX+3R;y^bD6Ta>Or-Vuqnyrj*nZF2+;o&qaX3-0rVZ$RQLPQ
zcidxQ&PnHO#Tmd4#G<VFz`>B+SDt}X3<&<i at v|~4+JE{37Iwk!06XAL6yrU<Tm8tx
zwmnAP at qk4h<boXcoN_E_Xtv7?i#J}YtRx!vs$OO?;aeWen>%aor1xjjNw%3V*tv_|
zt(Xmx(oiWisxEYVu!)R1-%6osS53F;Oi4Z--^9%@Q_#Iu6@{4Nc9!mKhUh^*76ao(
zayAXShR&fB9599)1^$%%9hk$xE at Z&EON=}W7dHb87GQPO&<KCCLE;m{nKXv$EEd5o
zzV_&{Z3dtUJCll={WKNr&uM}jsyxh52fod=nX_p{GbRpLGzgy;Kz7(hI38in2t&_v
zJX)Q2C{b>=a>q3dFITh3D|_(59FV)Q$YEewhB7mZZ^xV;oUo~n=4CFYNlWZ$ST2 at -
zQ|&CnYd2n7B+UFSWrk+qSRdDQrGpdv8P<^|vxgdmc~t+IdfA$5tSm*YS-1LHF{%0(
z**DRF4uU;?0>a^|uEF7ExH1s>GdfCzoTpL%uQ75u#`Y}qJtzn40WMimKb@($a at Ym(
z${{tEVf2&dWzUek3r_4y6CO!|rL+Bow0RlH%sS02BYVX<1R4iN at b}EWHXUEm32|gr
zgZn{El+ztJki`QfXMW{+m64hCMlTp=&93D26O~PU?w;9gAv24NUYd7`sR7rC*cIs7
z!(<WgiLuutnFgGlbVI|hDMPPGr|uo+JU8 at GJee=z33wjQOYXTZ+-1+*pY!q63qFST
z^U<HaGcVHT{X87-=mCaKVFT<R0iU*i<MTP)r!VMI{d8fzDS?3*o3GA+nTkEigwu}8
zDr`u+<VKHFTyUCY_v`*>U;45?LnWBle8Dsv*aY-|8a}DX+P~;T=~0IM^P768+)G&j
z1Sh at _@;{!vL`U(}03BF&H9EhYJS$8CblLj#hxE{HAO*(8FDv~6&u)&QmubK{%S&+?
zu!-fFP4U0;?4jz%0eCjd{{zp4w_fJi at h?nAhd!jX at O)mU1JV|L3tK8QR6i46r2rK-
zJ{fZVv|tKEEJ03F8*W8Z^s>2oqMP at CC?{A}HQz at Mbzmu#?vXN5&*<nX=>zHOwqh#e
zC$)4zYuAD_6ThqLe(87aqHmX&u0{z<&+yFW`tnUy&<y{U&BUJz{=!>PxHyE7PWCAM
z!Cho{GWlw_u=HDA)MWjSeO8EbXfz5EO9Q3ogw8}7)}mA4-UZvN3E7_6Y1l1C2U?L~
z>sbW%IF9TJNSpC3zEi+Ho*o*#!DbRVh9RH8q5f&WE+}Elv7!ySH3bjQpy*}Md%zBq
z7<GCS#I{QRl(t5DAIWuHb!tdQNNidq at 9GH;(eIniZ<yqA^(?>j=RRv`DkuSgp;tj>
zE7FJyCfOQ5&`}!lL|w^ybi!S98T%DJI?Jm**LShBYmybgttFipmV&9aXT0b-bwPNm
zG(4 at 6y85lB=>D2>sZP?}=P~K3dTT+Yt)QV5HHf7J_p9I>TAkWNUoG+x9p6;6VWP*?
zA{tE*%k6z3a_M9^hBVn7y at qjtf`L<xqK`-&gOJZHB$v7199yr0L_vxmen=W5y5Jt9
z71H;D3wKE0IpF!~SstYCV(I at 34>W~d73=MNY|N~|$-|1*d788t5ko+w5#|Q1`I8mm
zgqZ19cEDO7Tde*hLvujzHag^iJ=~WB!O-Z4kL^?CHW!c=4RxuDp80Vc96Uu){d0&y
zUY$`?p8*>1sJiNku96>WB)4vg%H0kO<EQo2LTlJxCsfbiDy~}1 at YL<w(uZgI8L7O0
zm64f4n)#^kVJ%ILl?Xk>Qq?dN;97F87Iwycg=u$j at Bo<3zvwgJ0E at fg=|DXzyj5!v
zWll7a%_5}hs5QD~j%WrQ_HDH^T`J+H-N50YIBvq at U7s+a`kSt?5y_r~($J>bESpOX
zNc^bcpp!{B$kY8&i)V<(Ceqr}WoP0<v8l11AYuSY8h9X(TkJZ<RlHgA*^<-+p2vm=
zwFWEP;9blgBl4((!GmHBY(zlsJq<`@CS?;j<Sw~AtFW||zL+P%-4ELi_=E|}wjQOm
z7Ua at x!GDH=0P5W4ovPIaN^AH=R}f*m8l}{RSu4V6SBC&qsCOO=%k~0!)IoUxM!7vI
zhj#&?oDtc)gy94PF5?$dpq^AU4b|{NChwLWw(2k5S5bgAv^i;cwKOR2Od`<)FYnY(
zcEhs3x&tURXLnW(+P%SQUx-f;B(yyn*Dq7AAdu87g*PyRvkT)bJ5bBgQfAjC?yo&W
z`U__Aa=ik>XM_tkchhB9flVZ6U`I%{r*;QEOBzy0`70-ehZK5;$@mFsepsS7Cedqt
zqIYO|t+b5_Y4ZjjUM0&sr6@*=cbgs%sAYTYEqY{^w9T6)XBxl-LPz{#(RU~Wct}Jg
zTWT*_;lC0a;3Y%Fpci*@H&DSo5L&!ewyqY)O4|}6i2$)&=Ix9?xu!N`yfh?sHyl1`
zSzQa);#zM|HLqCV7IuoN4fSafl*{CaF7j^}nm+j^-)1Mfx|JtiBP4cu6y6emT?u)}
zrDe5U*S)Lk1vQynNvOxY_LVBev+IU8u-UZ?)zHs~4&N>})RKVdS0$ku_p(rHgC}@O
z7)AQ0*;W%a8CiA1yDAY8Z`NLlueDO%RY;25c7qL!2s~)Fle2k24OW}-WH2FVD2c7L
zRu**RN*T{)#<L~5Ho at Z|uro3mHR*bCtFC)@$)#2D5V#`~=g<BuvMpxJyvW+LsM;m-
z;h at CY*J~fXK4i_hf)ruBce*fOXzG3|P3|6p53J!*7%7}JH1#N0X<4g*{<)Hb`ozi<
z$&k{oc1;qB;H3<fz9n4 at SF?G}z2Uv44X1b<+g{+ZHL6zazg6~1?HCu at NJ4c3X-gxS
z?0O%U1;{2C4}56|=*7xe`ft|=>;+9mnYGEohZqS(Cnz>E1j8Y8BcWRO6guc2d>|d@
zLf?M!DN6(7G|4sZS=)Sg@^7>wyVOFoq2m?FhK*!84`lI<j_FVfiF95NSgd)=x)U^$
zLE_-nf-%FckMSQrCSul at nGi4+<S`SzR||PrLP{?rQ**>9pS_q;NW8NlHHQF2PjD6?
zowt#^(8(PA(>}G3%n}k4!3q0f?c15Q>E!fAM$J+H*VKG50-?+eFNJMn^TJ4Kw^`eU
zIr+_UMF)n!=RYBf;rv}Ol?P(jAph5ou{8QY$lM2WlS)EPl+c6DB%eA-W|b4r{x8c(
z3o)0VWF596Bsp`(my8n}49AdQd{W3o6f{8#pb0wu&{ufU;If4eEkcUVaUYFfL&QUU
zX$WL^Mb0tH$VEQJF`4{4Maq~hF82K(!AO9t6dvv__7M<hjm3Ieuo2(S&eC1%J<HEN
zK9YpiE^=@tKN2p-W$hQi(TmIn(+ODYT{I-~^NP<WMBE2IG#z)?XBw}DU-g}}qUcmq
zRNRNB4tgK2uc*JeH=u*g4qmmoHwU70Lp4LrZn3x(JhN6Xj}E at N>iwl|_DQuknp)rT
zaVz;3C=_<_egjOEX2Ho+m=`aluuSIS;W>&6Fi9qtyj{$PY4`<v^&U<?EfB?y;d!Vj
zxbhw!k_t~vw)!0H8^^XmDD#)NkCN~t49*iu7HkOb9@%wr{Y}<J6wBHTRgFHm{3_V2
zqllnzSf2yib<>ET1yH7N%d(UdwpI*nt&3i-lx`yuA_Tdm)}7)gkYD}OdnG@`Z{v_p
z!&fTc9L~nq=l|%rGrVoWauIgh6~2<camcO(V{Z%^dc#L}19yukg^CC3_yt36 at Ps!!
zM|LH7qo8om84$EUcmwABzy-t(9)0#P$S*qe`ymD4))eaaPU|soRQ{*l8oy;L(pK`b
z!`FP8wsA<x)YSB}bR6~W{b at Nk_G9676P9~F_qWUGZ`XnkUB)h_KlP3eXM at Kt_)vHz
zoMi#D?hK at SAlZLpBy)S5Dhxxn;&r at waB_PmYZAo{Rr#OX-W-Nm2h4T|Sgb-q)~9|X
zUK1`w>>ER8AKH2apvxU<T_IkQ9LbLy5~+y9ysmit?u6xhYylW8ry?;b%Xut8;c^mU
z=`U~i59dRb6lAK=Jk+H`Xe*lD%YYqxkL>bSM$E&lJJ^SSS0q<?`wxlWL*L;)ZKkTf
z?^v+tW47;*A?Yn>u^_B{en^-14g#Tva(>8NZ)krZCe~is#kOe=<vi(K_`=Ny^h#+r
zuTJjjAGY9VxF3fAhX98FhX98FhX98FhX98FhX98FhX98FhX98FhX98FhX98FhX98F
zhX98FhX98FhX98FhX98FhX98FhX98FhX98FhX98FhX98FhX98FhX98FhX98FhX98F
zhX98FhX98FhX98FhX98FhX98FhX98FhX98FhX98FhX98FhX98FhX98FhX98FhX98F
zhX98FhX98FhX98FhX9Abe+z-s|5m6Mfuww9${;WX!+!)k)C;Ho*Dp}BiU$1P?`R0)
z3;(fN6fzf{W$=6e&t`bG!?O>bBk&Z!a~7T|c$(q44G*R>6T(a3zX!}b9SRu?j})E|
zcp~7L3C|*Umcz3Kp6&4LgGURGi%=lo!;jbhqyFtj{{Q at l|HU7${lEQ}%A_%BW*f7e
z`JCCo>|``dI+MZdVltV(F<&r$XLd7tm at H;5vyb@)v!6M at WHUL;LFP*)mpR1zlld2;
zWps?5Im{ek49rpH7z0-VGWpC`%yGuVe9e5r6flL1nfaDE!F<P at WKJ;_<}`DLDPqnt
z#mx824~&&5VQkDf<~;KwbAc&k%9wKIB2&TGnM&p-=4Ymgxx`d6mziIfUzsb+Ri=ii
zW$Kt~Og+=UG%`(0Gt<JfGS`_KjDu-o+L;dKCUcAVjk(QqGIyBYnJ(ro)6Lvt?lTXV
zhs-0Uhk49AVV*L*%rmBsVL+`gfk-HkJj6!=Bt$M~5OPH#L?bukjy#Yj5+g6<jRvD3
z$OjEY!_aU97fm8xG!ngnMu8$(8ImJEG#V+;7&I37qW~0$f>1CThh9amfg0LS6o$gl
zc=S5 at 6Pkb`P$Y^%6VW6z8T}beLD47?C877wa->4ZXa!0^E72<SKKcNCh*qP&pfzYM
zT8BPD>(R&P6SM(+iZ-H6=pFPfT8jPy{U=)X-wOX3+KjfKzk+JrRFsC)XdBv&K1Vyy
zPNYHUC<E<6ndoom3-oui8|^_^XfN7_{(<(R11KBipo8d3l#33bf1-aOEz%)9I*g7W
z13HS1AtTB|`RFTj9GTG9=o?gk3XvIoi%y{L&`ER(S<q>81{I;Rs2F{Zen3`Kf^6s<
zI*)!t7f>lGL*?iqsz7$o^!^k28C9W6s2W{Hzo1{y6?7HVpjuRiuAzF=fErN~YDO)n
z6<tR+kOQ@$cGQ7xqFd-UbQ^V|JLq at Rh3=wmbPwG}570yO2=$=H=m~m?deJk~hZuyb
z at PAbam+hEIs3;zZJ*`|m?xU2%u-E?t-*3%Sj>BK at 9~L&AHM0(MKQRIkn-NVw`9V;2
z5IPR+%?HrnxzBZafTD+0TBCt~_{3g4wJyw*kRWCXcEi6T(7xgS0VcPzW*4x5M+SfX
z at JQkDgGUaJ2%cg65Bx!KMo>5r6l(T2dcNwDl^?DA%jzg;XlPiZ6g$2A`-6L^(;e7m
zL1KA`cKRMKe8J<(;s3Ar9>@%wSPF2!4|HV($Dt293Vjv)<L5kld-7hG2kie({U6AC
zT&9hXer(477%uz|<%s>q{WflUv3)Q6WBVTX=fQs${NvKi{Wt_T1ULjZ1ULjZ1ULjZ
z1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ
z1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ
z1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ
z1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ1ULjZ
U1ULjZ1ULjZ1ULl#cOvlr0P-DjVgLXD

literal 98320
zcmeFZdstJ));K(S=fWi$C5Z7tXhSdpf-PdL*H&&t0d0X+s#ebtG*)S~XzlSXNwgR)
zr-f)OP}?TaD5T~T(y9TZg-8)FREX9BN((urRc=B=MJn09H3_yom+yOi&-;AO_t)#=
znmyMwYu2n;v)0T6PYHoM5&lOYBteMHLWnCA3L&HOHH1QO3I0bRbZdej1^GRq6Ccqj
zk7$aqpG?^3$#!*?KMBLvCm;cOVPHUj5 at uJQ`^r0gM?`w~b8k+tLJ8zN_^J=e6K^}(
zLqYgIaV}JP at YO)50C_11&)5lQ=>$&!B9(|`e1i9XzyHgD|K-5{4;=U|*7Jv0fi*Vt
zek^(pp}>fFiAz(wYSy6^SeA?dNx0w_hG5=v;j=?y{%z?yiTlpY^9MJ9)h+bC8=8Yq
zcnd=^n;R+&75swN^urw;fa2F(uR|CE=>PLyLo8}>^K6S1+=&f+6dNvyO~{K~r;JT6
zb&&I)_5VGm0UEmr?zpA5xP?A)3qS0Z at Uh#v({AbK9pwCb?f*<SIr{I274*f1j>d+E
z$0j7iu3HzIo*pZa#OCG2DwVON|8KN3xp{WD3Hsbhq5o00 at OZa`H{8~}<(9tJO_Jl5
z_lcYG3%AnB|7ThR<_Y^`C=sz~`afy=zsZR}G4}sK(`Y8W7^C;9Q3gfKPn`9b7Tjip
zhAHPUVpEii|C^5gzo`@cKcxRj?{9mc(62vp>|5ai#B^jENP2NSKr28qz%_sZ3Wb)c
zQK*tdFYbgit|%G%o`k$S07u at Jcp~V<CLeln^dx$*U>v>pj0ZwWh$|=3i`Vez#k-Q9
z^QwmW-jG*|@d5M#xU%WRo&m6N0$vKoDSGiD06xS8 at LdX!0Pq at sH$Vu$Jb-8b1pN*`
zzncI80oFmD0Pv3nqtK%e_k?)3h+bShjb0o9-yM_b#d3f{07}S9hp(rdh2|kP?C)&k
z4KNvi4{#L91 at N5)@B_r70S16ZD?l;e*6<K2h3^G`w>?p4EA+Jzpbg*`sPmc!&<A{2
z17Cq3&<J0p5a@?KCju=rP2WX`M4Ny&p^%>Dk9o;Pw|(iwx1XjLm%?}u(6$uvZaoEK
zgS?dhg8=CO3<15^2k`bn{b;BY2oM6W$rIXv(iwm{1qu`z4KxHv*k~a at H2?(=2!H at s
z04 at L_05br*0Tux?L)%g=%;-Z*B>-)wj{=?Z0QX7w4s+?nagiVwnP3m)v5_mlyGPl`
z1Y_$w!bZ>l88`?XfxM&c2<`F)zE40%paA|tc_2Wz2Lla2ngeYo!q*p|8lV$k7$6kt
zgaOO}cnM$t;2ywzfP{+(nO*_;4To{Yl8FA6ro$u0BMFSVI0d1bFm5H_nEVkNmBRNZ
z(D63V{IngRMeq#;+T<XgbOuoRGY|{B%!YIb$gc$CH4who;ERSinFsSZ%}$~Lknmz?
zy9m;`@I4M=JsN>Ry;D)>%@h=R4Zs9Y`d1V>8T$7GxRnoO0M`I^<)P4%Fc%1Tc=UY~
zx)LA+U>X1)pm#qC4cv-C1pq@^APsN}U|=J}p}#{wQ$6%4fw%$710V8$)`TfQACw6o
zKNs*v1O8e70YCzzi{XomJ)EWQpil$gehuoC!ki#Td#9t&d62gYVA(U$bAFy4Xc?4O
zL)mxG?iPH1gK;}_<pA&tBo^V7eg)t|h<gD{Lx_=q(AU at smLN3o5kgm>&LIM1V;y|m
z2y}=GFoi(nP-piLtQ(+f5!9JBj)9aAUo#%~oP$D-9{`%*`!(=m5#-gv*E<t<1aK07
zk^^tEP^bjj<Yq&C022TM==KM=32_8g`5E|L0O$o^LB0s!@nIBt2-=Yl7Xo|@@gV>T
z`k4%%91r9lD?ru2(`w8EG4Kl at 7vK=UJ^;Z66uJS{+aXx*LtGU47=Z7ELYD$002~EC
z4B$P$Vvt7~Z~{+QAq31t0L%mY3y1IV;||@6UW53uvHW?^W+lYe0faL_U%=N7v5*U3
z0Cxlbt6s_iJOFDzCx?QLiH7eP&=bWF4*{M2yc9{Z>@N&-x+1CE`Spcq!%hPCefzBU
z?V%3)BI1ypHw`s2QOZI4wuAPa=fdRd0$$EWNlA4;8pF*sUOqR?h1`VPCd=wwl_w)|
zyE$4nSGUP-fo}6;^`6Ni_MOk0CMLP1yl3C`o?ZH$J^ycZ)!*!!*dKMerli}qrQ4<H
zkXmud^`l#^&qttH>2?_d^$z>JV>giMV8m-j at t+iuo$km$7ITuP(yBB&#W_FKRkhhJ
zebWxqQ+#6-@*Tj$Lwo54yF!URp3G6D*vnGw(sj5B#0~Dbd&}0 at dBLcY1GOD>s~9>`
zL_rGL=}2qZlYF((WUVD<x;yv*Wqs at pRudD~rp9ZMf3H9ONqt<R at p9%n>shzCj`cCt
z&O<ZFS?(8Zcl7rpU;Gs5NIPf|Lo4XdS*F?Y1V`d?c8am`y=noPFQ&ZV6Dyui&i4vS
z4TDae2NyEL9(gf5aJ!D7sU)Gegn&SfgDhM8>?KEjQL7Ln3br4<;{sp9_S2k)PsWD(
zwf;tr at j$cs$(Vo#ny>#m51tmsIL89~aI5jK%Y<R|`v7m at +msLfkcD35Z_84{^v0#j
zC~=B&Owh|?=mq#LMUlcV9a%+)l3i7!qm*m$9%z-7MoocFWNO$lDvQkWDooRx2_31w
zEElbH4dL)jIx_t7!74($#ubw+PIA=|6eold2e|#tKk<lj9Ao at j<>a3fEpuIBm{7&k
zF`9 at DmdJ&Cf#5fUFcV^rEP87hm1<n7V_Su8Yc4ON)Mi56C>IIc&Uov{1+^9iGP}!d
z?n`~N1O1_vxFvc9@|oRS8LE#veCbGM6<hWFzmcEhB)>2%%L_yzag0Whuam~HIrp_b
zaMI~lHTtLdrhGM{#2;gc{<ADtmpzdMYC>Y^D8+P0L#!A$HeVb|#y*jS2t<*&Cn<Jn
z6cFmO at y(<6pUiWjW>j at zl=6u@;YqpecN0Xn2=*S-o)o%EQa&H$dAaNx<>%^^lJpBl
z0H>T2GEO4d(JT*@$UGs!Tk5s?wX_Y=W1~i;JpKD4!XH?HUTI<3v%{{^VT at L2;Uk<+
z*?wS!{lFR?A2cE0a~DONgd#H$f)1c~9w#((NAv8^&@fMkBwzil4k3(@Un^4{qWI~D
zaIb2WgqlgcNWDV65fUX-<}P(j_dgOI&P<MWj}&Eu)&9o&%8kc#o$kG5`n1*4p9=}A
zeVqJHmoSke4d~#6T|5ammXeWCxsVkplKRXJ6HDeEU8@&o)J??luYGSRshtxZz9TX!
zXS++bLM)N&<nD=%6iZFfP$`o_HgN`<gS0PUipj9(Yzw4a{zzw;A<g(BEjNlYaG$O~
zWZe%ir+7#)jWpInPxu}YHyXyA5_!!YK3f^)pAq=gzFXl!&!De<FOM1xyGEumri5=)
zrcTdTd5)SaSs_?;E(~%UEhsYRUB|ll11H6Z2pyHq8cjOkDOk}G_fqzXFmXm$wB!j+
zDu{9ix(eI*E8#<3&T15W>QXj+l$xrJH;&<=rc)Csrl0TVEWxNe398BG6$o86Jp-|V
ztbonq;Jf)6e*t_-YLlY&%zcs*4>{$Kv)5%aDSkw6n=l#}23dh#lTw9LoYlrm*DBRB
zG72hU_aqbw3M-s=Cc`HSTJ4&PYiS0U>FrAM%#8Y~*!>A6K|__ZRxo at p?%fU=yNC{#
zCPo#hBdizcCKjw;Ld<uqVne!;#*5#mG>LzGvMxZsyhTQIKVG^6kf=8Z;?>qPtP3HU
zqZU1hURG;1lb^b))mR=B8w3!_cYJ)?p%r(4R)n;1nx8Rhe0B5v>e~^PsBz{fkLvpO
zj8PPBFioh4;^Zms=NTgOQSRy}x4eS)6j82FWSi!1i*gwd+#irf$fDT7DAvI6d$uSh
z6j7vc+B9Pn!-4WWOB6Ck5!Lk(#wbwyu-}5LTvm(Lk+Uiv&wCfDtJm{^;J-R(U+){Q
z7XQT=eF{dn)){?zY!1uLJ<k97vC&K$Llz5-k7Yp_I<i_`E!&)7{lOJ<<Q~i%z+c5!
zqF^isR-X5Af$Z;4a-17AVLmy}yLj61D1EzKc|xgfw;N|<s2d-f7I9{uFS3DxW{9t$
zWG%aXnn8GhK`|-TZ4Tud*MEmIOf7klpgfj)bkmgYBTjs!oQgLcUJO}r%Y9b7e&qUE
z2CQ$2MKRBO-8qLV_c}a>(vd!A#aGZxtwFe;lH&WUXywGuj_YWH)=OR>C~7&Cyk-<{
zxv-z<7*$O18gxAlEfUpvBORexN-R-OSOPTVXs!EW?Q&wSm%C%zH9)#oOHIxqi at gog
zj_)zwu$ML}jW_L<kPM^gvFQVj_Q&J47=#Sd>V?)Yph`!GULt0)96Th5wWN+IszIh1
zi(q9yQ6zC$WsM-zHIv0SoF>9ys3x4iL|K#2^=y}1HT2A~mZF*N$&E}3rjcS_RvEZU
zBRG4*E2}xtrIlRqNhs~@4!>00FxYA08dE0fh|6W-FydG at M@QKFCf=4-5hMMr<1W|u
zbhM2VwL*2Cb`(T>Atqn}CP0J3X(AkkTIa*opd+dnniAaY>d`7DR4c~uqQ{hXED<})
zFag>GS6U4?GwwG?P#n=&oY*r0c&K3Ud7RKb0(8_u%{iQCAHJ*tjQrn}%4mM?=?i40
ziB`r}>xeDPDyFEwQU3w%O8ha7ci(4!N5fDDx2Jf)AoVHUqIoz!Z74Ejg3-?_HHEvM
zy7U|wBSw*a6I1=XZGIC{>pDAWt}VqgHHFvtHrMRuS)UjvE%u8OrfCiC?WTH4XtrNk
z=(2BEmYPuqKBtGeomS6StnqT)_n7kdaUt8}b7=vgqtvMx_0l*KOszg7!>3aJcE)Wf
zj4Q6+@{`^COa{f_beOkhh)!XGR`)>9(3NR1H7<rkw-ei;7HxTEvAhhL+;JUFyj|A*
zbE`+&Q}xm+cZ?LTAx#i&Tl6bt5J#-SiN;^f1yBWkX@!b5S`qXj$PwjM+*Rmqn(IXD
zmo|nN(8{~?HKBJV5Btfx(&*vwj{-Flh*ML^%pYhdte0Lcd(w}{AETby1=)H(o<@pO
z3p!zT31iJP=Pbqj46^<)NE-txT`m)4<2oZ9V=WFi+INh#uzrS<Gj86NKH-If6P%r?
zb&qX+yvW+ewkw^s6kf9TZQgfnIzsQq)$kqoT6L@{Le9t{M28&dA9XBo9!lQxBQ>Rv
z#a=^$?l``ka-m#(<eL~Pnenp|{>EO#jE_F7=);_FIX-tDlk(a*o1n<_cdK_p$$m0f
z4F;}LuO*;+`^g7-k{t}oPH-|}`=tQ`vw$J@{(4n;Idjv5mD3I#%gpi~_P_nyYw!GZ
z)wI^x_0hLR`|dnvh$?uk_QvIgX!)y~HXJn+6u!1{{qT+r?~6BYxczSHp`pLnAML0=
zXgGSU_h#Fnf+O;iUpJJOAKi3f<<}VD$>FbV*J6ZUw_dEjh7r~pt`^+F2(QWixoHR^
zytVRP*u`*+aVWAj{M>nx-AY&Q;ihk|#sg!0>T>Szn)DMu@$hYCE2G}~*4n;1Ov42E
zG>pZ2lmE)+4xP))nie>FIQq5Jq*c$|j%s~v=o$N?==xU;8!%a~$=7e%@pZkSe8b9j
z!!8Ooy#IdIj>uZUgTr4mLpQstkGpVo7BGr|rzcmvp9P#t{?Tp#X0EO*XTH7=7#bhF
zmQ2=<@iGN;xQ>jS)kIvD!rTyr+$=wF7R;H$UR>%Gs);!LH5ns-BY^1BN6F+5U3eo5
z<)P{`uB}3f>g?uH93NA~- at BK`_b+9}x-RvKuT&nb{&>+HJX&hWg$a~L=LK%D5GrN2
zIf|DovYokmrj~N+{F>V;3>i1jUyYs$uV{khrw+-`Uw(|TRcL&;QkkEp6{z3N_|!G|
z#wgZ>;1qB)TO6d}y=Rx#@i#XKW!LTN-c+Vuw=1S+fK;01b9Np+W|L3Y<bQmr0?Ibw
zGPSd80Z0DvF`LQd%<Y}?LJ!ZKb>>aEu)tsPrc%~whgx|7N>CWvr)Pu-Gj`4%KX<Mt
zY<+<9 at Ud=(D)#an*k{B0UJ8;9jBM*4`IMn!mT(J+oYzw?h00tdVVArkFD)|3H7OwJ
zLag=;F2j at LJI^mB>t00lzZNeU2(Iq5aPRm at gi7TX%IYi1ddlFAwNxcp7mxOhfF?5d
zcUS6f*cHJUK81?6GIY$r>m#i$!{cxJ4|G&_b|}pqcH>N}lT5}f8P+w9)kO_9jSP>k
zpWb<w3wnuSlL$~dHIEL|ZR<3-OM^$j^!jPtg#~Vf{8s;g`s!xWSPkpXG%WH8S5?&r
z at b`6$u-nDpqADNhyv!}0aW2 at SK$Q21@=t0Cl+;3}k`gtJDXFr5jL3s^Owdsxd2pjc
zM~NE{=qUCTI_4>K#%MUNV+GB!w$r<2&2+!nu5&3Ms#rh_OB4w`ni_R3;+K(WH8r)w
zrH-aSTQnmbTLb~adIiloHlky~9#4U%W&IwN<&8wfgH8IoLO<(r+s~oWE6L1%(5CoH
z6=dcaT1UlvLBsgjUGI<7yWAev>fdSlW7y(mJnXnfH3vovF7=d-SzTF;cb?0gqGvD<
zv=2drfpad}fK!bRbSzOcx?bdk5n7+edD-`C-ud^o;u*N4`<}C{H4dlT?{$fx-tBS2
z{+&J51N}<pw&GBwna?8~OH%`56+^%PdqKz4R3p{u5pgw?Siiv~5HLg#Fs)Tc_3DVY
z3KbIuuJsCieJYWw){zFnIum!ar`NGeSdb2Jq<eI116SmTS9BW)ir>I$U>kgNjNnSq
zB3xtj9UTKUB*#`;bX&)yf;9_pe*3Sg`6JXq>wKJW|5e8ZgnYZQI-1{isfw)gL)G6@
zcZH4USmooyKDbWft+HoEpf`?qDkORB?92~ngA3=A%!x3HLD5J{AP8uy#}gs0_o!m|
zm>N8q*rJym{dEGv*s#N1A9-9eb)<|x0t<tqV>{<Wd>&6#(NASwBh=LVi6iQGNr4-s
zE_Cr7XyJ}fRD*|(T|d#l>*9=XZnTXny?`eY*2h+^hKs40I>LSn&iUYcpm;jPspkc4
z>tM}fwnePm1_qQh&^3sdKWlT!uIU96HhFF4t}9&Md5T+-$UDYNk5N*L&D=u2Afni>
z$z at Z7VOF5X5WGMbNEUG9E`mZ?2U#SSY?N7#(H-n!u7PNBF$7ybqU#gmXU9hexlkVQ
zJKMZ39jUHz2_!agm!{Mw7K`Fd)^V;dUQ+WFT`xM84O46oT0a0UuZn_!OcUX#bDxu5
ze*}8PWB}>sJFHfJ_IQWFZenWA(=UJ>2wo!?)FjC1H1vY|&2AlOoq~>O?U2b7<)dTm
zW5t at eP|Vw7pUHTFTSscfBe~ZCZK<4l+TfvYe7f{(1Y}MHBi=RC8o_&+Y4Ga&iF^JV
zQN6J93vOp+b!R2F;;}XtbTpXrvR2ymGi`fzv2D}hAB%pe8h3nQ0j@`Jv<*-U>PP?f
zVmMZVjpM at 8tzK2*e*7jF5dDKod4w<qW>@=EB1X72_qJ=iwlgmuJspnvRn7f^JFarC
z4!6S#f2Q33I41*}+~B`-f at v=d?*s6Ad<Qtuxz|chD&_9jv6#n+KK;o(a0I?CU=)(#
zabUUMOe+ at Fdr=&iVU4?MvC!a8Evv3oiXOT+40PB(R(fp`a#kE?trIDilA!5L*ElQQ
zRNRAoYm;!@OUk88zAc1}$nbDUzRPVkN66Wj{J>r+;?9_k_kakr$$t}@dMP+2A=-0Y
zbl$ohGD?*GUUho345DL^4YGABsuQAR;o<9Ifo43X;DeA;X2q9LuCUvJWMvW7zk!V6
zLcj at IoR5Y?%iV8ke<$B+ciCsBeC5K>!F7*ZLw`EaM$Tl8)tA2dXzo02f!Eyf1)Z;Q
z6O=>N1H^K7(~LFHTN4Yc^xs?z!sDORgVq8 at h<|nY9jo{e#KnH}e6Ls`=BHEMRx;$w
z1i9>bfx{`$WYNb)h$hGRq$q?ojjST;$gwj-k$~oP+cDwbNi*TJa|R73vIz<6*biW-
zImW^i4bs at t`jgB)^RF^%9fmZPSxrAZCbL%YA2KVZ{#9nB*kRj(z-QQk<|;hEa3MrL
z(fdLBxV`{xoCkI%L>)Mz!sT+r$_bmikBf`_u@|cO7cX=Lc%jEP7Ei+^r~aj6qr*Q<
zJ1g!6ERR<^ERJ6w+N(YOpmQTv$1?c0g148}k!AuH(A+_rXD$}KPJ at w<%(D+OZZik^
zWp_yMd^P~7c;^5Wg?TZ?*He?HU}sOX;}5=Sc>l^*jr+gyRpb31d?iKK=)J2GUX6JD
zYet9+>>^O%O-%7k7tYZ$FBP4j+&C)?!aP|-saz>{%`5e8uH^a at B2KtOk}r!x9BuLs
z4s!}UuL486iqS;&u|H)NMx;~@FK78x4<}X+%SaSI=Xl`YH^Uu_?U6{Kl<b~)x#Qud
z!-Xu$*ROoo#?FXW- at Iau0rSLarjK=_f at uYAE}LD7Bdjg-^`cOIr0;;t_*|+nv^?=n
zN`sVop>5m7rxyv0{NjlHu7k>9>KV%`8JbIU2}`E_{Ir3rD}-L|o-QXd>(H{_PFIpK
z7g6gre(t3OWabe>`Nk;F#%Cw+?2~Rk!~4f1LkeZmRJc&FF&ogv#m|6*x{GwQ^ysjy
z at j;0z at 2?Y+XG|_TJWRzEq^RRFc&og1Zj?!1-zq-u!hRXS>9NHaZZyu at _)19GfnjPj
z)G1DcX?YQ~Ce|DrZpc5T4bo9n9-w5e(k%yuOFWYIOo-7SvyfY0OWw~)zQf$j8hmHi
zBJ|w;N(S$lNz%U!XXQ6BTS<eoYsavTBQB)N-W at LB6tf3ljdM<~@+g)X92L5 at 3~N57
zi&<q`hJCaKP6_M$g1XG*D8z$PSZEvEG%Szc=#YE3Jg{WEBnzf5=S!G1$pG__yUT56
z0f$%Oa`Tm9&c>;pW4*1yy(#F-$*7erA4dfha(v`j<0PL}qbcEVZ%(TQ9tj>%hd+(l
z%{tyR)@B*B5q$_m7}!2qOu;XMONWQY7fW$@SM>0}w|SwJO3~lx!KmTg<1JT)>z~~{
zj$>+gmQwo;sKSRSAMsgQI&Zkd^=5^^TV3C(tmImY=$hw-3$~R|radh|!;0k at b@%A5
z(BV7zayIoervS)7S5yrwG&vlIdU(Ny@~S?Q7phfSX94B)EaV#w_5cg%Rp%F3i}N+1
z$S+`6Z5*;b3p#OLiso5lic$%OMb9F?sl$PwucT9e<x^(0lEu>avL at y>-(jcsQs`&7
zbXzP1<Ab+wxQbaX-8b4s=_rw&?xKcE#%121sVy)W-qbq-qw62BeJaac#9z=gtl<{c
za9e%}mE1n0^DwxGv~(AO`e4c#T-4v+8mQ!oO6Z#39zu- at o5l24o=80{ccnah<dUBc
zwvFTY_JbXkDE^4fw>>OzOWrd8mMmGPrycw7r?lVDL$Fg=&M|9*s2-a?taH76s at o4W
zelSL{A5wKcv_6egw;qb0h71t|u&|A%kmfU&OeIY%k7=Q)wd`iP%0owKKA>A#AA(?Z
z-zf&cD!cKpmAu?z-9{Hn%UT~=bLp<;hd{mi?J2XI3~D7U-`}F1endRtKJpX4L$|We
z&D_JOqNXCB at Urh8BCV=~WAIjL at tpWnieWmd@mNRxSsY@>cV>$Z(`A(pOE}3|ODmhA
zIDKbcZU6(h>|5yD!09^ou(T2j4hZCCKwr<|^x-l7y66T>Zq2!e)Km2oZ1K)Ke!lKu
zYs7B1-BT<L!>zuyh9OzQ<K3>i$G45k6w?N8C!>5SHTiVeXSmO_8wS=B{QAp7ngeuA
z0Vc4!&AG6OSzX5-j?tMyQ$Bwds!9;5FX}8~`_li|Z=A#nWbHICu=H(W9G*MAn3M}o
zP4&2b3Q^!1ZgF2HSm!%%ipRdb0AWAz%o6Y{=lMH*%h0*Xu&dZd-R__9@!at{=U)lC
z_$S}eVF18Bkt?GnP_9{CS;G0yi*p}6t}hnmwvX8|*8Q+;TQy)0P~+}+-4V7yHE4I-
ztDelgS?pECkS}Xu;;T!@_gg)77pH>F2;XRl_P8i&1c2Xd7kgI#?;tDpSTQ?ywR}M_
z51KULgK)O!H(JNmL#<_+5jyuAl<$FqoYjsR=~veuUpuv_icoNng+xA;orR%_NVuW7
z^hvLc at NWj2j<!YkC$ArQLVLu5hbo_kvM)UkPZb<DD`qS at p0(1e*!%S&_hP0&*u)aA
zbji&&ycSvOoxImhF>^OiWPNl4n5e=p%};R1Ny+-^UwBO6mrCD(?XD=eVCO_geC9jg
zvuig#9r|U`R7csVfQL!0<SOPuc@`--QvLe;+n5VUuEb8OYa!(S-7WJGosh6$`owF~
zMeft*JU#uT7ldCPhdG=3O{y!I<jRn2<i<neb#P6>%<QL6Ejg9s%G~bCfKs^7K^wVm
zKxz6$fulmqFZ8L`-*=QUlU!NQ7P8S!E>wOCQ?QYEQnT&`KJS_?q#})@vmQL4$Z-FI
zFg^DlIL at RggO1g~*k*fB#kQD<l)(pz?{kgc7c`uYUs8BF?2 at OAot|`wfB4%4B}9JD
zgMkn8isVXHR5uA_u0k=dAofcrhKbmVQQdr0$3Zc(QC%Pc4-NcORue<Ci!O0Fvq#4i
z-vFmt1OWqlaDxlw{#J1544?wGL2yNwqBN+NoHlA?r~8<UG3i*<>zF=2n`6?gKVo+r
z0)_|zrsfBngn+S$uwJ9#)`VH_B??B+e9xmCJs1QHU=aKUJzk^ny1>N^m&7|9_#avT
zf4?Oro&76)yb4HUYEp4Q`~4iFBee#nMuuR_J|D_r(qlTX1J7t;iIN at d??FM`Fl{u3
z;NfXj;moPFx)#V;gNI>V>u5g^(*!vQX5&1|_Y%jM|2D{1G(O<1vgrYJr9LJ-xv0PH
zJE#?HHilS?I~=XILM<cIdVJRmwK`z%$qRa8F3>)Af#jG9>_bMNl$-};IS=BK)f~z8
zI?vvApKKjV7B3Nh2YiA1Bz%Hj?*&d+3H!0Tj+LXg8t7wtAX9wN!4IR+JI8D>Zh{V3
z);H-A_Vl8sV4re<*#TCt0Y^1m%%<Gln4t`U`7wBvyWq)i>H?WCC~@<H<AIbAoJFv@
zYtkOTmG4aV<JrCg3RZe at p}pSAD1P<a1r}UHU8=Z&Cm=udfhzTZ-c3RYO>Y_9k&kvd
zpQ#qF at Ey>*Bctfm<M9?HZEnbDzxiXuufKkBOYctjXp?<l_kQ3rViT{R77RSD9)WY7
zNtW*g7IBjJDy<f;<joja<Y5%8(2J7%mOa=sBhvU-M>baK#mQhb)zWYeWD&h)79|Du
zcXm`KZ-GlAy$FgHd035f@*WqN=rx-tDNX*C{P5>#j<TAC52!7o8ai3aQixWoMJv?e
zH?)PSxepZL75c_uQ6-)1&eV%u-?(tW7%pn9=o`AXq6zo_xT)o$D*9%-QS{oI$0t`Y
zgrXHqY}iG;TuenXeld%ZE#eiw`ZB2PaBKux0qe}?C|2B8I))6h#3c6tuK+uLaE(JI
zd|kb;vQ!^o4Wb`cU?)I4oqjxH$yqqOzb#ySFYcnzB(YtQls0=SsEkrs1jp1VS2lNW
zu at zI<>WT_gc^!f_*N4Y%V#N-9Ij;Y6MJH%Sq(Gx~MkFGL7<Xt?5N;CVaE(<7VS)yS
z(IOnqvwjX?h!}?g5e~gIpV2vL at n`sy;Q1~B{4Y{vy#KGS9g<SQvIuxAz||4fm)ZD!
z%Nh;$n~tkZ%@pjFfGl^R5Ka at JI(Ofq-r8xAQ6Ak|b^9cBV-=|W5RMTxfwdO6uoxd1
z;b)5ws98uIA=D8H7NLmnm$67$#58fRUB`gCi4aQj8=LZ>NM$g{EXJ)uRjhUO5o=xc
zlk28M3xvuzwJ{E*#u3z_V6D~8ZeqS+?juwm-`A_RmY$bU<14fw9B1!V5g at H1_>}ko
z8x96p`WjEZ13w6}WA18 at y5>+)%~5?#PFLyu?dq-R at 5)jR-S7T2?aj#Ra4WH{iFrDB
zoPt^*qn78%LuGM?VhCiWUNFb)@G<#lUoYysQ)9e7$!6-ajNAXH#q^Wf)WPRiKSaeX
zRF3#V*e%5&c6E&>m8-u#Nq_mi^&T7Fc{x)>BAoIZu7OR`Hr|g4l+dBJh^NKA_9mvK
zMgYuE*91@=GHU;fEw*vUrvsPApkKAF3My;>Hq00VMD(Bk?55i2X=tHxMg4&9P}za|
z`5E^^2X^Gq+ at jBYv_|HBGe_bR?Nixo3N^;<eZ8ogKNVl%Zq5k7ciwOn+qL69OxayX
zw2jjf<IMzRAv}!4IMmOTn7EP-qPkL0^Y;DD4ncFJpz#XSE2k6$M^CsK2{$$2j-Zm3
zE2$)ug#y+3`%t^IP at w%1>J_?M2p0vxR1+*2!A4L?&Xp7rl74}7^?j(F*DuKa5bE6l
zObiQwWCUR*NCcHSxRQRN*(7L5ybrZIO at j8Hp<e#W_ZvKlE~zPrnL41SG7wa&<d$j$
zMrwcA()$t<Zc_1WwN?O?OE`*#$3-7o+$3tZ12VUavF`f?3b*~z7w-cdKtVPQG++wG
z!}`MVkZZ0vGstjA@;_z9nj<6q`k#2GL&nKCKiD`$cn;!nK?)R{vx>O4eJXvlKNfZB
zh!76-cWS)xr%6WBS(rSreYA>H*9btIzh_e%%1ar1#Ib%=e)kpXt$9aevc}8VSu)rC
z+WDrHV4mlQ-*z0wQMsBHHWqwX>wuwF1OnU4e&eLy&qFfxR>?sbm76Wpo3Sv7uCQ;M
z1X;p~BJuZZb?rnx<w$A7ps|?L8oha<{&Jqigb_aPM at YXPN>Xo4cvY4?M|~Amr$$WL
zs|ZVt=iV0eRi3(W7aRc^-KT=c#DhuptB>pXnRkuu``KW~5s_CXMb=iqE_)9yfT=0c
zfg%0XN!jni0Q8re at Y0AFZyg+UiuzRtb>BHeeGydCUsj82A>~tcYlvE;xd^mjrQk{R
zS!xpCxmu5dR|z4MwGy@~;8hUj6`U!sQd);1+J<Co6qJ|?N0(JBqw<W{%ub3yDe55w
zdw=e3HTyuQwmP}p!>@UWn$%mE6tgcWCL<|EQgdMlD}=9PVKE09FN1h&kJ)cJqx$>M
z46XJHK1Z_x>Dl{|_cFl_(Sm6}!BdShz^UyUf>hrOl^q<SUNEY+R$r8vH_T|PEcY`i
zCHfn{&5uB>s?HA?{HR5a^h8jqP}bnx^3{-KT2Q`zi1!y3$Ig4&otgnZlS*2Gd`_38
z51G|lo3G0FRD0!&O3hd7n&KfC4W-GTa~r1@`6b2d*D*nZXEIT7Vwq|P5Q at uSl#mPE
z;jNdCHP*P!zG_~-YpXZ^+lN&o+Yc$4`c%h;sKBH+6ud_kM^w3S3VB})7nT{78ycjP
zdzFjy??V(HOme*oDv-ixp*)CHDCJGHXO2Tg_P*Y?H$L?=HSSm6sp at Gn#H+yo$Q{`=
zW7zwVYTz(8Yn!az>+rV$)Ks`1e!;B?E~((b#SWTnrgIxiTA!T-WtkS5n#%s3{zO}6
zqT%ppylVSUNxsOyrreucq+5sb-x(@<XQ+HuQmh21$Xx*Sj5OsAXXmrzsg0K!A|RJf
z$+z8Bl?4n9M>yFu^02*9(qB#a>TgUM=~sV$SASUpSL5cF`>mg|R}rhYax*k5;ex{v
z7DPa_!=;ZfsNh#Cvvgz=BOTs1RF<cR=Fm-Km5Ywin$kyzpQBredHK9g at 5%d$xzJ=4
zH|8?7rDw$sQ7m}P8L at Qe4+%TNPKrXa{Du^aZED7LUg0<ah}Z%Tp4&JDBfaeLy;Swq
zbwXLnqM;eV$@7_dWp)!AZjj6q)s1<Y91te)`@c)nX|`(iP}y at sQs$6RB}v+}IcZax
zbswz?9V!cj#UR>8D^!v~{9sb;ALOIO-}OP_f3U0Aj_3 at dd7{xY735$8%#HPoGZ#NX
zGW*Yf(C8oBY^sKc-aq70CDvmyN50KJSVH4`+xid)E&f3`TtD-;*K`FZDQX*t+g=)P
zfmpC~Ovn-KaR{0)YI{=D4r)qL)VqqPZIqiLDjixTtq+Q+al9D=3kmBa at nJS>I&XXp
z+9{K*GTd{n6 at Bf*MlGLmY`z-N`%m;({=<9A8kK6<7l*V#VUZG9QiNE^PKrFBPW7W=
zLoVx6eHD=z)YR$kE}MRE_Vg2>%Ur4P&}kM{_$7F^al~S;bSdu{Puw<-n*!Hf`2KQc
zM!2b_VZZOd^mn~B9wZf!RUvmao*>ncRf<$!STDi)$SU?Dr>(8}@t$H#@8<lA-nDv<
z>0Qwtc8<EN@*ZfY-9R~<ulr@(i)g$TxE&~nOnpA`VmP=w;}Ir`fWYy1RhBv*zh1Sf
zYVJXnS%}$ebcA|GsBnZ}Y(u`y5n3GKf2}#`5q_ox6;6)>ht6rI^PXeN$pSA)!fSEh
z3NA4)d<qM_&dgaxC77x&J;$$NtXAh}#aqEZ1^-}(UL6fTtPHyt9$8yWv9>VJU7H;K
zH at J$aVu<e1&g=)*?kbmX!(*y*k2b+_#?3Wpc)Sq^9&$}#=f~a)a at PlC$buvldi~~%
z_!*`0cV(L$5BH!>BuR#m3`sOMe6}ExB!ndVs(>pI65&FDz+Zs(nJy$u@%&{1SvLbk
zf{thT?Yoz<_uh6R+`5ZD8P!kEFi$@~xtAoCq?BxHi|}ds at dDvfY20|AAw|D2qdc*E
z8*C!*@b>ScE$`fGBCu^VGvhWD-w|(u2d;PUTdd=~u(N`BWX&FRRDicI1 at 7>^OB-*3
zohZvurLW2bDrw}S`tLl?Yo at 6EJdHJ>TFqR|IG+_}3Yp`u?WBGElfJc_xnGbmt&WQ_
z7+=b(_wtgs39l;CR=`}uI-X!T?jRWac+o02NGn@*uhhr||0Fnrzs#Bd3m-PDnXqBu
z`SK(3qwkfba*L+Rgc+ty{5VZF4L9+677=&C$U<PZ>cMW!En9RiSX*&U^eX#{T736F
zG3FEVxuPz5QHJO8 at K&Oj+g$oua}&4u{r8&thAa`wWf9Aa5wA3BrT9@^#LMc4c%?i-
znRj0i5i5&`Qx?Ccj#zqr at xDdbrH$1(qD*v8d=vI;ea%uZC*YjYt;S?C*CfCT=n^H2
zs<|AVKQ8bo%*)%Mj%O%4?<?UQU_7$K6UyQUbv(Io)paTd>Xq{$&=K?b6lYnkbv4`Q
z=WX$u2+wt7ev{0?429q1LXRxpM>Frd(6HQhK&{-B`LjbuP!puAdxG#CdC}bLXh0FR
z;fN#5$4r8CC6MgMx7+gM+#)&T%dyaB5sAOqfN>74Q{J11)D;JkCLW;3P`LJ*hCKmj
zH|rByke|wCD;09JNoJ9+g|+_<(FWIq^WNPq!nEqw2U5B4ZI}Q-`2=<2Zmn7ahX;X>
zRIh(F|CX&tp>UHxO0hl^LRFV-)hyZiK(U#o7CP$8P3*84S-6IA{X7T(V?>)RrQHS!
zvzc~Q*}KBb%CWJsEvzgF%>^5I0PNEa0Ti0o&)XaQ=20_5W&S}fxADjn5ZU~oo=e3o
z$L}+ at ub)Y=;7h$|AdKsuDJ^A|Ugmc8;?@?RK4ai9j at s5^k|6^^vwu)PAI1cc(;g3&
zEl{4pRn+SP^UuO~aMXVm81oPb^pp9A!CDmvcI1C)%d>&~54kmaZTx5OSsk3NclJ2#
zcgx!u)J!nup%@;iHQ~plsuMPafAE;q4r!^<X7LZn#)G~yp7H?a^*3u1-Nftww|3Jk
zK1bAqi}e1pOG}6J$>EN<#qT(&MT(Pg(%H61jR0P77s<1y!NPxveLNehAyBUV?ED;?
zD#uoI6dr>r<pS_O#@fQVDihgsjK)eZU2k~lFB_}^HrU2*z>zktg4ZFu^kSCExG6q1
zip<`adY$XDGYK9dY(!+Zk1!#xEH95)6unV)mm)X5{!e{vFW4Ghr8AtiH*~*7>R~HY
zfZ)kmy at d4!xCEXGsuOzf!94(eSm0tCwFVR+?idbV#%R;4FHlsAeNdntXx1uW;KtP)
z3cp0Q#NoM#jKi)}1NN(uR(s&+9tbus at WGO=SdE`L%ci;?uEceXQ{68ud=RUf-?VvD
zwaiu)XQMu_ET5*|;;C_CJQ-)=65H!xJ^^QECB{HxS-{!A#AuspImG`GFvoux?A(bF
zknjzd!=Pl4SZvFWhV+7fIc#h%&h?szclX4Fwpj}x`)<Hle#Cs+62%=V33BJzr1Kz`
z9WbYjviS}iR+#ZI(8r3)__pn|qE at Ip^n?!8EX*%iV;<f#4`_qHSs|$$IA?%I&kQQp
z%69k`5r_|uOlP^;V0e-14?-v$3$Y#%uE#E(aDAR5Jmd)9cZ8Xa at USD4JHjjof5$M!
zd#Q3z5IG=;#%!k;f!J7@?>$yVi4tBlpPI;<F?)uXGaIfO6%6D!NtQ*SHx<2(eT~Y^
zN46 at 4^n=0DFqryme-OyuK#Hjx_*F^SPlMFdim%l12OM>w+M&T0n33wO;gMjkXHQd`
z9iGxK3#2F4kvOY)2q$h}=HQYJ%>#IEGFH-Oz0az08B5$3-Dl+&4N{Xq&||?!amsHM
zLsdEmmZ10^95O-_7KDwJpFgNpZv_j9a at 7`&RmQT;kX8>WU>Oy*@Hs_tbzsK6`FTa2
zN~L$FmJ6<{`pfakLQinDRgHsXje`(ZH4K&(dO}383Eo-DTFJ1LNK{CaeFt)zSE^Np
zr8(DH4eGi$Ncn^`q<lNL>|&Be5ORp$nx%@BCM3y1!({VoR+U9m45qCJxxW1ea{Hvo
zakDq)Q_PeX2PHc_cX!GBGnTo?>K}hRkBnJ_$`kX;1|@RmVRB3y%AAYpo=0pqGV>M@
zbCZavBiQGNm at t$%3)O|9Obrq9EMn&ob$=t+Gf`$L!G4(lQ-M7LWiBEz15r!>!45z%
zzC`9EB9kCuMo^s%pG9W^h8DywLouN!<_M}|p%@X$<fA$t#P&g%I}v*uVyB{*6=<nc
zM;d`^r8`>7EGHAXf9dY1u52-iaw^VM*E97unEJ~rt=H at qrt)uDP9f>RK|UwnZLqjY
z;Wh;h5=y#ME`v7F2g*XFA}GU$&f0Aj<=Dg at fQC1VbJjAG)@M-eH?sxG9mcC9oZcBJ
zsiAAY#Kngmr8_k5!y9P0f0lPyL?75nXlNUfre2>h(BbLRpC|Xs>avJG06aQoOh4S{
zY370$YRU<ocBJOkKt0~Wh7%v*&+cmwM2bI%6n{v~RF}|Ht_+6Mxg&&=dYzeCx2n8W
zA<B{Tw-~yl34_&_1!-Gh*0cK+fSRqiO3L7Wy~O;oMA0CLjFp;|{mmCVZA&HPT~(y$
z6<QTDNNq8%&#<nCM;@x^LGgN`k6jZnNO@?#26YZ<u at tDI7J?s1?YBnJnMO#8z9e`v
z23qC}+8mTPhd<EYVx+$7?l5~ue#|9fYKV{~z?*pv>WgdX-l^6uR+YQj|DdRwDLZl7
za+S1Af7<dCYhc7Ep^Xn;B<nsRfn1<c;dWTDJ|m_WbETNz72Q!^CDn}*wR<M8FH=oN
zo?N*y9|$`|@Y;WU(nP*-K3Nw`Zrn|>!^w?VWZhbFV<E{FlN(FOn72uF!`<ZeTPlx1
z at d~1bg3&2Uhn&O%**dcOm$-g$9FdugiB%E2?ssIOoK=I8Ebruk)LnzaiTrV8j|S8*
z+JqfSO-o5cg*GzokR?uHi+kT3 at IiC)eRG`D81Ml$%Hm}DfDiO>2P5M$6#+SU1x!WU
zVK}KTAWT#1Hj_FQ<s&K}sHs%E9!B#Hz5TiyFtmu^O}(KGm`v>zdy~BO6c}Cs!Askz
z2$-xs0n1pX`guTGVsw`nO%Ia!f7QDm0!lcQ$qV<W+6P2SiJCTexd>yjXat>B$ABz9
zF)iP(vK|E=JO8zTvK0d$)FJPbI%HY8dLV1J4Z6Qcl*J9G8U_r%8BcgtSIX4wj0&yV
zU6Q;yv5JNDO~Q`6k6OAL)YPHvl+;YYp_2rk$H}?bJB^fVdm{W=ncHq^)CT-2nER`5
zVFcXNpC2#=fA}Nc6J{80N-X0foc`pe-sW|`1w;QtEXPBC1^TWF#YV&KYl{Nm0}^(*
zMmrGlLRPKDm(C2LsTkmZlICS6!t$c_iryzuVYN-o^vBY7nBc|LI~%*fr?3|CR?&vU
z;x6Z^EgTTdCHA|6oB)#N2>$)_(xI9o1Ip%qpK?(!kpz@@I4J>C{M#Xq3TtJ+nJK^L
zg>Z^hA$?96hb7)tTm8im5X2btdkr1gn%`Dy-4F8kO%+KQ^0yAa(uAo1_Q7nx8ZG)P
zZL5wgi<O8A?eI2pC(L#NmIR26v4h=bcZ?{c5<ca%zl&NGsXe7`XQ~@lTF==z70Eu>
z7Imlueo2U_p;m#FP+f<!F%;>@5M5iX=soD+9MDpziXV_&XGS_`34f_YR=-Mn5`OKL
z9j$}cen5+LD=g)Bpd}ip*-AuWF0&i$N!KxFcV7!R4xg5n2J{+1LoG~2eNCyhrTu!l
z=?}U@=WyrSHw2nRQ((^de}&17{0o_mwd9n#bMOk<EwSt_wYT`b<`;rDGT(uB5jL_F
z)1L@;APBe2T~b~vtACI at IRwNTWSbNHgz2*6$Jl!SVOa|w5SG1dwMJD4HEJEPvnLR+
zgs3$u%hadZ at j0Jfrah(ARGf1h%H at govxXD9cmvelZmWK4P2|f`+shIM5 at Wy=vr=N-
z5D4eQ#y6)>qi~{Ny>E}n9-D~0Q0u=>#25DDZXaM-yylH33pr*#ypGhYC3qE|scd&e
z<FPUwysH^U)b!t_Je-=wp-{)vjdOL1-k|xDLE2)rjP<z+`gDpj%wG`|S99|&ND?p~
zB&qZ)mZY?|8Y|(gs*YVnivM9ZHs0m+`hr9y5j=NHl8-nEleF9nBE0g?Lh<6AT1|_c
zm$ryA!h6S`a%U%wbt!@_SJhH`VaC;>$zl`1%T}mr at 9IU9u}Zis)1HBe`IOo??^TPt
z%<O(p($B at G|II^Zs;IvzfHKJr{dJD|I*8~h{pHXKaJ*Zp$ozlY)fN}YVV at Cq*a25;
zVrwD at 2lct)glFC-G7u5a4d$0+(%`$<S&^GE>O%eyxu<}lJGDkYUAhB|A at j+V?bpE~
zf}OkO<XyZP;2i8I9I!QBZm%@L@&8rQ+$5>4W*V1Ejp~WwzhI|oI8iN{B7+VKLpb^k
zfv`$o69nx5=Nh)9vKa2~tZ9}0E at W-B8~Nj~EofBM^OuW$uxFA8kn{gy0fAmi0?(Ls
z9kY(Kt6 at u>O4?4?eF_w-$;020b<ZLGs!TqpT7HjyeFl4ElyYC)5czgS=EKpJU3a^N
z`Umg#TgFWZxs6|HKa&Ad-gbaJFbae^ma;1SZpb^2YBfFZ<L#O)cR`P1FH1DvDIKuB
zt$?tsv%jJ|@~w<a3nXH?MnT at 52aPi;|JA#{YYS~5IJJ2fnaO<bysw$bl at nYxzXL<S
zoA>uN*m18&EU(CoYgwYrEcN<>z at Ly3Cu>6Hr!GCLSenV6f?%AD&_iAQD9F=W;+X^$
zr*`Px+EM*g_=&Sb&yj8P!G5)acB&*~nL`b+(`c%kJvye<u at jV4+jXvg@Ejg_OUPa4
z(D`>^?%Uwlr>=BVHT$mkCOk}4h26E at z(ao)*2<XKA%;D`z3Wu};3?dk4<|mDaV<f2
z+YT5(wpF95fV-Mj)W at t5{m!rHusKj)@R#xbE-tqNj|xWd<`FFNd!l4P;pk6*nJlaO
zc#O<tKql5F4Ykhoe-_$h-iNN)S$Oz4VqfRR6Z8J4B_FBZ`Ve>qkFp{_nj)7<(kg8?
zn6 at jcz>@6X7%y*W*k*28t-rihJjb5g?gs7O5Kkf4QJ`k2VC!T|#WEls_H-u|U~Z@$
z_KQuZWR~hdzx6WO at 2<MvFTRXw2Ky-w!z`!T0FAMyvZBIajA1*)`Yp;_=M<eqZv*)b
zy$tW_y`C)mnAM}ITm9BLRAT`&|7qxGDp5?rXiH0feOda~(P?W+-%_ at l)IYeW8{6UJ
zZJ7ivHJ3>Z+nU&#AeJE|{#1Hv#OgZZP4 at O>?dD}S)Nj>5r<8lh%hEOd3Z+D`A<z~$
z#j-vlj~3WAO<^w_Rn_&2zC|^)|Fv`LSBO1pRP{~2^#ZE7 at L#KFz_Je<Rh{eCRA7#r
z?H5&`62IMC)z|&-gG$Kxs$WzNzr3sWGjM-rQDdd{66TH$9t;4hYEJiybtsbuf(!we
z0Sqv|GAE3x6#e3}s3z~fbnH;cE3G9c<WbCDM^#1r8a=%ERu%S(^jJm<`hl*RkD%f0
z2-|gL+}Vl>nE8=@$Gm9lp#J$ju+BpEYw|&c6l$a|p=AL%^-@@Wpr4G(50}b4^Wc!<
z%^bGvG2CeZQC{$8 at RClh3}I6 at sZS_hi%KG0+b at XB+jgLzYA7t*&FzflZu at 6nYzn_T
zJKB^co0gF!WfO3UmN8#S?V?6X^0QQODZl7r!}{`wHopd<Br!j||A_}yHvwg`N%lj6
zZ6WF!3ATY?mk at P%M5csbzeTWLBiIXw%x8%@K9TtV#rzYoub{eGlv#o5&Y;ZCQA{?f
z+k)yA;)g$V6oM-(-+>pfTH3-a;Fi_&slM$4Le%cyD&gNy7w&<Foag)a!FtG?;U!Ll
z0}L>xdl4_~ajIhjTPFs?H0 at 2;&{FGGR<Xrj*bDNLM=})Gu+cAblftt{$qrep^xQnM
z?jZsEjIZ$mcK5M0d8i;!u>l-bK=>=-t$wJg>ZA6mXH3?l5WGDP^2_ at aK~GGHiT&CV
zy}Lwz`9X5<&fo21pZDMeO`>r_hV=*nypQ`j+$|b`js)`zwzBSDos$ngT#PY6&!R5G
zb05@^PP$uDEI+y%uaU;%V9 at UB?tpPrS8u2`DgPLYNM#4t|5@`jQl=3|WHR%o<euW@
z<_*nF%92^z9`>o*@#@;M%eF*nULp|<F)i0f%V*^9tj^{QopQhC4&}&be2494w&j&}
z*(*29Z{jspkf>KmT4a6dYuNoU&YS{AkfmU&Y?;E(0VapVNr5L|!DZqR)x(JT-~bCo
zquCEL1=4j5 at hWb~u95JI{jek036#0YLPIx5W>>?RJiCDan*v(PP|f*1AcvaQ;SQ_G
zfnnf&i)40`!+Mv6ex(EvA9>i>ufz{_z^PcN*@>JqDcog6M-*{z>ICF&z6t^8#{BY&
zq9)*9hyF*VlYu%Gunb)7igTK8?5fj!Fyq=&diR5BcNtvSgTt|kR7y4^7mGnpej={J
z^8IIwR8RRu>m|hN{;{T@&jO;<yrZ+DrQHO^IPCho-1Yob4&nHNhGF8-MQbhMHC}@;
zI!OhQSQL03jw!Yf(8~=_qMtHi((o>@`6_h*K4pLDD>ZZ5EA^cH4HtQw)b1hvJQ;BL
z6^VnH-$MUaRJ;0&4zmPok%BUZr*t|C+)Khi at vL|HWRBU-o^AHeX>Mlmn&9t%h%X>s
zoT+SMpTa*!^;#bs$|%<7XpHun#6HTS3bP!1d8bwQ>*{`M3IdA|kDpqhBY{z2v4l9s
z-tu!V-=)<Tleq$uP;HdWpNVfCa$tum{f3!d$xXng4nv{?=u>=zyZI{rd-&YB7&FT`
zMo7Ru_vNFD;;(?7i;gO0r3t=eQI0_+?5O^(rR`#4rI91mb0qNEqp`B)pS^I)x8^yF
z_<cL4J$cKtm0U+tn-cpek1m39+1 at J)_>ljMWkZH+vyYX_sHx~JGxYMcdd?K}hK%Y;
zCESnb$W?nq8TJ}ouj6E6e#oEaw>6^eBB)(nce%=|&su7K!QCO5;#F{jTr=*!uT=3X
zh`MXTd`g_y=b(tnhEK`G{G+`E_)oLIU<0$Ji1*v)5udU$uY!8Eo`Ci6m0r#43hHZN
z_5tGuY%=I+q{BEfN5_J9CB`i|qBNxH?ajCMrfLKrv{YcxG4R?nr-3MVOqYNuy44H1
z<41TP21^YVL-#M5&+OD<2Q4t$pMt*A<@fih9V!pb3W|!bNhV<?7teCgX;gjzUsxLr
z7x9v)0JML{212Fv80t#v9Zc&5dk5rc%;2GW+u%6T(b74pcOG37eT;ZhPpiJ`6 at 6yk
z;;#C(SM%u;rdeM?HQ)AHG=dKI1?O}mOx(X}IO{@0ed*-evR;F#_`cYK;H_4dZSGZF
z>eUBNcADo7XKZ7Eml6ChQ!+mka*NAgGQo0RT^{xpaDAm}Q<;ve_cLn*`cv%{=bO#4
zZB at M$ReSaumq@~vNHquTNpIG`kL?6#FsWCjlvtjf0 at o|>e#i386gC^_uVG74!I2gd
zj at u_iy!70Bl6?k!>RxvU8|P!%gj&;ZQ6xva83lR=L%|8skrn5iM|a|bFs<SYJ8wo%
zy~_A+N at IE3Ma?0^D>|y$*Q?0^J~`FhY(%ZEV%O}%llWgYfEoXxd=>-0CBW~zC))vN
zRM4d$9AQhPf9wF(eRkD`URWhqP&6~Kpsa&Fo~UH_wbfr>h5XA*KKtmRW)I at +$xd|)
z4KV(Bd$h{!s`y at OhJ8Oq7>KB-Dwmoif1niaw1brV8R2ceI~T?8f&9In|8g&87pR7c
zmWo1~n<V*Z7o(dr`2ySQCQDw+hQAf!SUQot+ZuYThwO$)z;T5}Q0!L{oTrefQhSR&
zk_F``<({(5_}Jbt8Q7MN=Z^=?Iu{j3us?#Qsqj}C-ogyaZ|nJE13uTIEpePKx18_k
zI^SdVm_mKwyrZvK-rKNWya~;ZsQU at GenaynXko?gqcAG5B5t^tH^?kibEP9a)I$A+
z3 at vz(aA at vx!yG43dGuPhVwq?6inju*RjAjx#a=`9g5U7flliPT2V(zZOSe*p9~E=R
zKKN|!>G1*3=_lfKsBWX3&tNO;st<a~KIj1xhCknF#7N2BtUYya+4*mSIECTg*YWo`
z1Pc#7ShEI|ecm&_4Ax2J0=homQKG}{=H{zrCDyZT7pcqQ)lM10?9r at 3^HuDXWA7!b
z at 7j+y;`^2SrXDrSkG6%1gj4>YjC~6ntcKYHd0vH*+eceA^-y6(k16oWZ2Y6 at kc=|a
zTfWb6FCkM)<{Dt9*W`nRRra`t@=&er8C=^VkPTRVCY5r0H`0dPFY`(cgkdY)DSWJ;
zSc-3)l2+m?skU7Z7{^W7ymY{U9De3<96M>o9;eo!j<<aftDWDtc|@P)X^fkuZk(oB
z2glitdpEdWqZpx~WiIO(i}bz&i_~NJi8Vd1hxrAZO-pQo$fkffWXuedc&_JlJ()QZ
z1)L?}v>#tcB{oCxQvqiuC7#D+Y$23ApIC<D at F!sd<_P$}_C!5Y3JW;vn^*!7mw-9$
zLM3d(vvfUkz2 at QjtcZf1C5row3$HJsMqt34$wK8P(C`SPx&qElkSco)E3OIE at K`qS
zG~~S%FsF_RgM<naoPe{od}Ytaim&SaNvn?coK|S at yd)lTpgPMf`>5x<;^VrTbfOe;
zgaLDMLNHNkNGybrfcWBjx0=s;s*Tmjy$>?YbW2`Ov$;BX<^!P;^mW&JJ)M^7<d^TW
zP4wWwo&j5R@`pok&OhsDPiR<m^2K|9x>uv3w+(hLt6P^U3v)S;bSW=wMWi?*;K)Ro
z%YMo&w^gxZpJK^=#gYSxB^in(zyo|YnR2iPmMh!_No74n8-=_Bk|<9}lIQE*)63 at e
zsHeY}r0R_P$J60ao(V!vc#nY(J~qB6Og?a_n<Y_rDw2Uj$Nl at Po^G(C{U?ibV6eGF
zT<T7V>4BvAglDr>w(aenv@``<La}(|tg at Q!n$10&JG^H+Z4Gwbvt0OX4NvZ6Y50wI
zz{A!s!n- at 6CbdV_@QCL+8Ge4l`)R7J;UVvHU+RLa;Xbce;3T)`V?`UGa0`s9$U7R5
z3x8)U32!DXx^5%$J*S+5rEuJuU<c=^<&~bUS9)y!p!d})*oU0!B`?ogd)hc7u7r at 7
zJgW- at inKT*`_Mq4Cm4cYvHYIX2|3>}L;sZ13^|vWo{k)BgQdk;*oX9Aj_<TEdHsCI
z*|#vS+Pl-lEiH$GaDhH84*udnBlz90A{W_>PM(-#jkQT{1I0Moc*DbJD%IcMz^Rug
z${zE8#<?~~UkNo{2{&I^YPqt~c4eJ-p*`j<T`_}NqiX5an;q4kW2&a~7*&%U$vL9g
z%(`D_YEc)jr)6S~O(T$<jLXgu&@Jti-Q9bBuFxuck11Z>Z}vTw{cUrrpZVo|qEIIF
z2KBbWf6VKl_7@#Z+MI!BQVPa@;|-&FtH9LKu616Z<F}vI5Zq*+#ecHxgeUx at l-l2Y
zD`_ikV#WSK<Beseu@;Nq${naYstIB)dV|XSZ!Pvf3u$k+`BZCTPq)?xq8I|iR_L>Z
zMq!3%I#VyqppM)8C(BNFN@(b6WGLw!+?9AIG|ZQF=gFKeJ}iNXvK!r<|EIlg0c+|?
z+uhl at a9bh)j6y&|1d8AVg4SE at kWEBEoJv}$cG?-)kO;I`Wb9O}mJk&|aSCZ`0dXct
zi=x#TNUe&OnGh-7u*KARgH(uu#I_EmR_#S{zO at 60{&VL4=lsue&Uwz+d7kXG*X{e(
zde?QY^{sDJ%8wkeFVMgw0Qx0u)HdaU4ax=DJJMSZ3c<lSCEge?)wrOzuXhzAma^Ht
zU{hb;UFU+0007$BRs|e}^1l)|NdGz#dDm*WbsSxEN{Eky*&1yrcFTX^-UGLxBGWB*
zrEEBMt2#>Q|8{J`-9?M#F9!P7pAZ_Q&svmk<90(oJJ7#XaBe{tTh8>aT2If``KnQz
z$Y3rehr2bVZ-iOr4%KQx^b^nU`&%YhYBFihfA82Eqa!tld#q6%H2rG3(dN}K`<C&{
zRLfaEdvxJ~z4liN7aTpg!+9KmS5p;uDXiGtI^ZtDB5WX4b0)m2<W>l}_1&$b5q>;4
zcZBzmGl;aG9H8!MOb8dCnu+%cVHvlyIpRJnhSlD6nGBkh7ag)%Nk;4~N&NYloZNW&
z5}AI44+~k$C*eAaAV{-O9y#WPd}b^-RimDmjF-f^zjGOy9TIz={x%*`to#D%*Pc)9
z3~=&SmR+Of=2LT5QgaKaxxc68t^zKw6|DQ~NZ8oaW)=cTH?icJBVSratZTa<j|_bt
zzN>p(q9ZVKM&j<9t~>iNaTsubI8a93s^whEUq@>B1x7*8Q<VWa;B)0kKI04XLFVG9
zAFQ&%nnQ9m<na at iZhT?+M^i=KBYcjC#3(X4*>dKg=$Mx}#lPGds15}!ViAsh0SCSb
zB82 at A_>hA<nIi?Z&WNC{hmH_WZB|sL18S<osS3LDlLHpB`fmHdGqkT<AE*uvUN;-1
zy88u4AaGAbxQl>=-fsb9)z`31^^-wxtM(mNo;Q4FY;!Gx1t{`XFURJ6XO)BbP;g#@
zb;2b--R|Dtdd3UZl_A*ME`9k_bCu9Iz7YDM`n`VT)e=~YZGi((SSgLcReS+YDMChE
z!G)UK438 at pEP5N5r(wrSRpaWq=%_gFa0-GX+VXst-Z`ZZY9}nFPxNvW7CW#^E3THf
zt`C}M4C2DnDlN at XQRARsL!TG4=uLgWQ-yo0vJc5}3N>m;&LIjM6?!VXaOSGPc!Orf
z_l{Rc8ioY2CTV4YBVVi6MirK1zc|KQECo at cr?!79)w^(D;29=!$a@`8B>FxDV@*p7
zi at F`lmfh79`+-$U<EE{IexhX2qPs6{2-Iwn3^{|coY>f at ksFxCh2zqN?k0xm-ucRb
zNa=GLyIuoc4l9}j#uZBeh)4fH{)OY1F_M}QshlR+Wd<q8Io at vuM}ryzL!hG}(191L
zHsB<?XL}{{jowp10@%b`=kxLOYA>T=omi^Y9t-V at J!@{@JIt+pZJ-UU_0*A1jqP5>
zGwV$4-lnrJM_lF_A;#XM at 0odB+$Rncq)V(Z={1H1NE)Q3(F%_$i!^tfG^J9SQr6Jm
zX!@(;{9hbh`y6-nI*RHX+T0*#Zm>O9Y|Ra^<c6AZ#~ROMb$fA(K6V)D94NKxW5=C6
zj*4B5^VfSDK6IG!!gP7#op}-Vyh!Q7eU`kZjCoRRUbw;Ns1<;8Qqyk7`C3QUhmJc&
zN0HuP*y*U)>CokhwYeeA+)#V&SZnS$OD>FRVbT=XI(bdS<w_GvimDwbx8YBYrW(ij
z9geOl$DI!x&P-U#4Yp>AEtw&v%ur+ISZ(GQ!*)jm!3ZP){vb#7aYQtTq=e}lqFek7
zokL#kK*2iRMlG*c$=i(2bu5-Zp+*L+HNjNjHzO^lABvvwR3}fV{TfcuqEsUl4#%gx
zRU!g3sI{)K3hLCMq{{9(&Ij5dXun-hXQ#qz17RLL7S_x^1`TBU&N#`-TJkDIwqyDV
zNkYP1^^{SDRgQ*Lj&r<-zk><{2s#n%j}Q_vIv%=9UWrLir!VJu<nBn{`lG7bm2}f3
zqr!J`O at dl9v17KCSJKwWv#UjI=Xq^M-ZdrFLKgu^+v#l*>_WfpcvZ_QIsaRS=6hb>
zk#{v(o;I)0l(Z{}L*Q8k6>bvjJ~q2^j|5^sJSgv(iI93RdIs(9_9Tki;80a~!*h;v
zeATC1CS#B`l?MR|H!<UGUMFU`dtM%H*QXPcKnXWsJJ`+!P5JU(SiSrA^liqC!0jWk
zP at 1@2nusTQxXeVcmGYI791A|gBeGVz<xSbV4Z3++<-8(lo(6dum(SNOPcFE`q31;n
z at -FeOQ1pU$E at e#4HYVp7lk<(qD~&l~X^y`vCsdmgs50V_lxgc3(|b`$_S2A+5&;PE
zfmtd*qrjLiSfs$#WWlb0Os$bnl7u?ISN9=N{yuirsr_MT*<>K<KgCv)+J9GkfVjZt
z-D(}2Q*gV8-~8Z%JJBbw>M7*XwBm3UwLik5WtT<MM_?_O_Q(J`J~O=8_YT<hxMi4&
z0()WI0zPM}qbO;i(Mqac at G^SCp_1v<@F~O00+{g=bc^!CZuZJ=_O86ys|zodHNPi1
z&GRW0%@~QR?^!?f%x9X2UZZphCReRJa~d`sP15LZU#@v|?Kd_L%a<dzww^id9HDWh
zYQ#2i>&=}`3%L)4s(M|@tYSQL!>6Da&Qv0iD0kvcfs&pg;6R@?1qF$M`YUok7RCpt
z<L%ZnTKng*x4x}zH6e{Um>)TtR4 at 2@TyyVvOE3BrI=ne1WfzDjKTn{&=&0q({_}t9
zwewV3UWPXDBYR?POuQXkuqS?`Ll<?4wP#x|)qLyBDmK#5COSqZoniz*k}KM#g}ql>
zm9Jr`_|jd!Nke;gy-Etk6CmMXq)Z?q534OrdwM};T{J5UbQ}~BbXIZ1E<Cv~Vhzr!
zTJCWy!)TPsRQt&WeedoVvZcjk*omXmc0lLf0}j)OU9-uSBEp)rO_^AtOe|L>*3em7
z9tyN*6ZG59cp2X9HKC2B#E(o=y^)@v%PP at yZ?IDhkY3Pj*Rz#OnoFQ<hxU_#Kf)=y
z%_dYF@{=8Ha-xkoRH{W=h$<|E0z$VSaa+IF>_l59`m`N2!w`PiR}0G2#R-}c4(zso
z!@3`dQUl?g07 at _V9>5Mvtj6Zo2S+J=-H^Z$+ at j4lLxOjIOtvf}4(%lc8e%8^FqTR&
z(lLj((sXqF0bc#~cXf#$6<>1oh{@su#I6`J=`SuPDz>A|R#XBB(xI&;REkklRGSok
z)+zK_g~6)$lU=dX3h7qVv|Z=5^@TeXJM4<ewq~A1QC0g1?n`|TQcb~Ickt3ZnS-Jk
z7kHV4Uuq|DjJzEY`5-*}I+UN_9oMe6<h!<#Eq`<w(Iz*9#>86p$+uqb&R$U2y6dtx
z%Elr|iMtPbqc!!1LH%o89Vmd^qoZTAbhKcX%X;>w^qtp$pdFPu(IzVt1uD^@&018<
zAj<)QAm`KOlGNXpU>HG%(n!Ed at H%KfgA=k)rQv91?P*tf?w81`c%cj30{jF at j9<$x
z3>?5_BF#|1c_WhyM9%`UcoDJdH)P8)R{<(E0VnBX9Rg)C=JdmHP)jsF3yIxAKr*qU
zZ?8m)HbScO(78grC=nuWJT=SOvTvNpmv0##CBA*;j42fq+-?!?{b1IurmCA(=@k2i
zew{Y$)z&)?Z#wA-4+X`qxq5bxEpu^_k`i4F4{o0Gi1|kAQ>cYjdK6me4kyz%VwBVY
z9r+0y5uu~uBT+|vB3R=x>UTqak`jqS(WGFi3l1~igjyZva_am0em1B7<E$NNO^A_&
zV3yrQukwsv_Mh`WzIU$Oq(1c1o`~={y5%SoT0)gp+*(~-{U`D5pYZ0qF17GbO^-!7
zrQy|^3Gh7$%k5f|&j$SQ%23)rBI09LOMnYq#elA301a?K0a>%&C>FZxXCJ2fHa`g=
z6wt>)VzUta`8VI$v$i=COYCSP^uu?KV=SPeXi6nofD%ME;EM*gLqUW~d-i_%lE3Sq
zMP-QuKli8a_?s at V#F`mJXGYMOVTQlngxUpCk at n2-P>ROP9D%cc!G5cL*QJ=>LAxxz
z-487rT7WTY8#HPtBHSEtDQTf_6U(>2XWvHPx$kw0FVBk0?CMp^Lc$cjMaL}{?>$oW
z!L074=*J4~J$Jz+_4jM_wVJKw^wpozi6thq30OegWH2bvC6*gevAe!QPLTJ$AgwP?
zhZ$5?Ym*5lrhXqPMZjaU-yWcSv%d%Q0Lh3D&qH-&@$vpSzR+ZK=pCJ+vU at EwM-!K{
zaJi~`1PV5Bc@{3e_6Gt*o45iC*Q5I`fo7Swo))f`>MmjAa%!I at 8rJkwWZx`V0KG-k
zjNVtl$id<(OUF97-W8cQ`}(H#xrDGY4sJg}M2=J6>(kNJ76=^>I9>52LJ|)DhS+8B
z*$dV^KE?0%8_(WNzf}vVQmyB-RP+}l?lL9r)+N>{6F*WW*5-`_-NV1N)6rHs#!8u!
z=DM6`@1*|&<b?t3KO~j*JL#DJ7$pV0Vx^;vl(|LY{stZ#w+Bu6!k&MBo-wBf<{6oW
zK^iFDs$XU&J>~m*UcGJ2QrWYzKyiSg%tU(DQP4- at Pj|&O9qCy at m4n)6#UGWVr&95b
zEK3X&@Ej%n<q^G<d}q&se>*la2SLB<a`m7@*^05w6`@v=0F<r2E;~iZOM9?brlRaH
zWm0Ub^VjRA85Mu5^hfdKm2#cpof3boqTHPdhK_uF4~p-l&VSqUo7=LyvG%-B3+TQy
z2U->a1>Bh`%IYY{Nu_^z`81v4k0t&Z(4wt)XNkY^aXPQ|WL)ooQwpQdd??(~OEMq>
z)5MdEMJm_`=7f_q-`7|R6!`^N;|dh3m5PlpG+yd4s>kZGLcylQ;3yUOO2)6j+S7Et
z$Ew%7?CTW3n6k~D6>7~IXUUS9vNDU|Ys8I%CyK84tS4?k50XhU#l{?oF=w1Mu}tT+
zQJ1)F5bF{OLLKdqQ#}P<8)@EZ2)3-m98;pr8MlNgP;3K0K+l{~QO2w}$62}`D{Go0
zP|SS)mB+e~R~02)?ysAdt(%vlo0m^550otrH7ys;kWKH2K%=yz=Q8{aJ#vmG2;*y)
zi<R>VkU+p8X3dZ-7soE$0I#oj!7Dv475OXYtz`JgcXguxDqM(2A4LLIYy#^Xs02L?
zy5d9RHQg(fJ at Y}s_gV!&Xb&&!?7WL#(lER^U%}(_XYjCw7s3u5en}%5=JmjfQAogN
zHhy7(76=K=n-Z{s7%M!Pm{mBYOo;8UuTh|9cHS__9PIG~FRPHqw_$p~0iGrxe?X33
z7%Om7Z}~g6hHGF$!tHLz#0dcg`gj~s#)Nb-{G3 at Dlmc!{I`%N>2-n2M;)p}RG%g&=
z$;7e?iO%boojc=@nDmD~W8ZKMERMN at b<M}@z^n7|bb_M^*!>1fhFv3OH#{O(fqwaI
z%x;K&+j-2+%5aA)q<@duJqGinf!x){V_YHG$EZkuiQUpK8Y^{L9*RbeS5xD$4_q%3
zd%zt8G|ils;CLn>(RLIwG!1gV0kZHqfizqrk>ET at 884@hFf)DX5E9^JhYZnRG8vli
zZ)XU&tHrpubm5xva?DFNm<;`3Zop9rtl<f~3c&nVyfjaM_vIt-+`PhWbOVXu_<<F?
zB*Q?^K~1T`SPmry-rblmyi0LDb>W3iq&WZa+SPX#^Bw|A{cD_0SvV}R;C#vkF_k=C
zyUdI>1b6A at e1&#G&?}f7BoRNEf&q7C<YL^z3y11C1c3UVKdiMuPGHR++}%0R0^DOx
zgB2(+yMCX+u8E!jWLRxYTJH+0!H!OVWiWMu)$pxcL?8hQL@{VODh2;F7vYiIVtD^X
z+Jf#n at 0VTzt3BdT&v1Sorec8_2Kxr^!eklJ0CQWgY8fj-qp=0-GA4&`O@)@}02q%o
z*uxo7IMlYlYKI==iBi7ssFZ=5x(p+2kI)QtQRnG at l8`}u8qg;?keAb#-SCKDwL*G_
zwPT1C8;{uy6=1pxx5(kK&d<l}AY|L)H3A%+Dh>KAm`v^*?%X}<Ilu$h)jz>5?n!nb
z&*6X(nxUQ+tT<h<tME%25lII}>PFw!Map9a5(1}%-Coe{<DSBfz$eyyXflnE- at f6d
zfS)Rj)$)^QXR6h5Pn4I^Co_w<DROZ)TK_0W?g80VzU;(nGvzV}%3UCXpz&$<b?YEV
zmw&fC*8neZCvM1cr^8FH+`0mv#zO(J6%3ITZ-Y&?0(UWSU)+F+RW4j*qDdeB0yxmQ
zpJHcDw=DNCV{J_G0__v<Izx{9rJ03szc6Xd{Zwj509Pp!jLL0{N<%BaK3`6SZ-Qki
zc%{;axH~v71zA<#3J5NdbWo&DbYB-fkp~U{+7L1V9`VeRZc8)dS1|=8Q#EJEm6 at C3
zzP>SQ)=VLeJpQq!3}F%$V=l at xVo63MJvLG&zOS3iq&+U at h7=MZ#f2bEBcjrh`0h-_
zoqfUs(`A^=7ea?4Z@!U|k{kw(urNH7E3nEB++fm<`7s6l7`I<PL3D^){SzLKxP at VE
zdoj1Tf5NTmUvZN?L3D`Qdrx>Iuf(L?P{vVEl<(Y_h5Ioo`mwylt-mq6#OALYs?HEL
zZuAW&AUQY=L3PHCYaeq27eE_7b8#JWMm*NQ05{|>8Eg;Op9u>M6W$*+OIgH|(}4QH
zt`Nc*VT3T?W@${rjDV&(;K@!0+)Y5*eaM#y2C;CIN1U$U4!{)gXr_X_p}!f{g9GU&
z`WyAQzsqH~R>~qCtALP?gkedv1G8aciIw3Ew-Z4`CYM#1w>9F?bPcQ)=vs$$)sXxr
z=T<2=m!^m($DD*=Db@)I$*|FL%|-Ms#F@$Tbxb0h1BpO?0r!DE at Zap=L+~I5ls{o8
zhxS=vBYM9~-vw4^4oP43TlJmrZiV- at ei=`bzCmEsZ+5%1obB(ra~<B@{S?h~Qqa4s
zuxGBN4Y-8pOx#adGvA&V31-}`>on6tAwhclA#i}OV_}~P2XyyV7q1WXR&XfDsxQW|
z(}>~*EK}8W?)pH7SO~%X(++X#^}!CYBl8i5<BvEbeTJ#VS-=H%m3AY~KG+8q|BYdI
z6c}PJnUUvCSJyspX%rlS>nicyC43#lOho5Dxt_7xUeR#94{Dru0_bE4d8xd-AzJ|3
z{fivJ+}l at g0}bco^^c9cliwa1REs{kzNkuAz5VKhE$U;RL<amWa{M}Vd_V{8L<`gy
zo&vgvd15p{@x){E6lsxOk9%ooK%sr23;f8ymFjS^SVE|$w&6(XLgo~dg=QktMIgr^
zYWDECAo|Wz9gZuGn}vyC0ilw5T%wOW)q%LK4d-s8*MnMwVUFu3;@Eb&sO8EdX9yL7
zZBDvKz-$1zNdVoDx!|k>{+|zob}Q`~%vtfN0q2kj5KhN912NFD16DT?<51Y+F2LVF
zggh6PejUMKX*uGl$N1JcBZC)Flsp-f925&-jaL{s>sfVhOr`UF6%R!lX`woJ>|tle
z4hxy^W1Kp~Uq%aOI^Bzy5!YQ0;##<%=-vCW6ZUi9=x(wT{Og<{>1YF5kH)6WxxO;i
zj!v1RD@)}?v#ythh1Bg9lP%*3(OFN5UTLHYV7tK?g3yl5v&f`K;>WlO*>(LEI8R&|
zruQV0q=aBB(UQ$6&fv7X%qf0>Q at ns9h~l&ab6S2T1RaFHN)(?W1bc}jkY0bEXxT~>
zuOQ%dpQI&(Ae9g#5+H|EJf08)6UF{SWHi?GQRh?#lSvm{CNd#XOakCDouJdmqIaEy
zgmt)Zs-y)JU690-5VO6GCS7S}#O at fI1Kv$kjgp>J;dvdca2iP`9R=$%I(l-Bb0Anq
zE|kEYW_K{y1VK4EI$BFdRs3R4`T>^?Ln)`((A`yszW{aQqAT9ztvpf3Vz4!OgMy~P
zKo8=6Y;Nl4>c#WKQ!gX2kxT<U*uF$?0&&#fa})<h5hGk7g~iOC6k1a1ZTPCI;j1p#
z`pkn(W-O_X_EfEfMRtZ{NDPkB-0kTSRXYt=>?Y~t2+{?YIt-<K76h<LU*FSQ at wYDE
zR+-~t^Vtgr&qd!YH0gWmk4P{9kmPZbb07PWQ8fSo#-znQ5jhQ{mD~id_}JQ-H(01+
zfQ{hrP`*z$Q`9kc44r|5NIF2K(cUtE4EhNu?`g&v<%za{lCFg2au6;!on}H8Ea+nG
zQCDS}(d#R%*9CkOzC>d5x}@{ESie`;R)tT-!vtS<i30}=s^1eUw8_VS4&14?d+I^p
zXmqFX&vui6Yoeoum9e|1Dyr=0HLH<Cw;jz^I?6&v!?qriPjnTGqQb6<!Y-|6(4J=4
zcZXeNr_}$))p<xzG?X`rs)E8ayxw(6e!96X1()f%nGm(n+e#fvYIYYZIu0&Z{$mbT
z4&-pZt8(RkFNfI!IRxO(30f`=C2t^`^guTMGMG)l53Wi)rC{o1(cCV>T%66uW)LnM
z%;q_-mgXTkGoc78Ck6qwI_j8cEf4Ng87!tEcP1hXuF9f+orwy$-Cahu!7^%uGJ^WB
zQ_wKmE<R(0YQz+RiJlDoC}>vz#bSFnr-gPht+C?fkr at MURlDjqr-X+qoamArePu-#
zI$_z{Cb6T7^)6xCppF)MJBK-ecnrk6ob4Pq8Am;L>})I6;-^ljb2HW%xM;o>x8<mc
z5IcakQvkZJ261tRsSw$X<gZNz4-5Gm+y}V=Ll|wLjzfJi?-d%Ml<y41xYFE!>=npv
zAvYC$)9KS%H{ZNt1CXooHj>JcC3-y`ul9`5K~*1zm7BW^J~*zCT==7Bx1X!P0IzAP
zFZwC!Zxr=+iZWAF6Gd4dAq8|4Zf*CvtYn&<R65CpC<+qpq>hy{nYUs^Sd&|>;z5}y
zR6CVwqH3T<Lo~y!F7yi2KROETSr`mJQK{(H4%}x^4CoN9_%<k_rC>*ZI(E*t!qz2t
z0Q>2K4ZXd~2|BBzwA3*t(77>02ME|1F0h~F03e|sIo~Dda5c3-S~pFc48$6~=z^Z%
z7)GKIka(h<XAD%J^)67n|6gwB#|P^WZs(BphC^MC*C1#Ifv)mip6KoU!1s!G at u&Ue
z4|t;2?=iNCa|EMZTKy at _7Tmp+LigrgTYb`$7k2*J>*i^%cgkPwl&aoQ^3TuD@>#rS
zQDCG$(&~HN5-_E34~I^F%Dq2c1s1$E9!7Vfco`jsxH5Xmes1m0E*V`eqbp>z&b>bS
zQYYT?w at 5Rmxb5q@49d<1dDjv=o-|HFA$52RIt{yw*dDCo=0WG}1A3nVL1_p$Re5LR
zcWJo>d488qi6#k`MKV8xl4gqB^e9Xp6L;ef_iQI}4zr8K^UJcz=9Wd`ZFSM}ytLnS
zQJiosf0K+Oxwqu$)=OC94~S`m5oOU>2p5HD`Kyrk=J;_IieuuhGEsz%|HcPX$5<$?
zh5v?yBC$9wmial(HBy|U`z|8|@fj6vc;0B3-}Qi|;PBx{o^>SQ97(p-^UAe>ZB0DL
zQJa-l#p&zhl?ve_B;uYc?oOwPtAd*<55`P+KpRi2dKBvbue><0?K58adx32ihsEl4
zx|k?q56*u10d1Jnr5nk&j^z9+Mz#NN88gFrX~Xy~oiE?&%dz{CZBNoq==UoXc6Rc*
zzjf0OZ~FFH%>v}D|Eb)J1-PbzBIR+5_4-wZs=o;d-FFszZk3zy(O2v<d9vGwi80Lm
zS&`cvHj%fgl8KNx$j6kpuZ2Tcxcfo#cH(|(CMe0Vax+k&I&TCDPW6G~I+=mB_+n&d
zW+2MeCT6(nS=^~>kCkg&^EG4|R?ZpbYhsyFa6VSD>P3 at 3h@CCNBf`^SX;}$~f4wOC
z78e<=15jtT)10&e*Cf*NMN+-;v%50s(^AXb*5_Z;-5m_%u1g>iLX+h5_XYE|bBBEJ
zeo!#-$k^Ap?}S#cdh=8<R0B$|nu3ZVSo6U&f2a>i!5Sr06yR1saX^E20mTIcr6>UE
z8V|&7K-K(JqoCwo$_Nw5X?+V<I}6Dvn}~T>1*<Pj70U)m+68NFPV<)yk%YQ8oTNLQ
zDDX0E*xP9uL0CudKwDRv8ERUf>IYZc(oPGmU*dxkLoVa`H4x+gudNxXRUjzbWBkV+
zeCBB&Q4J(G))D+65^$vr)Ew}6f6!+zCe$e=W_SaA4Mgrz_mfZ^>Tz{=8mhzC1yJlK
ztG_|EeZMdjWyojp1h-Gky2sq at pFb3R?ZJ5U0=f%oBMJ1KG;tqOJ&aI2-caX)YJi9v
za9s?DVs&@AM7ew_QDxzNWa3sUGXu>>GflXvz!B^3E(N2L)jYIMz#-INzGg|x0A^=k
z_qD!NFHOB5O}!{h{Ysj8$<9>)%bT<8KmbH at x%<RY0oE1TZF80FMZvXKU1hUScv3ew
z!OhuG+RVU+P(~06>Ofrh<Ml62at#@W)yKGyYe+iWKIR&NPU}%W6?hF547d9|9)AWU
z_qkT{{1RHL*C_b}qBV>CdPZ at OQkn%c5SjE4e<wlz`{!$N?zJM~+VI<vL*ApZr?{^>
z#$l|6@&|O>#cR%yzFp%x`}(pv|8bt?>ifQa*P1!{6s_g{$$~hR#E+KH6nBS`Poiy$
z7EN8G$b?<pI$FuCG<kE4-emoOFxUjqe-wrmw(a!;0c12A9ss{Fv96MaV<I{)F7htl
z&eJ9CSxrNM6nK!@#4KeZ4Fv^-V@%A<f0I{`xHn&&NhhlJO|_hO at 3?n;d%5_i*`_xx
zykuPXm2u%k<H8GCt_su?ZJuq{are{&*|~o%`y47ojEYZ$<mhlwAuZU&Dju<ugm%T?
z&}9BvYbe*D9E17mduycPM(ad>Q)&)edSo3zz$b<^HFe+{BgXWauoR92M<fiEB^sUf
z!L`-`Dk3~qA)!>^t(2Dkwnw&D?a$Zo-yWUg|B^+P3YQQP{t3ix0}MdMSBGTL(4;WK
z*NAJBLL<$#coQZvb^2>!(NPbdt)dyhy0iVF2_s}QH0XR8wX%B#u&gf+{MHM7(f5tt
zeV;Jx=f8-?`0GA at Ao3bH6byFnL;KVd$XX|evzW^ReHznayzJx)b}IL{Z_ilJCPbV{
ziSGZ7tjyK=E<OX=xWF#H4R?*C-2c<9q;N<?uDLv19TWqRc8v{QNaElSm^Tzy!32Kq
z)=e>HtC|7~Ta!<v903w`sn{tEtiNrx(_+zYakgYsj+GW_y@?|(CmpDzQbYT?dm(Ax
zLJSl6f{x4=#rRpNR6DuYPUV;1HsAWzkunqyzCy`dr&L_;GCP6d7hXUJXbA4RbY#xG
zHMChDXfl-FQGP8hwT}#PUh4<JYpjWa%4vK%eG4DxCXIp*&lhTbv{0##4hE)`hf{!?
zlLS=r;Y!V0M;lX;IH0(a=#~;%vcRV#{qQ$BTqXrGlR at H=T&WJ8IfO6JDJBRvwa`AC
zhsB^iIR);Z3GK6!1?sT at 5DzZAnWd%0&J=J|;byM`B0)lvcc5DA17!(i4HN_?UA0&(
zAETQO@(IZ)|AxswsH4L3fui8ALwt-;Vr7(n{oYJHT$(;Vce;HPO0;^ewCH~}Tl{=%
zu;vY8RNKUNt$bo9$K$&(H813G4$obp3SM<&v4u~F7h6D$y86xS&NS}V6zG;K5GH8e
zfE{A3fmkvPn??d^Wr4~+Asg>*z-J^HL96`H<W9Sve%Y$dymwo-Yu=N9mrey^449P4
z-zUk#9_xOsd0`T#I%_ at 8$jAX0RsLe>a@`$#J%C5`A(IcGaj(zfxSD26#)4m&c9(I@
zR at 7x<1S~<bfHX1y at u>MPNEY3N97)2%r1MkD-|+ij)7X0l#1Kyz8L;Ie5WrQYET%kP
z5Gx?15W4uD%jlVkX6Jvr!8kt^1wp{7^a1)|XKXTZO=N)kG=ry>T7yT)fpKpm;#OE}
zXpA)3tW9oG`;P*3e>>pH1t5_8rBRQ~JtsgSj0 at -8n9rl+FHL&<rO^nkZW>`Gn at Jj4
z_A at kKEtv{8hIRVD)x at Bi7cP0Hg%Aq&v3qQYt0mX!q{6aZ8MU^oO7#m&b3yv67N&pX
zTS!zBY?2CP)LX4Px_@%f;1dk9I|z=#k0nP2t$_M{euoV`Yq61$6J9guY-oAp%vF)e
z5f(V4guK-`!Ay$LHZb6D=sn!*aTef_11TMRx09>aoKTN_QAVvU!HXX`*-ml|MGMpa
z`cQ|0+86~|jd1DvVEAN+YXlEwZRHi;#<j(KqWhK$EmjB584QDFH=d}y?o#5ss)U>Y
z2}+47h-j+%diSv+IBPLLklEaV)YXH7k<7mLQXIm|eoQKtp1AXh?ZK~bnqJ3`--&P8
zsca*e3fFda`*6U?NJ|7g?t%f^(i5+EB-#JjIRJT@?ce at xfwKcKVRLL5zp^2Z0=2Lp
zX>~!;8eQbL8M69Dt9$9`L!&+mbd|k48(e^Y;|ZgFy}k;+R(;82V?K949GDAkrJ4Bg
zJbwH>N(x4l7EPK*`@-9mOCflL>-0%&(p!Ud2=U at 1HgMDhc$ZK<RUeE&AVSWpa#4wJ
zbv*bmCevO(@9Zeskfuntib!0?fX2un8VrR9Mq$lk6d>Gm6HhYU4Cer(W*?XgGBmq?
zAmoQ^le|F}f4HRfp-Y-2(el=%{oH|klo8`hiuH?LvWS+9K#_4jcA(MHFZ(4Q<JGpx
zGoWW(J#!S?D#;u+&~bw#OdZNOEkJ~N!l;tqCp^}iZs8N^pum!7w}%m8xEJocs?H2q
z4p*zi;|_0Gbky9q#l&0hW_D^zPY5rkddOb6c2|&eQKPwW?W1^Wd&D89j(EH_y5uD{
zebDt^c?2%n8?-}g9dDf&)Tpe2Tzznq1cmMc7!;l8pf8hl!5HMFxviJ3D6A^53dU#{
z4ct}REbz*lA*!k|!7Fp}U$-F22=7e#tB%luF>>0bQS0f*LNsk at 3+#u8<7EnsyNojk
z(0TlT?DcPZN7z^UM&Hi6Iyv7c=V|30eYcCSl$BgymvcK$$vZ*1RnD=<Ni6UxCk~U(
ztgf$r;n0i}x87s2;buoI4-VNJ2NlZ^Jfi8Hj#KOQBNHP$ZjBCvvsH=Qw$2~gK-+O5
zVNc|=U6xxD$p!I;s%;1#lP!G>TAzM3obPe>U;icD+i*!eqkFsJ{{LL}cKLnT39OR)
zpXlE1Ho*wT)KSniMT!-RVtKLB*hOh_^?^HsZJ_oXLFu!?w%JgC5zfSrFu<yq<vbbf
z56LdE&#SzR*1%g8C2$R-9j$BFU^`!8>ngV0*=Q@$+6;xZ3XM&d8=}n(b>@z>=Z>@D
zqak8TZWw$xrZDG2x}d}%EnVwvciyr!thY6-vlYE<t5|0{{|6hR_*QuJsxD6hXCf?l
zT2o$;GjF3kZ-diDqGub*I?%);dnKR0D}653lDCNw3I|$6lRMUZhrA$hSN5#?0t{!4
zxOWM*PF@#t3d>tJu3NXXZU($mMVDlQfo3((Hf>aB?b`&}qACP<a2rm0tpjnX&-`GU
z7$h_nC>AUh$Ey=T${zd-FWMrC;G!~pIUb>PbO0#Fh8HazXh^dG1${ksH_ at X&7!h8K
zG%Ro6`9sFb9iSede6cVr*_OK)3BZq5y~{i+;F*K_))}1X(f}|kL1O_JRoJbBv)TAf
z2wqb$sqmT!J at DMhyuZUd%bDj!=DCS^;!QbPh4%nyRSEMfHQa1R;yOG)HAcfDC)%CY
zxEt+Cy-R+r9g#=`?h|DALE#Wq{IDFxbeH-xh2UQ8G#>ny;uZa+uj0pB_%Rzl7T^a?
z)Y2Ul{<gaJ;H?UeZTra*r#5q3_di@{^5sBqaD>7m2cM{#FbM{+%5Z7s9l5>TWVkEM
z8lxRQ?#}Lx`omiAZuByK*sCwWugd4%mcAZV8Whpvs_k+~6_rv&6?{c@;BUNk7hLrP
zVWf$kaKos3QUUu;dEE6p(KIxn=*3U?kTotoBB6=^-A<w{(x{?|T)dz0l|dhGCb$X9
z7F8s4R&?4V;MmrAm$+vDM~S3D;*$6!B(zC>kW8~2=f-}nl`OXREiT&6hh4&xpZfZI
z^3wiGvph~7;h9eK at 7-^P>^^VV&tC-L;_XDd5H{}Tj6C^wU!U5a=3I*RIQa>0)*K^j
zXadjC2}F!g5-lMP7~q`V?}$V30reZq>6Lz!McA*(up4xZK1=vWal$7Q&HIEbeuikB
zibf~AY1!}5JqFZOjE*XT<WBU`SDA at 74f*vy5MBw(5-Py0{;uV_k at X6@<msY({ar$8
z=)0<ge8ei?s?19Ve0~10w)xFxJT4vM*}v#Na}v&^!mSSVWp<)OSpSU02Kx3Tu|H|g
z@$N&=FPWhCkMXQ0AZXt+W&##_zr(21Ct4oz60U0N<L#38qI29!pZNORU3<b!*x3xB
z+d9nk=PdO$JKUQislSUAfM3<_N0*KU`RrSJ^v&7+myV7kekGK)_QSk+jxW;z&G99s
zU|hh0a}L+RMR*fsXPB%Ks|=7M$PRFad_j)uzlV%M=7y)?AA at ONGWTCe1L6{sg~rx?
zH(Qu1W4qh3d>XH$Xx at xb$e&3Kk^i(A*aCAjHtBD0r3H3<I*eEdy=g-153TVbBwSFC
zFyoEZM5)D7qY%cWbtKGxBPCwOC1&A49{P`lg&lQ19b1rBQEG?vY+uu-?c!SwpVK2g
z7{f1`*U^bpU@>z6)k9Z>qN4;3&Tc3TDjQ$N-7_I)X4$AtKGE>24n#gB=0%JmCU`f^
z=|Cecg?JRr#&IXO at 9n~OEx-xS5y)5d^WeDJFty{bPuXnHN998pWF2+hWx<`^L_=%`
zLMO$1=s6)eoL<ereU(V&vCi|U8Ox7z;wE%#)m6c@$`Q<+>SuU{aUF*_b>6#YkJM|H
z9ZB&~XcT?|4h&(Sz-&LS+nYG<9qBDxSMt_Nu+Fi2l!w07T<ZmwYDM~tgd5MewX;F#
zIb66f6)ul1qboU8<W^~`u0hy=&g}8Q>ugLPEAkhZEDz+~9TwOqo%k$Nf*C{_2W}<1
z%&+r?3(GFelIvyWv+=Ni#3jyZH&@Fl{o%GruRRm$q^&Pi at se<Ys)8$dtv|v&exOVn
zR3-z<%lz0m<&qE0#xr+S#30Vy1YCUtkHNbv!)~`TX^`J;&-k`|CmAJ}&#Uo|j-MCr
zvCfH3;f4B{aQcfN!fv)R2c%;gxKZ-i at v#j7vM_$SmM_Z?QW^frjDWan?UEjDY)=4$
z at NZh?2eU^#mMZ*#Lv&qjzlB2kBs~F$oL>t*5PQb^OFkuHq3kn=rZ3yin87 at UE$|2<
zj{Ai6t#bku9D*R0t_(ZX4rC=g88bN(gwn78Hv at S~d+bz6tfKtsCZemkz0b}y-{4*z
zYX(UfF84~D=$hbkm^8yv`d1f12cP3xTFE}A{NNOhxD~d8LhyD_pEUOT!S=rE0d1}I
zm&^KS;(_m#v28BDz8e9M%)VvBW%Q8K8`3v|=-P+rx6#C9-&1jBn;!&9A4f(NSypf?
zVPxBlfVK>RQ`>&t&~D}iv=I_wL^-&?#oM6Y|6nRF#%8w24^HHWZB5nf=5zJEH4>L!
zr^~NvdwcrGdyt&a1mc?E0UT-y=#xs?q_M^A%=yLbI<kj~OnaxjsJ#6prIJsq+}eI`
z+Nk`3`fQJmfKrbMqcz*jh%Ar9w`AZNHPa(`v}PAL=@}>7265{+N5Y+<%sqEugnUhV
zK26NvWd};0hdVTvE%sY<BOt#A=h%JmJj5w_qa8LT!&bFNdZIcKnhxB(UPr3E?+&zA
zCFwOW`GZjtp4l<+0osmNmc{B95089tHp*YCMSIu2QE;*?KmTrYIwbjn_wCF#`6T9^
z?Jz=;@I&mP9~RL~u?L!v*AexhgP^aD1Kr%Rs-jp^6QMR>H<%PGn%Xo;Pz^^<p$<Tr
zvdJY}Xlje0sqMQL;&HInb3MO>AhJdKL?wbZXWTQMj0G^xR_#6<x<phLS;pTJksZ0-
zvvoV%h|Ynp<4I7Nmx`eI<{vgU3fADYZHDgBfaLM5Q)iNp$eDp<{FxC0d0avwRQ3b3
zu5<h)qslg&-86m1U7Qh=@}X!h?=rvbQ}fxQhi1)6$vadXu&?86$E9~C^rFg%xhsCD
z0+u6)%cG91KRlxTq4})qe)Z(sS6>;?`pgx~=1R!vNjZFw?U0?hi|>3$=ECJq_g$}W
z at wN9$gQ_B0u(E-dUO3K&@9~zqgY!;-k0O#c5^hw2(*k0tI9{VRi+1%-V%jHBb|K~W
zG?x&^({o|v<;{3!Xy3tA4#vet!MKf9g&)ki1TF<!PE0)2+z+k=b2%}KTi~V{XmziU
z9`>`N?N<|4$t4>&=`#d1C(WCMvr4kLs{?ZQ+2hxYS{<JgAv at u>W}5Odzs>PI)3bcA
z-`4mX{+dx+7e95wYxE6~#|`HDE}OdmoEIz}eS_z5!!NXt4j^LU>BU6MVvieeS`<<N
z+yQ9OdoE&pg640d>f_DX+<lwPyM?p%t_~;}zifF?4!<%!e|6ECQQ3)_(xOt#x_o<4
z73RJ>{_6CsfT3_(-Ql(_&hvaK3#|@V5YI$kusF}-NIa7Sn3oRbd@$ROC!}m+ib{r|
zTRsin$a`?(cZ|(PF<KRMaAT(oCmk>=7BE<WJg(;V!u)s%!8|c^t)uN10lJ#e=H<ed
z)HC at rN6n0wiPO4#+MVfHeCz-iWpQU>RI+#sL60mFxD3=cY~XTegNeF7kw)~%38Iil
z`o#rq at E+O+w{WH5o)6raeo?9lh#ED65B+M?-1(}3es%GZH<*4E+Sx7lc-^Xu?F%S|
zLE at g2AA2VN`X=B6G+ULDK0NCHcb?=9yfOPsMMTl+t8 at AWhGyLTvOg9976BFk76BFk
z76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk
z76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk
z76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk76BFk
z76BFk76BFk76BFk76BH4|4R_?B#3ZGN*Mg3;4gvyc=$`<p8$U^_<IussNg5^N!PDJ
zz)bZcVxNJ3Ec_+#9|M0+_~(x#Vzc1SgMYIx5qkjsb?~o&KNsSyhWHXN6Tlt||8V#R
z!XLr^mk~tlxA5<T{{{G);a?B`kKkVke~8BXGy~@Us<8;tfV1>6nAz~h>H1ecobSOf
zo*n`LAAY=nANPqL`Jena|IOdOO6UJBKb(bs#cNsde2$9CBXOXdtVzGm&X7%e8s49u
zFQ0_pz?8|RGIquT4G`lHoOJ}^5Po0}gnw2g5&If|@4Ltyzy&}2HfX{t4(g}pLpeU>
z`W5!WzY9>N*c?pc0k98GGA_^Izi_4M4>`Zjd40t!NnGr-I0<eF!+($Y$4?#dpUQj0
zb9ngwFWv{ib!Rpq)kws))ey1R#DB$3BG!-h$Uf-)|Ef7y=D2PuAph8oO$-w at ah<~^
z?nn9|40itoOzi#uOdgnhU_wvK{#XQ91Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E
z1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E
z1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E
z1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E
z1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E1Xu)E
K1pW^r at IL@=F~jlz

-- 
2.6.3



From zyw at rock-chips.com  Wed Jul 27 06:07:10 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Wed, 27 Jul 2016 21:07:10 +0800
Subject: [v7 PATCH 0/6] Rockchip Type-C and DisplayPort driver
Message-ID: <1469624836-22792-1-git-send-email-zyw@rock-chips.com>


Hi all

This series patch is for rockchip Type-C phy and DisplayPort controller
driver.

The USB Type-C PHY is designed to support the USB3 and DP applications.
The PHY basically has two main components: USB3 and DisplyPort. USB3
operates in SuperSpeed mode and the DP can operate at RBR, HBR and HBR2
data rates. The Type-C cable orientation detection and Power Delivery
(PD) is accomplished using a PD PHY or a exernal PD chip.

The DP controller is compliant with DisplayPort Specification,
Version 1.3, This IP is compatible with the rockchip type-c PHY IP.
There is a uCPU in DP controller, it need a firmware to work, please
put the firmware file[0] to /lib/firmware/rockchip/dptx.bin. The uCPU
in charge of aux communication and link training, the host use mailbox
to communicate with the ucpu.

The DP contoller has register a notification with extcon API, to get the
alt mode from PD, the PD driver need call the devm_extcon_dev_allocate
to create a extcon device and use extcon_set_state to notify DP
controller. And call extcon_set_cable_property to set orientation.

About the DP audio, cdn-dp registered 2 DAIs: 0 is I2S, 1 is SPDIF.
We can reference them in simple-card.

This series is based on Mark Yao's branch[1] and Chanwoo Choi's
extcon-test branch[2]. The extcon patch is base on a test branch, so
please do not review the "1/6 extcon: Add Type-C and DP support".

I test this patches on the rk3399-evb board, with a fusb302 driver,
this branch has no rk3399.dtsi, so the patch about dts is not included
in this series.

[0]
https://patchwork.kernel.org/patch/9249693/
[1]
https://github.com/markyzq/kernel-drm-rockchip/tree/drm-rockchip-next-2016-05-23
[2]
https://git.kernel.org/cgit/linux/kernel/git/chanwoo/extcon.git/log/?h=extcon-test
- extcon: Add the extcon_type to gather each connector into five category
- extcon: Add the support for extcon property according to extcon type
- extcon: Add the support for the capability of each property
- extcon: Rename the extcon_set/get_state() to maintain the function naming
pattern
- extcon: Add the synchronization extcon APIs to support the notification


Changes in v7:
- support new API of extcon
- support firmware standby when no dptx connection
- optimization the calculation of tu size and valid symbol

Changes in v6:
- add assigned-clocks and assigned-clock-rates
- delete the support of PIN_ASSIGN_A/B
- set the default mode to MODE_DFP_USB
- disable DP PLL at USB3 only mode
- add assigned-clocks and assigned-clock-rates
- add power-domains
- add a port struct
- select SND_SOC_HDMI_CODEC
- force reset the phy when hpd detected

Changes in v5:
- support get property from extcon
- remove PIN ASSIGN A/B support
- alphabetical order
- do not use long, use u32 or u64
- return MODE_CLOCK_HIGH when requested > actual
- Optimized Coding Style
- add a formula to get better tu size and symbol value.
- modify according to Sean Paul's comments
- fixed the fw_wait always 0

Changes in v4:
- add a #phy-cells node
- select EXTCON
- use phy framework to control the USB3 and DP function
- rename PIN_MAP_ to PIN_ASSIGN_
- add a reset node
- support 2 phys
- use phy framework to control DP phy
- support 2 phys

Changes in v3:
- use compatible: rockchip,rk3399-typec-phy
- use dashes instead of underscores.
- remove the phy framework(Kishon Vijay Abraham I)
- add parentheses around the macro
- use a single space between type and name
- add spaces after opening and before closing braces.
- use u16 for register value
- remove type-c phy header file
- CodingStyle optimization
- use some cable extcon to get type-c port information
- add a extcon to notify Display Port
- add SoC specific compatible string
- remove reg = <1>;
- use EXTCON_DISP_DP and EXTCON_DISP_DP_ALT cable to get dp port state.
- reset spdif before config it
- modify the firmware clk to 100Mhz
- retry load firmware if fw file is requested too early

Changes in v2:
- add some registers description
- select RESET_CONTROLLER
- alphabetic order
- modify some spelling mistakes
- make mode cleaner
- use bool for enable/disable
- check all of the return value
- return a better err number
- use more readx_poll_timeout()
- clk_disable_unprepare(tcphy->clk_ref);
- remove unuse functions, rockchip_typec_phy_power_on/off
- remove unnecessary typecast from void *
- use dts node to distinguish between phys.
- Alphabetic order
- remove excess error message
- use define clk_rate
- check all return value
- remove dev_set_name(dp->dev, "cdn-dp");
- use schedule_delayed_work
- remove never-called functions
- remove some unnecessary ()

Changes in v1:
- add extcon node description
- move the registers in phy driver
- remove the suffix of reset
- update the licence note
- init core clock to 50MHz
- use extcon API
- remove unused global
- add some comments for magic num
- change usleep_range(1000, 2000) tousleep_range(1000, 1050)
- remove __func__ from dev_err
- return err number when get clk failed
- remove ADDR_ADJ define
- use devm_clk_get(&pdev->dev, "tcpdcore")
- add extcon node description
- add #sound-dai-cells description
- use extcon API
- use hdmi-codec for the DP Asoc
- do not initialize the "ret"
- printk a err log when drm_of_encoder_active_endpoint_id
- modify the dclk pin_pol to a single line

Chris Zhong (6):
  extcon: Add EXTCON_DISP_DP and the property for USB Type-C
  Documentation: bindings: add dt doc for Rockchip USB Type-C PHY
  phy: Add USB Type-C PHY driver for rk3399
  arm64: dts: rockchip: add Type-C phy for RK3399
  Documentation: bindings: add dt documentation for cdn DP controller
  drm/rockchip: cdn-dp: add cdn DP support for rk3399

 .../bindings/display/rockchip/cdn-dp-rockchip.txt  |  74 ++
 .../devicetree/bindings/phy/phy-rockchip-typec.txt |  84 ++
 arch/arm64/boot/dts/rockchip/rk3399.dtsi           |  44 +
 drivers/extcon/extcon.c                            |   5 +
 drivers/gpu/drm/rockchip/Kconfig                   |  10 +
 drivers/gpu/drm/rockchip/Makefile                  |   1 +
 drivers/gpu/drm/rockchip/cdn-dp-core.c             | 877 +++++++++++++++++++
 drivers/gpu/drm/rockchip/cdn-dp-core.h             | 103 +++
 drivers/gpu/drm/rockchip/cdn-dp-reg.c              | 916 ++++++++++++++++++++
 drivers/gpu/drm/rockchip/cdn-dp-reg.h              | 482 +++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c        |  13 +-
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h        |   9 +
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c        |   2 +
 drivers/phy/Kconfig                                |   9 +
 drivers/phy/Makefile                               |   1 +
 drivers/phy/phy-rockchip-typec.c                   | 961 +++++++++++++++++++++
 include/linux/extcon.h                             |   8 +-
 17 files changed, 3595 insertions(+), 4 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/display/rockchip/cdn-dp-rockchip.txt
 create mode 100644 Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.c
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.h
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.c
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.h
 create mode 100644 drivers/phy/phy-rockchip-typec.c

-- 
2.6.3



From zyw at rock-chips.com  Wed Jul 27 06:07:11 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Wed, 27 Jul 2016 21:07:11 +0800
Subject: [v7 PATCH 1/6] extcon: Add EXTCON_DISP_DP and the property for USB
 Type-C
In-Reply-To: <1469624836-22792-1-git-send-email-zyw@rock-chips.com>
References: <1469624836-22792-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1469624836-22792-2-git-send-email-zyw@rock-chips.com>

Add EXTCON_DISP_DP for the Display external connector. For Type-C
connector the DisplayPort can work as an Alternate Mode(VESA DisplayPort
Alt Mode on USB Type-C Standard). The Type-C support both normal
and flipped orientation, so add a property to extcon.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
Signed-off-by: Chanwoo Choi <cw00.choi at samsung.com>
---

Changes in v7: None
Changes in v6: None
Changes in v5: None
Changes in v4: None
Changes in v3: None
Changes in v2: None
Changes in v1: None

 drivers/extcon/extcon.c | 5 +++++
 include/linux/extcon.h  | 8 +++++++-
 2 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c
index 68890bb..6c7488a 100644
--- a/drivers/extcon/extcon.c
+++ b/drivers/extcon/extcon.c
@@ -157,6 +157,11 @@ struct __extcon_info {
 		.id = EXTCON_DISP_VGA,
 		.name = "VGA",
 	},
+	[EXTCON_DISP_DP] = {
+		.type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB,
+		.id = EXTCON_DISP_DP,
+		.name = "DP",
+	},
 
 	/* Miscellaneous external connector */
 	[EXTCON_DOCK] = {
diff --git a/include/linux/extcon.h b/include/linux/extcon.h
index f686204..9670d3f 100644
--- a/include/linux/extcon.h
+++ b/include/linux/extcon.h
@@ -69,6 +69,7 @@
 #define EXTCON_DISP_MHL		41	/* Mobile High-Definition Link */
 #define EXTCON_DISP_DVI		42	/* Digital Visual Interface */
 #define EXTCON_DISP_VGA		43	/* Video Graphics Array */
+#define EXTCON_DISP_DP		44	/* Display Port */
 
 /* Miscellaneous external connector */
 #define EXTCON_DOCK		60
@@ -106,12 +107,17 @@
  * @type:	integer (intval)
  * @value:	0 (low) or 1 (high)
  * @default:	0 (low)
+ * - EXTCON_PROP_USB_TYPEC_POLARITY
+ * @type:	integer (intval)
+ * @value:	0 (normal) or 1 (flip)
+ * @default:	0 (normal)
  */
 #define EXTCON_PROP_USB_ID		0
 #define EXTCON_PROP_USB_VBUS		1
+#define EXTCON_PROP_USB_TYPEC_POLARITY	2
 
 #define EXTCON_PROP_USB_MIN		0
-#define EXTCON_PROP_USB_MAX		1
+#define EXTCON_PROP_USB_MAX		2
 #define EXTCON_PROP_USB_CNT	(EXTCON_PROP_USB_MAX - EXTCON_PROP_USB_MIN + 1)
 
 /* Properties of EXTCON_TYPE_CHG. */
-- 
2.6.3



From zyw at rock-chips.com  Wed Jul 27 06:07:12 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Wed, 27 Jul 2016 21:07:12 +0800
Subject: [v7 PATCH 2/6] Documentation: bindings: add dt doc for Rockchip USB
 Type-C PHY
In-Reply-To: <1469624836-22792-1-git-send-email-zyw@rock-chips.com>
References: <1469624836-22792-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1469624836-22792-3-git-send-email-zyw@rock-chips.com>

This patch adds a binding that describes the Rockchip USB Type-C PHY
for rk3399

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
Reviewed-by: Tomasz Figa <tfiga at chromium.org>
Reviewed-by: Kever Yang <kever.yang at rock-chips.com>
Acked-by: Rob Herring <robh at kernel.org>

---

Changes in v7: None
Changes in v6:
- add assigned-clocks and assigned-clock-rates

Changes in v5: None
Changes in v4:
- add a #phy-cells node

Changes in v3:
- use compatible: rockchip,rk3399-typec-phy
- use dashes instead of underscores.

Changes in v2:
- add some registers description

Changes in v1:
- add extcon node description
- move the registers in phy driver
- remove the suffix of reset

 .../devicetree/bindings/phy/phy-rockchip-typec.txt | 84 ++++++++++++++++++++++
 1 file changed, 84 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt

diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt b/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt
new file mode 100644
index 0000000..e384056
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt
@@ -0,0 +1,84 @@
+* ROCKCHIP type-c PHY
+---------------------
+
+Required properties:
+ - compatible : must be "rockchip,rk3399-typec-phy"
+ - reg: Address and length of the usb phy control register set
+ - rockchip,grf : phandle to the syscon managing the "general
+   register files"
+ - clocks : phandle + clock specifier for the phy clocks
+ - clock-names : string, clock name, must be "tcpdcore", "tcpdphy-ref";
+ - assigned-clocks: main clock, should be <&cru SCLK_UPHY0_TCPDCORE> or
+		    <&cru SCLK_UPHY1_TCPDCORE>;
+ - assigned-clock-rates : the phy core clk frequency, shall be: 50000000
+ - resets : a list of phandle + reset specifier pairs
+ - reset-names : string reset name, must be:
+		 "uphy", "uphy-pipe", "uphy-tcphy"
+ - extcon : extcon specifier for the Power Delivery
+ - #phy-cells: must be 0. See ./phy-bindings.txt for details.
+
+Note, there are 2 type-c phys for RK3399, and they are almost identical, except
+these registers(description below), every register node contains 3 sections:
+offset, enable bit, write mask bit.
+ - rockchip,typec-conn-dir : the register of type-c connector direction,
+   for type-c phy0, it must be <0xe580 0 16>;
+   for type-c phy1, it must be <0xe58c 0 16>;
+ - rockchip,usb3tousb2-en : the register of type-c force usb3 to usb2 enable
+   control.
+   for type-c phy0, it must be <0xe580 3 19>;
+   for type-c phy1, it must be <0xe58c 3 19>;
+ - rockchip,external-psm : the register of type-c phy external psm clock
+   selection.
+   for type-c phy0, it must be <0xe588 14 30>;
+   for type-c phy1, it must be <0xe594 14 30>;
+ - rockchip,pipe-status : the register of type-c phy pipe status.
+   for type-c phy0, it must be <0xe5c0 0 0>;
+   for type-c phy1, it must be <0xe5c0 16 16>;
+ - rockchip,uphy-dp-sel : the register of type-c phy selection for DP
+   for type-c phy0, it must be <0x6268 19 19>;
+   for type-c phy1, it must be <0x6268 3 19>;
+
+Example:
+	tcphy0: phy at ff7c0000 {
+		compatible = "rockchip,rk3399-typec-phy";
+		reg = <0x0 0xff7c0000 0x0 0x40000>;
+		rockchip,grf = <&grf>;
+		#phy-cells = <0>;
+		extcon = <&fusb0>;
+		clocks = <&cru SCLK_UPHY0_TCPDCORE>,
+			 <&cru SCLK_UPHY0_TCPDPHY_REF>;
+		clock-names = "tcpdcore", "tcpdphy-ref";
+		assigned-clocks = <&cru SCLK_UPHY0_TCPDCORE>;
+		assigned-clock-rates = <50000000>;
+		resets = <&cru SRST_UPHY0>,
+			 <&cru SRST_UPHY0_PIPE_L00>,
+			 <&cru SRST_P_UPHY0_TCPHY>;
+		reset-names = "uphy", "uphy-pipe", "uphy-tcphy";
+		rockchip,typec-conn-dir = <0xe580 0 16>;
+		rockchip,usb3tousb2-en = <0xe580 3 19>;
+		rockchip,external-psm = <0xe588 14 30>;
+		rockchip,pipe-status = <0xe5c0 0 0>;
+		rockchip,uphy-dp-sel = <0x6268 19 19>;
+	};
+
+	tcphy1: phy at ff800000 {
+		compatible = "rockchip,rk3399-typec-phy";
+		reg = <0x0 0xff800000 0x0 0x40000>;
+		rockchip,grf = <&grf>;
+		#phy-cells = <0>;
+		extcon = <&fusb1>;
+		clocks = <&cru SCLK_UPHY1_TCPDCORE>,
+			 <&cru SCLK_UPHY1_TCPDPHY_REF>;
+		clock-names = "tcpdcore", "tcpdphy-ref";
+		assigned-clocks = <&cru SCLK_UPHY1_TCPDCORE>;
+		assigned-clock-rates = <50000000>;
+		resets = <&cru SRST_UPHY1>,
+		         <&cru SRST_UPHY1_PIPE_L00>,
+			 <&cru SRST_P_UPHY1_TCPHY>;
+		reset-names = "uphy", "uphy-pipe", "uphy-tcphy";
+		rockchip,typec-conn-dir = <0xe58c 0 16>;
+		rockchip,usb3tousb2-en = <0xe58c 3 19>;
+		rockchip,external-psm = <0xe594 14 30>;
+		rockchip,pipe-status = <0xe5c0 16 16>;
+		rockchip,uphy-dp-sel = <0x6268 3 19>;
+	};
-- 
2.6.3



From zyw at rock-chips.com  Wed Jul 27 06:07:14 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Wed, 27 Jul 2016 21:07:14 +0800
Subject: [v7 PATCH 4/6] arm64: dts: rockchip: add Type-C phy for RK3399
In-Reply-To: <1469624836-22792-1-git-send-email-zyw@rock-chips.com>
References: <1469624836-22792-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1469624836-22792-5-git-send-email-zyw@rock-chips.com>

There are 2 Type-C phy on RK3399, they are almost same, except the
address of register. They support USB3.0 Type-C and DisplayPort1.3
Alt Mode on USB Type-C. Register a phy, supply it to USB3 controller
and DP controller.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
---

Changes in v7: None
Changes in v6: None
Changes in v5: None
Changes in v4: None
Changes in v3: None
Changes in v2: None
Changes in v1: None

 arch/arm64/boot/dts/rockchip/rk3399.dtsi | 44 ++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index 0af03a3..7095ddf 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -952,6 +952,50 @@
 		};
 	};
 
+	tcphy0: phy at ff7c0000 {
+		compatible = "rockchip,rk3399-typec-phy";
+		reg = <0x0 0xff7c0000 0x0 0x40000>;
+		rockchip,grf = <&grf>;
+		#phy-cells = <0>;
+		clocks = <&cru SCLK_UPHY0_TCPDCORE>,
+			 <&cru SCLK_UPHY0_TCPDPHY_REF>;
+		clock-names = "tcpdcore", "tcpdphy-ref";
+		assigned-clocks = <&cru SCLK_UPHY0_TCPDCORE>;
+		assigned-clock-rates = <50000000>;
+		resets = <&cru SRST_UPHY0>,
+			 <&cru SRST_UPHY0_PIPE_L00>,
+			 <&cru SRST_P_UPHY0_TCPHY>;
+		reset-names = "uphy", "uphy-pipe", "uphy-tcphy";
+		rockchip,typec-conn-dir = <0xe580 0 16>;
+		rockchip,usb3tousb2-en = <0xe580 3 19>;
+		rockchip,external-psm = <0xe588 14 30>;
+		rockchip,pipe-status = <0xe5c0 0 0>;
+		rockchip,uphy-dp-sel = <0x6268 19 19>;
+		status = "disabled";
+	};
+
+	tcphy1: phy at ff800000 {
+		compatible = "rockchip,rk3399-typec-phy";
+		reg = <0x0 0xff800000 0x0 0x40000>;
+		rockchip,grf = <&grf>;
+		#phy-cells = <0>;
+		clocks = <&cru SCLK_UPHY1_TCPDCORE>,
+			 <&cru SCLK_UPHY1_TCPDPHY_REF>;
+		clock-names = "tcpdcore", "tcpdphy-ref";
+		assigned-clocks = <&cru SCLK_UPHY1_TCPDCORE>;
+		assigned-clock-rates = <50000000>;
+		resets = <&cru SRST_UPHY1>,
+			 <&cru SRST_UPHY1_PIPE_L00>,
+			 <&cru SRST_P_UPHY1_TCPHY>;
+		reset-names = "uphy", "uphy-pipe", "uphy-tcphy";
+		rockchip,typec-conn-dir = <0xe58c 0 16>;
+		rockchip,usb3tousb2-en = <0xe58c 3 19>;
+		rockchip,external-psm = <0xe594 14 30>;
+		rockchip,pipe-status = <0xe5c0 16 16>;
+		rockchip,uphy-dp-sel = <0x6268 3 19>;
+		status = "disabled";
+	};
+
 	watchdog at ff840000 {
 		compatible = "snps,dw-wdt";
 		reg = <0x0 0xff840000 0x0 0x100>;
-- 
2.6.3



From zyw at rock-chips.com  Wed Jul 27 06:07:13 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Wed, 27 Jul 2016 21:07:13 +0800
Subject: [v7 PATCH 3/6] phy: Add USB Type-C PHY driver for rk3399
In-Reply-To: <1469624836-22792-1-git-send-email-zyw@rock-chips.com>
References: <1469624836-22792-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1469624836-22792-4-git-send-email-zyw@rock-chips.com>

Add a PHY provider driver for the rk3399 SoC Type-c PHY. The USB
Type-C PHY is designed to support the USB3 and DP applications. The
PHY basically has two main components: USB3 and DisplyPort. USB3
operates in SuperSpeed mode and the DP can operate at RBR, HBR and
HBR2 data rates.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
Signed-off-by: Kever Yang <kever.yang at rock-chips.com>

---

Changes in v7:
- support new API of extcon

Changes in v6:
- delete the support of PIN_ASSIGN_A/B
- set the default mode to MODE_DFP_USB
- disable DP PLL at USB3 only mode

Changes in v5:
- support get property from extcon
- remove PIN ASSIGN A/B support

Changes in v4:
- select EXTCON
- use phy framework to control the USB3 and DP function
- rename PIN_MAP_ to PIN_ASSIGN_

Changes in v3:
- remove the phy framework(Kishon Vijay Abraham I)
- add parentheses around the macro
- use a single space between type and name
- add spaces after opening and before closing braces.
- use u16 for register value
- remove type-c phy header file
- CodingStyle optimization
- use some cable extcon to get type-c port information
- add a extcon to notify Display Port

Changes in v2:
- select RESET_CONTROLLER
- alphabetic order
- modify some spelling mistakes
- make mode cleaner
- use bool for enable/disable
- check all of the return value
- return a better err number
- use more readx_poll_timeout()
- clk_disable_unprepare(tcphy->clk_ref);
- remove unuse functions, rockchip_typec_phy_power_on/off
- remove unnecessary typecast from void *
- use dts node to distinguish between phys.

Changes in v1:
- update the licence note
- init core clock to 50MHz
- use extcon API
- remove unused global
- add some comments for magic num
- change usleep_range(1000, 2000) tousleep_range(1000, 1050)
- remove __func__ from dev_err
- return err number when get clk failed
- remove ADDR_ADJ define
- use devm_clk_get(&pdev->dev, "tcpdcore")

 drivers/phy/Kconfig              |   9 +
 drivers/phy/Makefile             |   1 +
 drivers/phy/phy-rockchip-typec.c | 961 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 971 insertions(+)
 create mode 100644 drivers/phy/phy-rockchip-typec.c

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 26566db..83706a5 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -351,6 +351,15 @@ config PHY_ROCKCHIP_DP
 	help
 	  Enable this to support the Rockchip Display Port PHY.
 
+config PHY_ROCKCHIP_TYPEC
+	tristate "Rockchip TYPEC PHY Driver"
+	depends on ARCH_ROCKCHIP && OF
+	select EXTCON
+	select GENERIC_PHY
+	select RESET_CONTROLLER
+	help
+	  Enable this to support the Rockchip USB TYPEC PHY.
+
 config PHY_ST_SPEAR1310_MIPHY
 	tristate "ST SPEAR1310-MIPHY driver"
 	select GENERIC_PHY
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 24596a9..91fa413 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_PHY_QCOM_APQ8064_SATA)	+= phy-qcom-apq8064-sata.o
 obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o
 obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o
 obj-$(CONFIG_PHY_ROCKCHIP_DP)		+= phy-rockchip-dp.o
+obj-$(CONFIG_PHY_ROCKCHIP_TYPEC) += phy-rockchip-typec.o
 obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA)	+= phy-qcom-ipq806x-sata.o
 obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY)	+= phy-spear1310-miphy.o
 obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY)	+= phy-spear1340-miphy.o
diff --git a/drivers/phy/phy-rockchip-typec.c b/drivers/phy/phy-rockchip-typec.c
new file mode 100644
index 0000000..278fc0c
--- /dev/null
+++ b/drivers/phy/phy-rockchip-typec.c
@@ -0,0 +1,961 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Chris Zhong <zyw at rock-chips.com>
+ *         Kever Yang <kever.yang at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/extcon.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/mfd/syscon.h>
+#include <linux/phy/phy.h>
+
+#define CMN_SSM_BANDGAP			(0x21 << 2)
+#define CMN_SSM_BIAS			(0x22 << 2)
+#define CMN_PLLSM0_PLLEN		(0x29 << 2)
+#define CMN_PLLSM0_PLLPRE		(0x2a << 2)
+#define CMN_PLLSM0_PLLVREF		(0x2b << 2)
+#define CMN_PLLSM0_PLLLOCK		(0x2c << 2)
+#define CMN_PLLSM1_PLLEN		(0x31 << 2)
+#define CMN_PLLSM1_PLLPRE		(0x32 << 2)
+#define CMN_PLLSM1_PLLVREF		(0x33 << 2)
+#define CMN_PLLSM1_PLLLOCK		(0x34 << 2)
+#define CMN_PLLSM1_USER_DEF_CTRL	(0x37 << 2)
+#define CMN_ICAL_OVRD			(0xc1 << 2)
+#define CMN_PLL0_VCOCAL_OVRD		(0x83 << 2)
+#define CMN_PLL0_VCOCAL_INIT		(0x84 << 2)
+#define CMN_PLL0_VCOCAL_ITER		(0x85 << 2)
+#define CMN_PLL0_LOCK_REFCNT_START	(0x90 << 2)
+#define CMN_PLL0_LOCK_PLLCNT_START	(0x92 << 2)
+#define CMN_PLL0_LOCK_PLLCNT_THR	(0x93 << 2)
+#define CMN_PLL0_INTDIV			(0x94 << 2)
+#define CMN_PLL0_FRACDIV		(0x95 << 2)
+#define CMN_PLL0_HIGH_THR		(0x96 << 2)
+#define CMN_PLL0_DSM_DIAG		(0x97 << 2)
+#define CMN_PLL0_SS_CTRL1		(0x98 << 2)
+#define CMN_PLL0_SS_CTRL2		(0x99 << 2)
+#define CMN_PLL1_VCOCAL_START		(0xa1 << 2)
+#define CMN_PLL1_VCOCAL_OVRD		(0xa3 << 2)
+#define CMN_PLL1_VCOCAL_INIT		(0xa4 << 2)
+#define CMN_PLL1_VCOCAL_ITER		(0xa5 << 2)
+#define CMN_PLL1_LOCK_REFCNT_START	(0xb0 << 2)
+#define CMN_PLL1_LOCK_PLLCNT_START	(0xb2 << 2)
+#define CMN_PLL1_LOCK_PLLCNT_THR	(0xb3 << 2)
+#define CMN_PLL1_INTDIV			(0xb4 << 2)
+#define CMN_PLL1_FRACDIV		(0xb5 << 2)
+#define CMN_PLL1_HIGH_THR		(0xb6 << 2)
+#define CMN_PLL1_DSM_DIAG		(0xb7 << 2)
+#define CMN_PLL1_SS_CTRL1		(0xb8 << 2)
+#define CMN_PLL1_SS_CTRL2		(0xb9 << 2)
+#define CMN_RXCAL_OVRD			(0xd1 << 2)
+#define CMN_TXPUCAL_CTRL		(0xe0 << 2)
+#define CMN_TXPUCAL_OVRD		(0xe1 << 2)
+#define CMN_TXPDCAL_OVRD		(0xf1 << 2)
+#define CMN_DIAG_PLL0_FBH_OVRD		(0x1c0 << 2)
+#define CMN_DIAG_PLL0_FBL_OVRD		(0x1c1 << 2)
+#define CMN_DIAG_PLL0_OVRD		(0x1c2 << 2)
+#define CMN_DIAG_PLL0_V2I_TUNE		(0x1c5 << 2)
+#define CMN_DIAG_PLL0_CP_TUNE		(0x1c6 << 2)
+#define CMN_DIAG_PLL0_LF_PROG		(0x1c7 << 2)
+#define CMN_DIAG_PLL1_FBH_OVRD		(0x1d0 << 2)
+#define CMN_DIAG_PLL1_FBL_OVRD		(0x1d1 << 2)
+#define CMN_DIAG_PLL1_OVRD		(0x1d2 << 2)
+#define CMN_DIAG_PLL1_V2I_TUNE		(0x1d5 << 2)
+#define CMN_DIAG_PLL1_CP_TUNE		(0x1d6 << 2)
+#define CMN_DIAG_PLL1_LF_PROG		(0x1d7 << 2)
+#define CMN_DIAG_PLL1_PTATIS_TUNE1	(0x1d8 << 2)
+#define CMN_DIAG_PLL1_PTATIS_TUNE2	(0x1d9 << 2)
+#define CMN_DIAG_PLL1_INCLK_CTRL	(0x1da << 2)
+#define CMN_DIAG_HSCLK_SEL		(0x1e0 << 2)
+
+#define XCVR_PSM_RCTRL(n)		((0x4001 | ((n) << 9)) << 2)
+#define XCVR_PSM_CAL_TMR(n)		((0x4002 | ((n) << 9)) << 2)
+#define XCVR_PSM_A0IN_TMR(n)		((0x4003 | ((n) << 9)) << 2)
+#define TX_TXCC_CAL_SCLR_MULT(n)	((0x4047 | ((n) << 9)) << 2)
+#define TX_TXCC_CPOST_MULT_00(n)	((0x404c | ((n) << 9)) << 2)
+#define TX_TXCC_CPOST_MULT_01(n)	((0x404d | ((n) << 9)) << 2)
+#define TX_TXCC_CPOST_MULT_10(n)	((0x404e | ((n) << 9)) << 2)
+#define TX_TXCC_CPOST_MULT_11(n)	((0x404f | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_000(n)	((0x4050 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_001(n)	((0x4051 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_010(n)	((0x4052 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_011(n)	((0x4053 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_100(n)	((0x4054 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_101(n)	((0x4055 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_110(n)	((0x4056 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_111(n)	((0x4057 | ((n) << 9)) << 2)
+#define XCVR_DIAG_PLLDRC_CTRL(n)	((0x40e0 | ((n) << 9)) << 2)
+#define XCVR_DIAG_BIDI_CTRL(n)		((0x40e8 | ((n) << 9)) << 2)
+#define XCVR_DIAG_LANE_FCM_EN_MGN(n)	((0x40f2 | ((n) << 9)) << 2)
+#define TX_PSC_A0(n)			((0x4100 | ((n) << 9)) << 2)
+#define TX_PSC_A1(n)			((0x4101 | ((n) << 9)) << 2)
+#define TX_PSC_A2(n)			((0x4102 | ((n) << 9)) << 2)
+#define TX_PSC_A3(n)			((0x4103 | ((n) << 9)) << 2)
+#define TX_RCVDET_CTRL(n)		((0x4120 | ((n) << 9)) << 2)
+#define TX_RCVDET_EN_TMR(n)		((0x4122 | ((n) << 9)) << 2)
+#define TX_RCVDET_ST_TMR(n)		((0x4123 | ((n) << 9)) << 2)
+#define TX_DIAG_TX_DRV(n)		((0x41e1 | ((n) << 9)) << 2)
+#define TX_DIAG_BGREF_PREDRV_DELAY	(0x41e7 << 2)
+#define TX_ANA_CTRL_REG_1		(0x5020 << 2)
+#define TX_ANA_CTRL_REG_2		(0x5021 << 2)
+#define TXDA_COEFF_CALC_CTRL		(0x5022 << 2)
+#define TX_DIG_CTRL_REG_2		(0x5024 << 2)
+#define TXDA_CYA_AUXDA_CYA		(0x5025 << 2)
+#define TX_ANA_CTRL_REG_3		(0x5026 << 2)
+#define TX_ANA_CTRL_REG_4		(0x5027 << 2)
+#define TX_ANA_CTRL_REG_5		(0x5029 << 2)
+
+#define RX_PSC_A0(n)			((0x8000 | ((n) << 9)) << 2)
+#define RX_PSC_A1(n)			((0x8001 | ((n) << 9)) << 2)
+#define RX_PSC_A2(n)			((0x8002 | ((n) << 9)) << 2)
+#define RX_PSC_A3(n)			((0x8003 | ((n) << 9)) << 2)
+#define RX_PSC_CAL(n)			((0x8006 | ((n) << 9)) << 2)
+#define RX_PSC_RDY(n)			((0x8007 | ((n) << 9)) << 2)
+#define RX_IQPI_ILL_CAL_OVRD		(0x8023 << 2)
+#define RX_EPI_ILL_CAL_OVRD		(0x8033 << 2)
+#define RX_SDCAL0_OVRD			(0x8041 << 2)
+#define RX_SDCAL1_OVRD			(0x8049 << 2)
+#define RX_SLC_INIT			(0x806d << 2)
+#define RX_SLC_RUN			(0x806e << 2)
+#define RX_CDRLF_CNFG2			(0x8081 << 2)
+#define RX_SIGDET_HL_FILT_TMR(n)	((0x8090 | ((n) << 9)) << 2)
+#define RX_SLC_IOP0_OVRD		(0x8101 << 2)
+#define RX_SLC_IOP1_OVRD		(0x8105 << 2)
+#define RX_SLC_QOP0_OVRD		(0x8109 << 2)
+#define RX_SLC_QOP1_OVRD		(0x810d << 2)
+#define RX_SLC_EOP0_OVRD		(0x8111 << 2)
+#define RX_SLC_EOP1_OVRD		(0x8115 << 2)
+#define RX_SLC_ION0_OVRD		(0x8119 << 2)
+#define RX_SLC_ION1_OVRD		(0x811d << 2)
+#define RX_SLC_QON0_OVRD		(0x8121 << 2)
+#define RX_SLC_QON1_OVRD		(0x8125 << 2)
+#define RX_SLC_EON0_OVRD		(0x8129 << 2)
+#define RX_SLC_EON1_OVRD		(0x812d << 2)
+#define RX_SLC_IEP0_OVRD		(0x8131 << 2)
+#define RX_SLC_IEP1_OVRD		(0x8135 << 2)
+#define RX_SLC_QEP0_OVRD		(0x8139 << 2)
+#define RX_SLC_QEP1_OVRD		(0x813d << 2)
+#define RX_SLC_EEP0_OVRD		(0x8141 << 2)
+#define RX_SLC_EEP1_OVRD		(0x8145 << 2)
+#define RX_SLC_IEN0_OVRD		(0x8149 << 2)
+#define RX_SLC_IEN1_OVRD		(0x814d << 2)
+#define RX_SLC_QEN0_OVRD		(0x8151 << 2)
+#define RX_SLC_QEN1_OVRD		(0x8155 << 2)
+#define RX_SLC_EEN0_OVRD		(0x8159 << 2)
+#define RX_SLC_EEN1_OVRD		(0x815d << 2)
+#define RX_DIAG_SIGDET_TUNE(n)		((0x81dc | ((n) << 9)) << 2)
+#define RX_DIAG_SC2C_DELAY		(0x81e1 << 2)
+
+#define PMA_LANE_CFG			(0xc000 << 2)
+#define PIPE_CMN_CTRL1			(0xc001 << 2)
+#define PIPE_CMN_CTRL2			(0xc002 << 2)
+#define PIPE_COM_LOCK_CFG1		(0xc003 << 2)
+#define PIPE_COM_LOCK_CFG2		(0xc004 << 2)
+#define PIPE_RCV_DET_INH		(0xc005 << 2)
+#define DP_MODE_CTL			(0xc008 << 2)
+#define DP_CLK_CTL			(0xc009 << 2)
+#define STS				(0xc00F << 2)
+#define PHY_ISO_CMN_CTRL		(0xc010 << 2)
+#define PHY_DP_TX_CTL			(0xc408 << 2)
+#define PMA_CMN_CTRL1			(0xc800 << 2)
+#define PHY_PMA_ISO_CMN_CTRL		(0xc810 << 2)
+#define PHY_ISOLATION_CTRL		(0xc81f << 2)
+#define PHY_PMA_ISO_XCVR_CTRL(n)	((0xcc11 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_LINK_MODE(n)	((0xcc12 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_PWRST_CTRL(n)	((0xcc13 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_TX_DATA_LO(n)	((0xcc14 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_TX_DATA_HI(n)	((0xcc15 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_RX_DATA_LO(n)	((0xcc16 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_RX_DATA_HI(n)	((0xcc17 | ((n) << 6)) << 2)
+#define TX_BIST_CTRL(n)			((0x4140 | ((n) << 9)) << 2)
+#define TX_BIST_UDDWR(n)		((0x4141 | ((n) << 9)) << 2)
+
+/*
+ * Selects which PLL clock will be driven on the analog high speed
+ * clock 0: PLL 0 div 1
+ * clock 1: PLL 1 div 2
+ */
+#define CLK_PLL_CONFIG			0X30
+#define CLK_PLL_MASK			0x33
+
+#define CMN_READY			BIT(0)
+
+#define DP_PLL_CLOCK_ENABLE		BIT(2)
+#define DP_PLL_ENABLE			BIT(0)
+#define DP_PLL_DATA_RATE_RBR		((2 << 12) | (4 << 8))
+#define DP_PLL_DATA_RATE_HBR		((2 << 12) | (4 << 8))
+#define DP_PLL_DATA_RATE_HBR2		((1 << 12) | (2 << 8))
+
+#define GRF_SOC_CON26			0x6268
+#define UPHY_DP_SEL			BIT(3)
+#define UPHY_DP_SEL_MASK		BIT(19)
+#define DPTX_HPD_SEL			(3 << 12)
+#define DPTX_HPD_DEL			(2 << 12)
+#define DPTX_HPD_SEL_MASK		(3 << 28)
+
+#define PHY_MODE_SET_TIMEOUT		1000000
+
+#define PIN_ASSIGN_A			BIT(0)
+#define PIN_ASSIGN_B			BIT(1)
+#define PIN_ASSIGN_C			BIT(2)
+#define PIN_ASSIGN_D			BIT(3)
+#define PIN_ASSIGN_E			BIT(4)
+#define PIN_ASSIGN_F			BIT(5)
+
+#define MODE_DISCONNECT			0
+#define MODE_UFP_USB			BIT(0)
+#define MODE_DFP_USB			BIT(1)
+#define MODE_DFP_DP			BIT(2)
+
+struct usb3phy_reg {
+	u32 offset;
+	u32 enable_bit;
+	u32 write_enable;
+};
+
+struct rockchip_usb3phy_port_cfg {
+	struct usb3phy_reg typec_conn_dir;
+	struct usb3phy_reg usb3tousb2_en;
+	struct usb3phy_reg external_psm;
+	struct usb3phy_reg pipe_status;
+	struct usb3phy_reg uphy_dp_sel;
+};
+
+struct rockchip_typec_phy {
+	struct device *dev;
+	void __iomem *base;
+	struct extcon_dev *extcon;
+	struct phy *phy;
+	struct regmap *grf_regs;
+	struct clk *clk_core;
+	struct clk *clk_ref;
+	struct reset_control *uphy_rst;
+	struct reset_control *pipe_rst;
+	struct reset_control *tcphy_rst;
+	struct rockchip_usb3phy_port_cfg port_cfgs;
+
+	bool flip;
+	u8 mode;
+	u8 pin_assign;
+};
+
+struct phy_reg {
+	u16 value;
+	u32 addr;
+};
+
+struct phy_reg usb_pll_cfg[] = {
+	{ 0xf0,		CMN_PLL0_VCOCAL_INIT },
+	{ 0x18,		CMN_PLL0_VCOCAL_ITER },
+	{ 0xd0,		CMN_PLL0_INTDIV },
+	{ 0x4a4a,	CMN_PLL0_FRACDIV },
+	{ 0x34,		CMN_PLL0_HIGH_THR },
+	{ 0x1ee,	CMN_PLL0_SS_CTRL1 },
+	{ 0x7f03,	CMN_PLL0_SS_CTRL2 },
+	{ 0x20,		CMN_PLL0_DSM_DIAG },
+	{ 0,		CMN_DIAG_PLL0_OVRD },
+	{ 0,		CMN_DIAG_PLL0_FBH_OVRD },
+	{ 0,		CMN_DIAG_PLL0_FBL_OVRD },
+	{ 0x7,		CMN_DIAG_PLL0_V2I_TUNE },
+	{ 0x45,		CMN_DIAG_PLL0_CP_TUNE },
+	{ 0x8,		CMN_DIAG_PLL0_LF_PROG },
+};
+
+struct phy_reg dp_pll_cfg[] = {
+	{ 0xf0,		CMN_PLL1_VCOCAL_INIT },
+	{ 0x18,		CMN_PLL1_VCOCAL_ITER },
+	{ 0x30b9,	CMN_PLL1_VCOCAL_START },
+	{ 0x21c,	CMN_PLL1_INTDIV },
+	{ 0,		CMN_PLL1_FRACDIV },
+	{ 0x5,		CMN_PLL1_HIGH_THR },
+	{ 0x35,		CMN_PLL1_SS_CTRL1 },
+	{ 0x7f1e,	CMN_PLL1_SS_CTRL2 },
+	{ 0x20,		CMN_PLL1_DSM_DIAG },
+	{ 0,		CMN_PLLSM1_USER_DEF_CTRL },
+	{ 0,		CMN_DIAG_PLL1_OVRD },
+	{ 0,		CMN_DIAG_PLL1_FBH_OVRD },
+	{ 0,		CMN_DIAG_PLL1_FBL_OVRD },
+	{ 0x6,		CMN_DIAG_PLL1_V2I_TUNE },
+	{ 0x45,		CMN_DIAG_PLL1_CP_TUNE },
+	{ 0x8,		CMN_DIAG_PLL1_LF_PROG },
+	{ 0x100,	CMN_DIAG_PLL1_PTATIS_TUNE1 },
+	{ 0x7,		CMN_DIAG_PLL1_PTATIS_TUNE2 },
+	{ 0x4,		CMN_DIAG_PLL1_INCLK_CTRL },
+};
+
+static void tcphy_cfg_24m(struct rockchip_typec_phy *tcphy,
+			  u32 num_lanes)
+{
+	u32 i, rdata;
+
+	/*
+	 * cmn_ref_clk_sel = 3, select the 24Mhz for clk parent
+	 * cmn_psm_clk_dig_div = 2, set the clk division to 2
+	 */
+	writel(0x830, tcphy->base + PMA_CMN_CTRL1);
+	for (i = 0; i < num_lanes; i++) {
+		/*
+		 * The following PHY configuration assumes a 24 MHz reference
+		 * clock.
+		 */
+		writel(0x90, tcphy->base + XCVR_DIAG_LANE_FCM_EN_MGN(i));
+		writel(0x960, tcphy->base + TX_RCVDET_EN_TMR(i));
+		writel(0x30, tcphy->base + TX_RCVDET_ST_TMR(i));
+	}
+
+	rdata = readl(tcphy->base + CMN_DIAG_HSCLK_SEL);
+	rdata &= ~CLK_PLL_MASK;
+	rdata |= CLK_PLL_CONFIG;
+	writel(rdata, tcphy->base + CMN_DIAG_HSCLK_SEL);
+
+}
+
+static void tcphy_cfg_usb_pll(struct rockchip_typec_phy *tcphy)
+{
+	u32 i;
+
+	/* load the configuration of PLL0 */
+	for (i = 0; i < ARRAY_SIZE(usb_pll_cfg); i++)
+		writel(usb_pll_cfg[i].value, tcphy->base + usb_pll_cfg[i].addr);
+}
+
+static void tcphy_cfg_dp_pll(struct rockchip_typec_phy *tcphy)
+{
+	u32 i;
+
+	/* set the default mode to RBR */
+	writel(DP_PLL_CLOCK_ENABLE | DP_PLL_ENABLE | DP_PLL_DATA_RATE_RBR,
+	       tcphy->base + DP_CLK_CTL);
+
+	/* load the configuration of PLL1 */
+	for (i = 0; i < ARRAY_SIZE(dp_pll_cfg); i++)
+		writel(dp_pll_cfg[i].value, tcphy->base + dp_pll_cfg[i].addr);
+}
+
+static void tcphy_tx_usb_cfg_lane(struct rockchip_typec_phy *tcphy,
+				  u32 lane)
+{
+	writel(0x7799, tcphy->base + TX_PSC_A0(lane));
+	writel(0x7798, tcphy->base + TX_PSC_A1(lane));
+	writel(0x5098, tcphy->base + TX_PSC_A2(lane));
+	writel(0x5098, tcphy->base + TX_PSC_A3(lane));
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
+	writel(0xbf, tcphy->base + XCVR_DIAG_BIDI_CTRL(lane));
+}
+
+static void tcphy_rx_usb_cfg_lane(struct rockchip_typec_phy *tcphy,
+				  u32 lane)
+{
+	writel(0xa6fd, tcphy->base + RX_PSC_A0(lane));
+	writel(0xa6fd, tcphy->base + RX_PSC_A1(lane));
+	writel(0xa410, tcphy->base + RX_PSC_A2(lane));
+	writel(0x2410, tcphy->base + RX_PSC_A3(lane));
+	writel(0x23ff, tcphy->base + RX_PSC_CAL(lane));
+	writel(0x13, tcphy->base + RX_SIGDET_HL_FILT_TMR(lane));
+	writel(0x1004, tcphy->base + RX_DIAG_SIGDET_TUNE(lane));
+	writel(0x2010, tcphy->base + RX_PSC_RDY(lane));
+	writel(0xfb, tcphy->base + XCVR_DIAG_BIDI_CTRL(lane));
+}
+
+static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy,
+			      u32 lane)
+{
+	u16 rdata;
+
+	writel(0xbefc, tcphy->base + XCVR_PSM_RCTRL(lane));
+	writel(0x6799, tcphy->base + TX_PSC_A0(lane));
+	writel(0x6798, tcphy->base + TX_PSC_A1(lane));
+	writel(0x98, tcphy->base + TX_PSC_A2(lane));
+	writel(0x98, tcphy->base + TX_PSC_A3(lane));
+
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_001(lane));
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_010(lane));
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_011(lane));
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_100(lane));
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_101(lane));
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_110(lane));
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_111(lane));
+	writel(0, tcphy->base + TX_TXCC_CPOST_MULT_10(lane));
+	writel(0, tcphy->base + TX_TXCC_CPOST_MULT_01(lane));
+	writel(0, tcphy->base + TX_TXCC_CPOST_MULT_00(lane));
+	writel(0, tcphy->base + TX_TXCC_CPOST_MULT_11(lane));
+
+	writel(0x128, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
+	writel(0x400, tcphy->base + TX_DIAG_TX_DRV(lane));
+
+	rdata = readl(tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
+	rdata = (rdata & 0x8fff) | 0x6000;
+	writel(rdata, tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
+}
+
+static void tcphy_cfg_pin_assign(struct rockchip_typec_phy *tcphy)
+{
+	switch (tcphy->pin_assign) {
+	case PIN_ASSIGN_A:
+		writel(0x19d5, tcphy->base + PMA_LANE_CFG);
+		break;
+	case PIN_ASSIGN_B:
+		writel(0x1500, tcphy->base + PMA_LANE_CFG);
+		break;
+	case PIN_ASSIGN_C:
+	case PIN_ASSIGN_E:
+		writel(0x51d9, tcphy->base + PMA_LANE_CFG);
+		break;
+	case PIN_ASSIGN_D:
+	case PIN_ASSIGN_F:
+		writel(0x5100, tcphy->base + PMA_LANE_CFG);
+		break;
+	};
+}
+
+static inline int property_enable(struct rockchip_typec_phy *tcphy,
+				  const struct usb3phy_reg *reg, bool en)
+{
+	u32 mask = 1 << reg->write_enable;
+	u32 val = en << reg->enable_bit;
+
+	return regmap_write(tcphy->grf_regs, reg->offset, val | mask);
+}
+
+static void tcphy_lanes_config(struct rockchip_typec_phy *tcphy)
+{
+	struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+	u32 i;
+
+	property_enable(tcphy, &cfg->typec_conn_dir, tcphy->flip);
+
+	tcphy_cfg_24m(tcphy, 0x4);
+
+	switch (tcphy->mode) {
+	case MODE_UFP_USB:
+	case MODE_DFP_USB:
+		tcphy_cfg_usb_pll(tcphy);
+		if (tcphy->flip) {
+			tcphy_tx_usb_cfg_lane(tcphy, 3);
+			tcphy_rx_usb_cfg_lane(tcphy, 2);
+		} else {
+			tcphy_tx_usb_cfg_lane(tcphy, 0);
+			tcphy_rx_usb_cfg_lane(tcphy, 1);
+		}
+		break;
+	case MODE_DFP_DP:
+		tcphy_cfg_dp_pll(tcphy);
+		for (i = 0; i < 4; i++)
+			tcphy_dp_cfg_lane(tcphy, i);
+		break;
+	case MODE_DFP_USB | MODE_DFP_DP:
+		tcphy_cfg_usb_pll(tcphy);
+		tcphy_cfg_dp_pll(tcphy);
+		if (tcphy->flip) {
+			tcphy_tx_usb_cfg_lane(tcphy, 3);
+			tcphy_rx_usb_cfg_lane(tcphy, 2);
+			tcphy_dp_cfg_lane(tcphy, 0);
+			tcphy_dp_cfg_lane(tcphy, 1);
+		} else {
+			tcphy_tx_usb_cfg_lane(tcphy, 0);
+			tcphy_rx_usb_cfg_lane(tcphy, 1);
+			tcphy_dp_cfg_lane(tcphy, 2);
+			tcphy_dp_cfg_lane(tcphy, 3);
+		}
+		break;
+	}
+}
+
+static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy)
+{
+	u16 rdata, rdata2, val;
+
+	/* disable txda_cal_latch_en for rewrite the calibration values */
+	rdata = readl(tcphy->base + TX_ANA_CTRL_REG_1);
+	val = rdata & 0xdfff;
+	writel(val, tcphy->base + TX_ANA_CTRL_REG_1);
+
+	/*
+	 * read a resistor calibration code from CMN_TXPUCAL_CTRL[6:0] and
+	 * write it to TX_DIG_CTRL_REG_2[6:0], and delay 1ms to make sure it
+	 * works.
+	 */
+	rdata = readl(tcphy->base + TX_DIG_CTRL_REG_2);
+	rdata = rdata & 0xffc0;
+
+	rdata2 = readl(tcphy->base + CMN_TXPUCAL_CTRL);
+	rdata2 = rdata2 & 0x3f;
+
+	val = rdata | rdata2;
+	writel(val, tcphy->base + TX_DIG_CTRL_REG_2);
+	usleep_range(1000, 1050);
+
+	/*
+	 * Enable signal for latch that sample and holds calibration values.
+	 * Activate this signal for 1 clock cycle to sample new calibration
+	 * values.
+	 */
+	rdata = readl(tcphy->base + TX_ANA_CTRL_REG_1);
+	val = rdata | 0x2000;
+	writel(val, tcphy->base + TX_ANA_CTRL_REG_1);
+	usleep_range(150, 200);
+
+	/* set TX Voltage Level and TX Deemphasis to 0 */
+	writel(0, tcphy->base + PHY_DP_TX_CTL);
+	/* re-enable decap */
+	writel(0x100, tcphy->base + TX_ANA_CTRL_REG_2);
+	writel(0x300, tcphy->base + TX_ANA_CTRL_REG_2);
+	writel(0x2008, tcphy->base + TX_ANA_CTRL_REG_1);
+	writel(0x2018, tcphy->base + TX_ANA_CTRL_REG_1);
+
+	writel(0, tcphy->base + TX_ANA_CTRL_REG_5);
+
+	/*
+	 * Programs txda_drv_ldo_prog[15:0], Sets driver LDO
+	 * voltage 16'h1001 for DP-AUX-TX and RX
+	 */
+	writel(0x1001, tcphy->base + TX_ANA_CTRL_REG_4);
+
+	/* re-enables Bandgap reference for LDO */
+	writel(0x2098, tcphy->base + TX_ANA_CTRL_REG_1);
+	writel(0x2198, tcphy->base + TX_ANA_CTRL_REG_1);
+
+	/*
+	 * re-enables the transmitter pre-driver, driver data selection MUX,
+	 * and receiver detect circuits.
+	 */
+	writel(0x301, tcphy->base + TX_ANA_CTRL_REG_2);
+	writel(0x303, tcphy->base + TX_ANA_CTRL_REG_2);
+
+	/*
+	 * BIT 12: Controls auxda_polarity, which selects the polarity of the
+	 * xcvr:
+	 * 1, Reverses the polarity (If TYPEC, Pulls ups aux_p and pull
+	 * down aux_m)
+	 * 0, Normal polarity (if TYPE_C, pulls up aux_m and pulls down
+	 * aux_p)
+	 */
+	val = 0xa078;
+	if (!tcphy->flip)
+		val |= BIT(12);
+	writel(val, tcphy->base + TX_ANA_CTRL_REG_1);
+
+	writel(0, tcphy->base + TX_ANA_CTRL_REG_3);
+	writel(0, tcphy->base + TX_ANA_CTRL_REG_4);
+	writel(0, tcphy->base + TX_ANA_CTRL_REG_5);
+
+	/*
+	 * Controls low_power_swing_en, set the voltage swing of the driver
+	 * to 400mv. The values	below are peak to peak (differential) values.
+	 */
+	writel(4, tcphy->base + TXDA_COEFF_CALC_CTRL);
+	writel(0, tcphy->base + TXDA_CYA_AUXDA_CYA);
+
+	/* Controls tx_high_z_tm_en */
+	val = readl(tcphy->base + TX_DIG_CTRL_REG_2);
+	val |= BIT(15);
+	writel(val, tcphy->base + TX_DIG_CTRL_REG_2);
+}
+
+static void tcphy_usb3_init(struct rockchip_typec_phy *tcphy)
+{
+	struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+	const struct usb3phy_reg *reg = &cfg->pipe_status;
+	int timeout;
+	unsigned int val;
+
+	/* wait TCPHY for pipe ready */
+	for (timeout = 0; timeout < 100; timeout++) {
+		regmap_read(tcphy->grf_regs, reg->offset, &val);
+		if (!(val & BIT(reg->enable_bit)))
+			return;
+		usleep_range(10, 20);
+	}
+
+	dev_err(tcphy->dev, "wait pipe ready timeout!\n");
+}
+
+static int tcphy_dp_init(struct rockchip_typec_phy *tcphy)
+{
+	struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+	u16 val;
+	int ret;
+
+	property_enable(tcphy, &cfg->uphy_dp_sel, 1);
+
+	ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
+				 val, val & BIT(6), 1000, PHY_MODE_SET_TIMEOUT);
+	if (ret < 0) {
+		dev_err(tcphy->dev, "failed to wait TCPHY for DP ready\n");
+		return ret;
+	}
+
+	tcphy_dp_aux_calibration(tcphy);
+
+	if (tcphy->mode & MODE_DFP_USB)
+		writel(0xc101, tcphy->base + DP_MODE_CTL);
+	else
+		writel(0x0101, tcphy->base + DP_MODE_CTL);
+
+	ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
+				 val, val & BIT(4), 1000, PHY_MODE_SET_TIMEOUT);
+	if (ret < 0) {
+		dev_err(tcphy->dev, "failed to wait TCPHY enter A0\n");
+		return -EPROBE_DEFER;
+	}
+
+	return 0;
+}
+
+static int tcphy_phy_init(struct rockchip_typec_phy *tcphy)
+{
+	u32 val;
+	int ret;
+
+	ret = clk_prepare_enable(tcphy->clk_core);
+	if (ret) {
+		dev_err(tcphy->dev, "Failed to prepare_enable core clock\n");
+		return ret;
+	}
+
+	ret = clk_prepare_enable(tcphy->clk_ref);
+	if (ret) {
+		dev_err(tcphy->dev, "Failed to prepare_enable ref clock\n");
+		goto err_clk_core;
+	}
+
+	reset_control_deassert(tcphy->tcphy_rst);
+
+	tcphy_lanes_config(tcphy);
+
+	tcphy_cfg_pin_assign(tcphy);
+
+	if (tcphy->mode & MODE_DFP_DP) {
+		if (tcphy->mode & MODE_DFP_USB)
+			writel(0xc104, tcphy->base + DP_MODE_CTL);
+		else
+			writel(0x0104, tcphy->base + DP_MODE_CTL);
+	}
+
+	reset_control_deassert(tcphy->uphy_rst);
+
+	ret = readx_poll_timeout(readl, tcphy->base + PMA_CMN_CTRL1,
+				 val, val & CMN_READY, 10,
+				 PHY_MODE_SET_TIMEOUT);
+	if (ret < 0) {
+		dev_err(tcphy->dev, "wait pma ready timeout\n");
+		goto timeout_pma_ready;
+	}
+
+	reset_control_deassert(tcphy->pipe_rst);
+
+	return 0;
+
+timeout_pma_ready:
+	reset_control_assert(tcphy->tcphy_rst);
+	reset_control_assert(tcphy->uphy_rst);
+	clk_disable_unprepare(tcphy->clk_ref);
+err_clk_core:
+	clk_disable_unprepare(tcphy->clk_core);
+	return ret;
+}
+
+static void tcphy_phy_deinit(struct rockchip_typec_phy *tcphy)
+{
+	reset_control_assert(tcphy->tcphy_rst);
+	reset_control_assert(tcphy->uphy_rst);
+	reset_control_assert(tcphy->pipe_rst);
+	clk_disable_unprepare(tcphy->clk_core);
+	clk_disable_unprepare(tcphy->clk_ref);
+}
+
+static int rockchip_typec_phy_power_on(struct phy *phy)
+{
+	struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
+	struct extcon_dev *edev = tcphy->extcon;
+	union extcon_property_value property;
+	unsigned int id = EXTCON_USB;
+	bool dfp, ufp, dp;
+	u8 mode;
+	int ret;
+
+	ufp = extcon_get_state(edev, EXTCON_USB);
+	dfp = extcon_get_state(edev, EXTCON_USB_HOST);
+	dp = extcon_get_state(edev, EXTCON_DISP_DP);
+
+	mode = MODE_DFP_USB;
+	tcphy->pin_assign = PIN_ASSIGN_D;
+
+	if (ufp) {
+		mode = MODE_UFP_USB;
+		id = EXTCON_USB;
+	} else if (dfp && dp) {
+		mode = MODE_DFP_USB | MODE_DFP_DP;
+		tcphy->pin_assign = PIN_ASSIGN_D;
+	} else if (dp) {
+		mode = MODE_DFP_DP;
+		tcphy->pin_assign = PIN_ASSIGN_C;
+		id = EXTCON_DISP_DP;
+	}
+
+	if (tcphy->mode == mode)
+		return 0;
+
+	tcphy->mode = mode;
+	ret = extcon_get_property(edev, id, EXTCON_PROP_USB_TYPEC_POLARITY,
+				  &property);
+	if (ret) {
+		dev_err(tcphy->dev, "get property failed\n");
+		goto err_power_on;
+	}
+
+	tcphy->flip = property.intval ? 1 : 0;
+
+	regmap_write(tcphy->grf_regs, GRF_SOC_CON26,
+		     DPTX_HPD_SEL_MASK | DPTX_HPD_DEL);
+
+	ret = tcphy_phy_init(tcphy);
+	if (ret)
+		goto err_power_on;
+
+	if (mode & (MODE_UFP_USB | MODE_DFP_USB))
+		tcphy_usb3_init(tcphy);
+
+	if (mode & MODE_DFP_DP) {
+		ret = tcphy_dp_init(tcphy);
+		if (ret) {
+			tcphy_phy_deinit(tcphy);
+			goto err_power_on;
+		}
+
+		regmap_write(tcphy->grf_regs, GRF_SOC_CON26,
+			     DPTX_HPD_SEL_MASK | DPTX_HPD_SEL);
+	}
+
+	return 0;
+err_power_on:
+	tcphy->mode = MODE_DISCONNECT;
+	return ret;
+}
+
+static int rockchip_typec_phy_power_off(struct phy *phy)
+{
+	struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
+
+	tcphy_phy_deinit(tcphy);
+	tcphy->mode = MODE_DISCONNECT;
+
+	return 0;
+}
+
+static const struct phy_ops rockchip_tcphy_ops = {
+	.power_on	= rockchip_typec_phy_power_on,
+	.power_off	= rockchip_typec_phy_power_off,
+	.owner		= THIS_MODULE,
+};
+
+static int tcphy_get_param(struct device *dev,
+			   struct usb3phy_reg *reg,
+			   const char *name)
+{
+	u32 buffer[3];
+	int ret;
+
+	ret = of_property_read_u32_array(dev->of_node, name, buffer, 3);
+	if (ret) {
+		dev_err(dev, "Can not parse %s\n", name);
+		return ret;
+	}
+
+	reg->offset = buffer[0];
+	reg->enable_bit = buffer[1];
+	reg->write_enable = buffer[2];
+	return 0;
+}
+
+static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
+			  struct device *dev)
+{
+	struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+	int ret;
+
+	ret = tcphy_get_param(dev, &cfg->typec_conn_dir,
+			      "rockchip,typec-conn-dir");
+	if (ret) {
+		dev_err(dev, "could not find rockchip,typec-conn-dir node\n");
+		return ret;
+	}
+
+	ret = tcphy_get_param(dev, &cfg->usb3tousb2_en,
+			      "rockchip,usb3tousb2-en");
+	if (ret) {
+		dev_err(dev, "could not find rockchip,usb3tousb2-en node\n");
+		return ret;
+	}
+
+	ret = tcphy_get_param(dev, &cfg->external_psm,
+			      "rockchip,external-psm");
+	if (ret) {
+		dev_err(dev, "could not find rockchip,external-psm node\n");
+		return ret;
+	}
+
+	ret = tcphy_get_param(dev, &cfg->pipe_status,
+			      "rockchip,pipe-status");
+	if (ret) {
+		dev_err(dev, "could not find rockchip,pipe-status node\n");
+		return ret;
+	}
+
+	ret = tcphy_get_param(dev, &cfg->uphy_dp_sel,
+			      "rockchip,uphy-dp-sel");
+	if (ret) {
+		dev_err(dev, "could not find rockchip,uphy-dp-sel node\n");
+		return ret;
+	}
+
+	tcphy->grf_regs = syscon_regmap_lookup_by_phandle(dev->of_node,
+							  "rockchip,grf");
+	if (IS_ERR(tcphy->grf_regs)) {
+		dev_err(dev, "could not find grf dt node\n");
+		return PTR_ERR(tcphy->grf_regs);
+	}
+
+	tcphy->clk_core = devm_clk_get(dev, "tcpdcore");
+	if (IS_ERR(tcphy->clk_core)) {
+		dev_err(dev, "could not get uphy core clock\n");
+		return PTR_ERR(tcphy->clk_core);
+	}
+
+	tcphy->clk_ref = devm_clk_get(dev, "tcpdphy-ref");
+	if (IS_ERR(tcphy->clk_ref)) {
+		dev_err(dev, "could not get uphy ref clock\n");
+		return PTR_ERR(tcphy->clk_ref);
+	}
+
+	tcphy->uphy_rst = devm_reset_control_get(dev, "uphy");
+	if (IS_ERR(tcphy->uphy_rst)) {
+		dev_err(dev, "no uphy_rst reset control found\n");
+		return PTR_ERR(tcphy->uphy_rst);
+	}
+
+	tcphy->pipe_rst = devm_reset_control_get(dev, "uphy-pipe");
+	if (IS_ERR(tcphy->pipe_rst)) {
+		dev_err(dev, "no pipe_rst reset control found\n");
+		return PTR_ERR(tcphy->pipe_rst);
+	}
+
+	tcphy->tcphy_rst = devm_reset_control_get(dev, "uphy-tcphy");
+	if (IS_ERR(tcphy->tcphy_rst)) {
+		dev_err(dev, "no tcphy_rst reset control found\n");
+		return PTR_ERR(tcphy->tcphy_rst);
+	}
+
+	return 0;
+}
+
+static void typec_phy_pre_init(struct rockchip_typec_phy *tcphy)
+{
+	struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+
+	reset_control_assert(tcphy->tcphy_rst);
+	reset_control_assert(tcphy->uphy_rst);
+	reset_control_assert(tcphy->pipe_rst);
+
+	/* select external psm clock */
+	property_enable(tcphy, &cfg->external_psm, 1);
+	property_enable(tcphy, &cfg->usb3tousb2_en, 0);
+
+	tcphy->mode = MODE_DISCONNECT;
+}
+
+static int rockchip_typec_phy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct rockchip_typec_phy *tcphy;
+	struct phy_provider *phy_provider;
+	struct resource *res;
+	int ret;
+
+	tcphy = devm_kzalloc(dev, sizeof(*tcphy), GFP_KERNEL);
+	if (!tcphy)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	tcphy->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(tcphy->base)) {
+		dev_err(dev, "failed to remap phy regs\n");
+		return PTR_ERR(tcphy->base);
+	}
+
+	ret = tcphy_parse_dt(tcphy, dev);
+	if (ret)
+		return ret;
+
+	tcphy->dev = dev;
+	platform_set_drvdata(pdev, tcphy);
+
+	typec_phy_pre_init(tcphy);
+
+	tcphy->extcon = extcon_get_edev_by_phandle(dev, 0);
+	if (IS_ERR(tcphy->extcon)) {
+		if (PTR_ERR(tcphy->extcon) != -EPROBE_DEFER)
+			dev_err(dev, "Invalid or missing extcon\n");
+		return PTR_ERR(tcphy->extcon);
+	}
+
+	tcphy->phy = devm_phy_create(dev, NULL, &rockchip_tcphy_ops);
+	if (IS_ERR(tcphy->phy)) {
+		dev_err(dev, "failed to create Tepy-C phy\n");
+		return PTR_ERR(tcphy->phy);
+	}
+
+	phy_set_drvdata(tcphy->phy, tcphy);
+
+	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+	if (IS_ERR(phy_provider)) {
+		dev_err(dev, "Failed to register phy provider\n");
+		return PTR_ERR(phy_provider);
+	}
+
+	return 0;
+}
+
+static const struct of_device_id rockchip_typec_phy_dt_ids[] = {
+	{ .compatible = "rockchip,rk3399-typec-phy" },
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, rockchip_typec_phy_dt_ids);
+
+static struct platform_driver rockchip_typec_phy_driver = {
+	.probe		= rockchip_typec_phy_probe,
+	.driver		= {
+		.name	= "rockchip-typec-phy",
+		.of_match_table = rockchip_typec_phy_dt_ids,
+	},
+};
+
+module_platform_driver(rockchip_typec_phy_driver);
+
+MODULE_AUTHOR("Chris Zhong <zyw at rock-chips.com>");
+MODULE_AUTHOR("Kever Yang <kever.yang at rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip USB TYPE-C PHY driver");
+MODULE_LICENSE("GPL v2");
-- 
2.6.3



From zyw at rock-chips.com  Wed Jul 27 06:07:15 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Wed, 27 Jul 2016 21:07:15 +0800
Subject: [v7 PATCH 5/6] Documentation: bindings: add dt documentation for cdn
 DP controller
In-Reply-To: <1469624836-22792-1-git-send-email-zyw@rock-chips.com>
References: <1469624836-22792-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1469624836-22792-6-git-send-email-zyw@rock-chips.com>

This patch adds a binding that describes the cdn DP controller for
rk3399.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
Acked-by: Rob Herring <robh at kernel.org>

---

Changes in v7: None
Changes in v6:
- add assigned-clocks and assigned-clock-rates
- add power-domains

Changes in v5: None
Changes in v4:
- add a reset node
- support 2 phys

Changes in v3:
- add SoC specific compatible string
- remove reg = <1>;

Changes in v2: None
Changes in v1:
- add extcon node description
- add #sound-dai-cells description

 .../bindings/display/rockchip/cdn-dp-rockchip.txt  | 74 ++++++++++++++++++++++
 1 file changed, 74 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/rockchip/cdn-dp-rockchip.txt

diff --git a/Documentation/devicetree/bindings/display/rockchip/cdn-dp-rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/cdn-dp-rockchip.txt
new file mode 100644
index 0000000..af87dcc
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/rockchip/cdn-dp-rockchip.txt
@@ -0,0 +1,74 @@
+Rockchip RK3399 specific extensions to the cdn Display Port
+================================
+
+Required properties:
+- compatible: must be "rockchip,rk3399-cdn-dp"
+
+- reg: physical base address of the controller and length
+
+- clocks: from common clock binding: handle to dp clock.
+
+- clock-names: from common clock binding:
+	       Required elements: "core-clk" "pclk" "spdif"
+
+- resets : a list of phandle + reset specifier pairs
+- reset-names : string reset name, must be:
+		"spdif"
+- power-domains : power-domain property defined with a phandle
+		  to respective power domain.
+- assigned-clocks: main clock, should be <&cru SCLK_DP_CORE>
+- assigned-clock-rates : the DP core clk frequency, shall be: 100000000
+
+- rockchip,grf: this soc should set GRF regs, so need get grf here.
+
+- ports: contain a port nodes with endpoint definitions as defined in
+	 Documentation/devicetree/bindings/media/video-interfaces.txt.
+	 contained 2 endpoints, connecting to the output of vop.
+
+- phys: from general PHY binding: the phandle for the PHY device.
+
+- extcon: extcon specifier for the Power Delivery
+
+- #sound-dai-cells = it must be 1 if your system is using 2 DAIs: I2S, SPDIF
+
+-------------------------------------------------------------------------------
+
+Example:
+	cdn_dp: dp at fec00000 {
+		compatible = "rockchip,rk3399-cdn-dp";
+		reg = <0x0 0xfec00000 0x0 0x100000>;
+		interrupts = <GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru SCLK_DP_CORE>, <&cru PCLK_DP_CTRL>,
+			 <&cru SCLK_SPDIF_REC_DPTX>;
+		clock-names = "core-clk", "pclk", "spdif";
+		assigned-clocks = <&cru SCLK_DP_CORE>;
+		assigned-clock-rates = <100000000>;
+		power-domains = <&power RK3399_PD_HDCP>;
+		phys = <&tcphy0>, <&tcphy1>;
+		resets = <&cru SRST_DPTX_SPDIF_REC>;
+		reset-names = "spdif";
+		extcon = <&fusb0>, <&fusb1>;
+		rockchip,grf = <&grf>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		#sound-dai-cells = <1>;
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			dp_in: port {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				dp_in_vopb: endpoint at 0 {
+					reg = <0>;
+					remote-endpoint = <&vopb_out_dp>;
+				};
+
+				dp_in_vopl: endpoint at 1 {
+					reg = <1>;
+					remote-endpoint = <&vopl_out_dp>;
+				};
+			};
+		};
+	};
-- 
2.6.3



From zyw at rock-chips.com  Wed Jul 27 06:07:16 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Wed, 27 Jul 2016 21:07:16 +0800
Subject: [v7 PATCH 6/6] drm/rockchip: cdn-dp: add cdn DP support for rk3399
In-Reply-To: <1469624836-22792-1-git-send-email-zyw@rock-chips.com>
References: <1469624836-22792-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1469624836-22792-7-git-send-email-zyw@rock-chips.com>

Add support for cdn DP controller which is embedded in the rk3399
SoCs. The DP is compliant with DisplayPort Specification,
Version 1.3, This IP is compatible with the rockchip type-c PHY IP.
There is a uCPU in DP controller, it need a firmware to work,
please put the firmware file to /lib/firmware/rockchip/dptx.bin. The
uCPU in charge of aux communication and link training, the host use
mailbox to communicate with the ucpu.
The dclk pin_pol of vop must not be invert for DP.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
Reviewed-by: Sean Paul <seanpaul at chromium.org>
Acked-by: Mark Yao <mark.yao at rock-chips.com>

---

Changes in v7:
- support firmware standby when no dptx connection
- optimization the calculation of tu size and valid symbol

Changes in v6:
- add a port struct
- select SND_SOC_HDMI_CODEC
- force reset the phy when hpd detected

Changes in v5:
- alphabetical order
- do not use long, use u32 or u64
- return MODE_CLOCK_HIGH when requested > actual
- Optimized Coding Style
- add a formula to get better tu size and symbol value.
- modify according to Sean Paul's comments
- fixed the fw_wait always 0

Changes in v4:
- use phy framework to control DP phy
- support 2 phys

Changes in v3:
- use EXTCON_DISP_DP and EXTCON_DISP_DP_ALT cable to get dp port state.
- reset spdif before config it
- modify the firmware clk to 100Mhz
- retry load firmware if fw file is requested too early

Changes in v2:
- Alphabetic order
- remove excess error message
- use define clk_rate
- check all return value
- remove dev_set_name(dp->dev, "cdn-dp");
- use schedule_delayed_work
- remove never-called functions
- remove some unnecessary ()

Changes in v1:
- use extcon API
- use hdmi-codec for the DP Asoc
- do not initialize the "ret"
- printk a err log when drm_of_encoder_active_endpoint_id
- modify the dclk pin_pol to a single line

 drivers/gpu/drm/rockchip/Kconfig            |  10 +
 drivers/gpu/drm/rockchip/Makefile           |   1 +
 drivers/gpu/drm/rockchip/cdn-dp-core.c      | 877 ++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/cdn-dp-core.h      | 103 ++++
 drivers/gpu/drm/rockchip/cdn-dp-reg.c       | 916 ++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/cdn-dp-reg.h       | 482 +++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c |  13 +-
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   9 +
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c |   2 +
 9 files changed, 2410 insertions(+), 3 deletions(-)
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.c
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.h
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.c
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.h

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index d30bdc3..20aaafe 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -25,6 +25,16 @@ config ROCKCHIP_ANALOGIX_DP
 	  for the Analogix Core DP driver. If you want to enable DP
 	  on RK3288 based SoC, you should selet this option.
 
+config ROCKCHIP_CDN_DP
+        tristate "Rockchip cdn DP"
+        depends on DRM_ROCKCHIP
+	select SND_SOC_HDMI_CODEC if SND_SOC
+        help
+	  This selects support for Rockchip SoC specific extensions
+	  for the cdn DP driver. If you want to enable Dp on
+	  RK3399 based SoC, you should select this
+	  option.
+
 config ROCKCHIP_DW_HDMI
         tristate "Rockchip specific extensions for Synopsys DW HDMI"
         depends on DRM_ROCKCHIP
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index 05d0713..abdecd5 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -7,6 +7,7 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
 rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
 
 obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
+obj-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
 obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
 obj-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
 obj-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
new file mode 100644
index 0000000..31d146c
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
@@ -0,0 +1,877 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Chris Zhong <zyw at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_of.h>
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/extcon.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/mfd/syscon.h>
+#include <linux/phy/phy.h>
+
+#include <sound/hdmi-codec.h>
+
+#include "cdn-dp-core.h"
+#include "cdn-dp-reg.h"
+#include "rockchip_drm_vop.h"
+
+#define connector_to_dp(c) \
+		container_of(c, struct cdn_dp_device, connector)
+
+#define encoder_to_dp(c) \
+		container_of(c, struct cdn_dp_device, encoder)
+
+/* dp grf register offset */
+#define DP_VOP_SEL		0x6224
+#define DP_SEL_VOP_LIT		BIT(12)
+#define MAX_FW_WAIT_SECS	64
+#define CDN_DP_FIRMWARE		"rockchip/dptx.bin"
+
+struct cdn_dp_data {
+	u8 max_phy;
+};
+
+struct cdn_dp_data rk3399_cdn_dp = {
+	.max_phy = 2,
+};
+
+static const struct of_device_id cdn_dp_dt_ids[] = {
+	{ .compatible = "rockchip,rk3399-cdn-dp",
+		.data = (void *)&rk3399_cdn_dp },
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, cdn_dp_dt_ids);
+
+static int cdn_dp_clk_enable(struct cdn_dp_device *dp)
+{
+	int ret;
+	u32 rate;
+
+	ret = clk_prepare_enable(dp->pclk);
+	if (ret < 0) {
+		dev_err(dp->dev, "cannot enable dp pclk %d\n", ret);
+		goto err_pclk;
+	}
+
+	ret = clk_prepare_enable(dp->core_clk);
+	if (ret < 0) {
+		dev_err(dp->dev, "cannot enable core_clk %d\n", ret);
+		goto err_core_clk;
+	}
+
+	rate = clk_get_rate(dp->core_clk);
+	if (rate < 0) {
+		dev_err(dp->dev, "get clk rate failed: %d\n", rate);
+		goto err_set_rate;
+	}
+
+	cdn_dp_set_fw_clk(dp, rate);
+
+	return 0;
+
+err_set_rate:
+	clk_disable_unprepare(dp->core_clk);
+err_core_clk:
+	clk_disable_unprepare(dp->pclk);
+err_pclk:
+	return ret;
+}
+
+static enum drm_connector_status
+cdn_dp_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct cdn_dp_device *dp = connector_to_dp(connector);
+
+	return dp->hpd_status;
+}
+
+static void cdn_dp_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+static struct drm_connector_funcs cdn_dp_atomic_connector_funcs = {
+	.dpms = drm_atomic_helper_connector_dpms,
+	.detect = cdn_dp_connector_detect,
+	.destroy = cdn_dp_connector_destroy,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int cdn_dp_connector_get_modes(struct drm_connector *connector)
+{
+	struct cdn_dp_device *dp = connector_to_dp(connector);
+	struct edid *edid;
+	int ret = 0;
+
+	edid = drm_do_get_edid(connector, cdn_dp_get_edid_block, dp);
+	if (edid) {
+		dev_dbg(dp->dev, "got edid: width[%d] x height[%d]\n",
+			edid->width_cm, edid->height_cm);
+
+		dp->sink_has_audio = drm_detect_monitor_audio(edid);
+		ret = drm_add_edid_modes(connector, edid);
+		if (ret) {
+			drm_mode_connector_update_edid_property(connector,
+								edid);
+			drm_edid_to_eld(connector, edid);
+		}
+		kfree(edid);
+	} else {
+		dev_warn(dp->dev, "failed to get edid\n");
+	}
+
+	return ret;
+}
+
+static struct drm_encoder *
+	cdn_dp_connector_best_encoder(struct drm_connector *connector)
+{
+	struct cdn_dp_device *dp = connector_to_dp(connector);
+
+	return &dp->encoder;
+}
+
+static int cdn_dp_connector_mode_valid(struct drm_connector *connector,
+				       struct drm_display_mode *mode)
+{
+	struct cdn_dp_device *dp = connector_to_dp(connector);
+	struct drm_display_info *display_info = &dp->connector.display_info;
+	u32 requested = mode->clock * display_info->bpc * 3 / 1000;
+	u32 sink_max, source_max, rate;
+	u8 lanes, i;
+	u32 actual;
+
+	/* find the running port */
+	for (i = 0; i < dp->ports; i++) {
+		if (dp->port[i]->phy_status == 1) {
+			source_max = dp->port[i]->cap_lanes;
+			break;
+		}
+	}
+
+	sink_max = drm_dp_max_lane_count(dp->dpcd);
+	lanes = min(source_max, sink_max);
+
+	source_max = drm_dp_bw_code_to_link_rate(CDN_DP_MAX_LINK_RATE);
+	sink_max = drm_dp_max_link_rate(dp->dpcd);
+	rate = min(source_max, sink_max);
+
+	actual = rate * lanes / 100;
+
+	/* efficiency is about 0.8 */
+	actual = actual * 8 / 10;
+
+	if (requested > actual) {
+		dev_dbg(dp->dev, "requested=%d, actual=%d, clock=%d\n",
+			requested, actual, mode->clock);
+		return MODE_CLOCK_HIGH;
+	}
+
+	return MODE_OK;
+}
+
+static struct drm_connector_helper_funcs cdn_dp_connector_helper_funcs = {
+	.get_modes = cdn_dp_connector_get_modes,
+	.best_encoder = cdn_dp_connector_best_encoder,
+	.mode_valid = cdn_dp_connector_mode_valid,
+};
+
+static void cdn_dp_commit(struct drm_encoder *encoder)
+{
+	struct cdn_dp_device *dp = encoder_to_dp(encoder);
+	int ret;
+
+	ret = cdn_dp_training_start(dp);
+	if (ret) {
+		dev_err(dp->dev, "link training failed: %d\n", ret);
+		return;
+	}
+
+	ret = cdn_dp_get_training_status(dp);
+	if (ret) {
+		dev_err(dp->dev, "get link training status failed: %d\n", ret);
+		return;
+	}
+
+	dev_info(dp->dev, "rate:0x%x, lanes:%d\n",
+		 dp->link.rate, dp->link.num_lanes);
+
+	if (cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE))
+		return;
+
+	if (cdn_dp_config_video(dp)) {
+		dev_err(dp->dev, "unable to config video\n");
+		return;
+	}
+
+	if (cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID))
+		return;
+
+	dp->dpms_mode = DRM_MODE_DPMS_ON;
+}
+
+static void cdn_dp_encoder_mode_set(struct drm_encoder *encoder,
+				    struct drm_display_mode *mode,
+				    struct drm_display_mode *adjusted)
+{
+	struct cdn_dp_device *dp = encoder_to_dp(encoder);
+	struct drm_display_info *display_info = &dp->connector.display_info;
+	struct rockchip_crtc_state *state;
+	struct video_info *video = &dp->video_info;
+	int ret, val;
+
+	switch (display_info->bpc) {
+	case 16:
+	case 12:
+	case 10:
+		video->color_depth = 10;
+		break;
+	case 6:
+		video->color_depth = 6;
+		break;
+	default:
+		video->color_depth = 8;
+		break;
+	}
+
+	video->color_fmt = PXL_RGB;
+
+	video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC);
+	video->h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC);
+
+	ret = drm_of_encoder_active_endpoint_id(dp->dev->of_node, encoder);
+	if (ret < 0) {
+		dev_err(dp->dev, "Could not get vop id, %d", ret);
+		return;
+	}
+
+	dev_dbg(dp->dev, "vop %s output to cdn-dp\n", (ret) ? "LIT" : "BIG");
+	state = to_rockchip_crtc_state(encoder->crtc->state);
+	if (ret) {
+		val = DP_SEL_VOP_LIT | (DP_SEL_VOP_LIT << 16);
+		state->output_mode = ROCKCHIP_OUT_MODE_P888;
+	} else {
+		val = DP_SEL_VOP_LIT << 16;
+		state->output_mode = ROCKCHIP_OUT_MODE_AAAA;
+	}
+	ret = regmap_write(dp->grf, DP_VOP_SEL, val);
+	if (ret != 0)
+		dev_err(dp->dev, "Could not write to GRF: %d\n", ret);
+
+	memcpy(&dp->mode, adjusted, sizeof(*mode));
+}
+
+static void cdn_dp_encoder_enable(struct drm_encoder *encoder)
+{
+	struct cdn_dp_device *dp = encoder_to_dp(encoder);
+
+	if (dp->dpms_mode != DRM_MODE_DPMS_ON)
+		cdn_dp_commit(encoder);
+}
+
+static void cdn_dp_encoder_disable(struct drm_encoder *encoder)
+{
+	struct cdn_dp_device *dp = encoder_to_dp(encoder);
+
+	if (dp->dpms_mode != DRM_MODE_DPMS_OFF) {
+		cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE);
+		dp->dpms_mode = DRM_MODE_DPMS_OFF;
+	}
+}
+
+static int
+cdn_dp_encoder_atomic_check(struct drm_encoder *encoder,
+			    struct drm_crtc_state *crtc_state,
+			    struct drm_connector_state *conn_state)
+{
+	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+
+	s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
+	s->output_type = DRM_MODE_CONNECTOR_DisplayPort;
+
+	return 0;
+}
+
+static struct drm_encoder_helper_funcs cdn_dp_encoder_helper_funcs = {
+	.mode_set = cdn_dp_encoder_mode_set,
+	.enable = cdn_dp_encoder_enable,
+	.disable = cdn_dp_encoder_disable,
+	.atomic_check = cdn_dp_encoder_atomic_check,
+};
+
+static struct drm_encoder_funcs cdn_dp_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+static int cdn_dp_firmware_init(struct cdn_dp_device *dp)
+{
+	int ret;
+	const u32 *iram_data, *dram_data;
+	const struct firmware *fw = dp->fw;
+	const struct cdn_firmware_header *hdr;
+
+	hdr = (struct cdn_firmware_header *)fw->data;
+	if (fw->size != le32_to_cpu(hdr->size_bytes)) {
+		dev_err(dp->dev, "firmware is invalid\n");
+		return -EINVAL;
+	}
+
+	iram_data = (const u32 *)(fw->data + hdr->header_size);
+	dram_data = (const u32 *)(fw->data + hdr->header_size + hdr->iram_size);
+
+	ret = cdn_dp_load_firmware(dp, iram_data, hdr->iram_size,
+				   dram_data, hdr->dram_size);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_active(dp, true);
+	if (ret) {
+		dev_err(dp->dev, "active ucpu failed: %d\n", ret);
+		return ret;
+	}
+
+	dp->fw_loaded = 1;
+
+	return cdn_dp_event_config(dp);
+}
+
+static int cdn_dp_init(struct cdn_dp_device *dp)
+{
+	struct device *dev = dp->dev;
+	struct device_node *np = dev->of_node;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct resource *res;
+	int ret;
+
+	dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+	if (IS_ERR(dp->grf)) {
+		dev_err(dev, "cdn-dp needs rockchip,grf property\n");
+		return PTR_ERR(dp->grf);
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	dp->regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(dp->regs)) {
+		dev_err(dev, "ioremap reg failed\n");
+		return PTR_ERR(dp->regs);
+	}
+
+	dp->core_clk = devm_clk_get(dev, "core-clk");
+	if (IS_ERR(dp->core_clk)) {
+		dev_err(dev, "cannot get core_clk_dp\n");
+		return PTR_ERR(dp->core_clk);
+	}
+
+	dp->pclk = devm_clk_get(dev, "pclk");
+	if (IS_ERR(dp->pclk)) {
+		dev_err(dev, "cannot get pclk\n");
+		return PTR_ERR(dp->pclk);
+	}
+
+	dp->spdif_clk = devm_clk_get(dev, "spdif");
+	if (IS_ERR(dp->spdif_clk)) {
+		dev_err(dev, "cannot get spdif_clk\n");
+		return PTR_ERR(dp->spdif_clk);
+	}
+
+	dp->spdif_rst = devm_reset_control_get(dev, "spdif");
+	if (IS_ERR(dp->spdif_rst)) {
+		dev_err(dev, "no spdif reset control found\n");
+		return PTR_ERR(dp->spdif_rst);
+	}
+
+	dp->dpms_mode = DRM_MODE_DPMS_OFF;
+
+	ret = cdn_dp_clk_enable(dp);
+	if (ret < 0)
+		return ret;
+
+	cdn_dp_clock_reset(dp);
+
+	return 0;
+}
+
+static int cdn_dp_audio_hw_params(struct device *dev,  void *data,
+				  struct hdmi_codec_daifmt *daifmt,
+				  struct hdmi_codec_params *params)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	struct audio_info audio = {
+		.sample_width = params->sample_width,
+		.sample_rate = params->sample_rate,
+		.channels = params->channels,
+	};
+	int ret;
+
+	if (!dp->encoder.crtc)
+		return -ENODEV;
+
+	switch (daifmt->fmt) {
+	case HDMI_I2S:
+		audio.format = AFMT_I2S;
+		break;
+	case HDMI_SPDIF:
+		audio.format = AFMT_SPDIF;
+		break;
+	default:
+		dev_err(dev, "%s: Invalid format %d\n", __func__, daifmt->fmt);
+		return -EINVAL;
+	}
+
+	ret = cdn_dp_audio_config_set(dp, &audio);
+	if (!ret)
+		dp->audio_info = audio;
+
+	return ret;
+}
+
+static void cdn_dp_audio_shutdown(struct device *dev, void *data)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	int ret = cdn_dp_audio_stop(dp, &dp->audio_info);
+
+	if (!ret)
+		dp->audio_info.format = AFMT_UNUSED;
+}
+
+static int cdn_dp_audio_digital_mute(struct device *dev, void *data,
+				     bool enable)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+
+	return cdn_dp_audio_mute(dp, enable);
+}
+
+static int cdn_dp_audio_get_eld(struct device *dev, void *data,
+				uint8_t *buf, size_t len)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	struct drm_mode_config *config = &dp->encoder.dev->mode_config;
+	struct drm_connector *connector;
+	int ret = -ENODEV;
+
+	mutex_lock(&config->mutex);
+	list_for_each_entry(connector, &config->connector_list, head) {
+		if (&dp->encoder == connector->encoder) {
+			memcpy(buf, connector->eld,
+			       min(sizeof(connector->eld), len));
+			ret = 0;
+		}
+	}
+	mutex_unlock(&config->mutex);
+
+	return ret;
+}
+
+static const struct hdmi_codec_ops audio_codec_ops = {
+	.hw_params = cdn_dp_audio_hw_params,
+	.audio_shutdown = cdn_dp_audio_shutdown,
+	.digital_mute = cdn_dp_audio_digital_mute,
+	.get_eld = cdn_dp_audio_get_eld,
+};
+
+static int cdn_dp_audio_codec_init(struct cdn_dp_device *dp,
+				   struct device *dev)
+{
+	struct hdmi_codec_pdata codec_data = {
+		.i2s = 1,
+		.spdif = 1,
+		.ops = &audio_codec_ops,
+		.max_i2s_channels = 8,
+	};
+
+	dp->audio_pdev = platform_device_register_data(
+			 dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO,
+			 &codec_data, sizeof(codec_data));
+
+	return PTR_ERR_OR_ZERO(dp->audio_pdev);
+}
+
+static int cdn_dp_get_cap_lanes(struct cdn_dp_device *dp,
+				struct extcon_dev *edev)
+{
+	bool dfp, dptx;
+	u8 lanes;
+
+	dfp = extcon_get_cable_state_(edev, EXTCON_USB_HOST);
+	dptx = extcon_get_cable_state_(edev, EXTCON_DISP_DP);
+
+	if (dfp && dptx)
+		lanes = 2;
+	else if (dptx)
+		lanes = 4;
+	else
+		lanes = 0;
+
+	return lanes;
+}
+
+static int cdn_dp_phy_power_ctrl(struct cdn_dp_device *dp,
+				 struct cdn_dp_port *port, bool enable)
+{
+	struct phy *phy = port->phy;
+	int ret;
+	int count = 0;
+
+	while (phy->power_count > 0) {
+		ret = phy_power_off(phy);
+		if (ret)
+			return ret;
+		count++;
+	}
+
+	count = enable ? count + 1 : count - 1;
+	while (count-- > 0) {
+		ret = phy_power_on(phy);
+		if (ret)
+			return ret;
+	}
+
+	port->phy_status = enable;
+	return 0;
+}
+
+static int cdn_dp_pd_event(struct notifier_block *nb,
+			   unsigned long event, void *priv)
+{
+	struct cdn_dp_port *port;
+
+	port = container_of(nb, struct cdn_dp_port, event_nb);
+
+	schedule_delayed_work(&port->event_wq, 0);
+
+	return 0;
+}
+
+static void cdn_dp_pd_event_wq(struct work_struct *work)
+{
+	struct cdn_dp_port *port = container_of(work, struct cdn_dp_port,
+						event_wq.work);
+	struct cdn_dp_device *dp = port->dp;
+	u8 new_cap_lanes;
+	int ret;
+	u8 i;
+
+	new_cap_lanes = cdn_dp_get_cap_lanes(dp, port->extcon);
+
+	if (new_cap_lanes == port->cap_lanes) {
+		dev_warn(dp->dev, "lanes count does not change: %d\n",
+			 new_cap_lanes);
+		return;
+	}
+
+	if (!new_cap_lanes) {
+		ret = cdn_dp_phy_power_ctrl(dp, port, 0);
+		if (ret) {
+			dev_err(dp->dev, "phy power off failed: %d", ret);
+			return;
+		}
+		port->phy_status = 0;
+		port->cap_lanes = new_cap_lanes;
+		for (i = 0; i < dp->ports; i++) {
+			if (dp->port[i]->phy_status == 1)
+				return;
+		}
+
+		dp->hpd_status = connector_status_disconnected;
+		drm_helper_hpd_irq_event(dp->drm_dev);
+		cdn_dp_active(dp, false);
+		return;
+	}
+
+	/* if other phy is running, do not touch the hpd_status, and return */
+	for (i = 0; i < dp->ports; i++) {
+		if (dp->port[i]->phy_status == 1) {
+			dev_warn(dp->dev, "busy, phy[%d] is running",
+				 dp->port[i]->id);
+			return;
+		}
+	}
+
+	if (!dp->fw_loaded) {
+		ret = request_firmware(&dp->fw, CDN_DP_FIRMWARE, dp->dev);
+		if (ret == -ENOENT && dp->fw_wait <= MAX_FW_WAIT_SECS) {
+			unsigned long time = msecs_to_jiffies(dp->fw_wait * HZ);
+
+			/*
+			 * If can not find the file, retry to load the firmware
+			 * in 1 second, if still failed after 1 minute, give up.
+			 */
+			schedule_delayed_work(&port->event_wq, time);
+			dev_warn(dp->dev, "retry to request firmware in %dS\n",
+				 dp->fw_wait);
+			dp->fw_wait *= 2;
+			return;
+		} else if (ret) {
+			dev_err(dp->dev, "failed to request firmware: %d\n",
+				ret);
+			return;
+		}
+	}
+
+	if (dp->fw_loaded)
+		cdn_dp_active(dp, true);
+
+	ret = cdn_dp_phy_power_ctrl(dp, port, 1);
+	if (ret) {
+		if (!dp->fw_loaded)
+			release_firmware(dp->fw);
+		dev_err(dp->dev, "phy power on failed: %d\n", ret);
+		return;
+	}
+
+	if (!dp->fw_loaded) {
+		ret = cdn_dp_firmware_init(dp);
+		release_firmware(dp->fw);
+		if (ret) {
+			dev_err(dp->dev, "firmware init failed: %d", ret);
+			goto err_firmware;
+		}
+	}
+
+	/* read hpd status failed, or the hpd does not exist. */
+	ret = cdn_dp_get_hpd_status(dp);
+	if (ret <= 0) {
+		dev_err(dp->dev, "get hpd failed: %d", ret);
+		goto err_firmware;
+	}
+
+	ret = cdn_dp_set_host_cap(dp, new_cap_lanes);
+	if (ret) {
+		dev_err(dp->dev, "set host capabilities failed: %d\n", ret);
+		goto err_firmware;
+	}
+
+	/*
+	 * Native read with retry for link status and receiver capability reads
+	 * for cases where the sink may still be asleep.
+	 *
+	 * Sinks are *supposed* to come up within 1ms from an off state, but
+	 * we're also supposed to retry 3 times per the spec.
+	 */
+	for (i = 0; i < 3; i++) {
+		ret = cdn_dp_dpcd_read(dp, 0x000, dp->dpcd,
+				       DP_RECEIVER_CAP_SIZE);
+		if (!ret) {
+			dev_dbg(dp->dev, "get dpcd success!\n");
+			port->cap_lanes = new_cap_lanes;
+			dp->hpd_status = connector_status_connected;
+			drm_helper_hpd_irq_event(dp->drm_dev);
+			return;
+		}
+		usleep_range(1000, 1050);
+	}
+
+	dev_err(dp->dev, "get dpcd failed: %d", ret);
+
+err_firmware:
+	ret = cdn_dp_phy_power_ctrl(dp, port, 0);
+	if (ret)
+		dev_err(dp->dev, "phy power off failed: %d", ret);
+	if (dp->fw_loaded)
+		cdn_dp_active(dp, false);
+}
+
+static int cdn_dp_bind(struct device *dev, struct device *master,
+		       void *data)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	struct drm_encoder *encoder;
+	struct drm_connector *connector;
+	struct cdn_dp_port *port;
+	struct drm_device *drm_dev = data;
+	int ret, i;
+
+	ret = cdn_dp_init(dp);
+	if (ret < 0)
+		return ret;
+
+	dp->drm_dev = drm_dev;
+	dp->hpd_status = connector_status_disconnected;
+	dp->fw_wait = 1;
+
+	encoder = &dp->encoder;
+
+	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
+							     dev->of_node);
+	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
+
+	ret = drm_encoder_init(drm_dev, encoder, &cdn_dp_encoder_funcs,
+			       DRM_MODE_ENCODER_TMDS, NULL);
+	if (ret) {
+		DRM_ERROR("failed to initialize encoder with drm\n");
+		return ret;
+	}
+
+	drm_encoder_helper_add(encoder, &cdn_dp_encoder_helper_funcs);
+
+	connector = &dp->connector;
+	connector->polled = DRM_CONNECTOR_POLL_HPD;
+	connector->dpms = DRM_MODE_DPMS_OFF;
+
+	ret = drm_connector_init(drm_dev, connector,
+				 &cdn_dp_atomic_connector_funcs,
+				 DRM_MODE_CONNECTOR_DisplayPort);
+	if (ret) {
+		DRM_ERROR("failed to initialize connector with drm\n");
+		goto err_free_encoder;
+	}
+
+	drm_connector_helper_add(connector, &cdn_dp_connector_helper_funcs);
+
+	ret = drm_mode_connector_attach_encoder(connector, encoder);
+	if (ret) {
+		DRM_ERROR("failed to attach connector and encoder\n");
+		goto err_free_connector;
+	}
+
+	cdn_dp_audio_codec_init(dp, dev);
+
+
+	for (i = 0; i < dp->ports; i++) {
+		port = dp->port[i];
+
+		port->event_nb.notifier_call = cdn_dp_pd_event;
+		INIT_DELAYED_WORK(&port->event_wq, cdn_dp_pd_event_wq);
+		ret = extcon_register_notifier(port->extcon, EXTCON_DISP_DP,
+					       &port->event_nb);
+		if (ret) {
+			dev_err(dev, "regitster EXTCON_DISP_DP notifier err\n");
+			return ret;
+		}
+
+		if (extcon_get_cable_state_(port->extcon, EXTCON_DISP_DP))
+			schedule_delayed_work(&port->event_wq, 0);
+	}
+
+	return 0;
+
+err_free_connector:
+	drm_connector_cleanup(connector);
+err_free_encoder:
+	drm_encoder_cleanup(encoder);
+	return ret;
+}
+
+static void cdn_dp_unbind(struct device *dev, struct device *master, void *data)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	struct drm_encoder *encoder = &dp->encoder;
+	struct drm_connector *connector = &dp->connector;
+	struct cdn_dp_port *port;
+	int i;
+
+	platform_device_unregister(dp->audio_pdev);
+	cdn_dp_encoder_disable(encoder);
+	encoder->funcs->destroy(encoder);
+	connector->funcs->destroy(connector);
+
+	for (i = 0; i < dp->ports; i++) {
+		port = dp->port[i];
+		extcon_unregister_notifier(port->extcon, EXTCON_DISP_DP,
+					   &port->event_nb);
+	}
+}
+
+static const struct component_ops cdn_dp_component_ops = {
+	.bind = cdn_dp_bind,
+	.unbind = cdn_dp_unbind,
+};
+
+static int cdn_dp_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	const struct of_device_id *match;
+	struct cdn_dp_data *dp_data;
+	struct cdn_dp_port *port;
+	struct cdn_dp_device *dp;
+	struct extcon_dev *extcon;
+	struct phy *phy;
+	int i;
+
+	dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
+	if (!dp)
+		return -ENOMEM;
+	dp->dev = dev;
+
+	match = of_match_node(cdn_dp_dt_ids, pdev->dev.of_node);
+	dp_data = (struct cdn_dp_data *)match->data;
+
+	for (i = 0; i < dp_data->max_phy; i++) {
+		extcon = extcon_get_edev_by_phandle(dev, i);
+		phy = devm_of_phy_get_by_index(dev, dev->of_node, i);
+
+		if (PTR_ERR(extcon) == -EPROBE_DEFER ||
+		    PTR_ERR(phy) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+
+		if (IS_ERR(extcon) || IS_ERR(phy))
+			continue;
+
+		port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+		if (!dp)
+			return -ENOMEM;
+
+		port->extcon = extcon;
+		port->phy = phy;
+		port->dp = dp;
+		port->id = i;
+		dp->port[dp->ports++] = port;
+	}
+
+	if (!dp->ports) {
+		dev_err(dev, "missing extcon or phy\n");
+		return -EINVAL;
+	}
+
+	dev_set_drvdata(dev, dp);
+
+	return component_add(dev, &cdn_dp_component_ops);
+}
+
+static int cdn_dp_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &cdn_dp_component_ops);
+
+	return 0;
+}
+
+static struct platform_driver cdn_dp_driver = {
+	.probe = cdn_dp_probe,
+	.remove = cdn_dp_remove,
+	.driver = {
+		   .name = "cdn-dp",
+		   .owner = THIS_MODULE,
+		   .of_match_table = of_match_ptr(cdn_dp_dt_ids),
+	},
+};
+
+module_platform_driver(cdn_dp_driver);
+
+MODULE_AUTHOR("Chris Zhong <zyw at rock-chips.com>");
+MODULE_DESCRIPTION("cdn DP Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
new file mode 100644
index 0000000..78424f9
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2016 Chris Zhong <zyw at rock-chips.com>
+ * Copyright (C) 2016 ROCKCHIP, Inc.
+ *
+ * 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.
+ */
+
+#ifndef _CDN_DP_CORE_H
+#define _CDN_DP_CORE_H
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_panel.h>
+#include "rockchip_drm_drv.h"
+
+#define MAX_PHY		2
+
+enum audio_format {
+	AFMT_I2S = 0,
+	AFMT_SPDIF = 1,
+	AFMT_UNUSED,
+};
+
+struct audio_info {
+	enum audio_format format;
+	int sample_rate;
+	int channels;
+	int sample_width;
+};
+
+enum vic_pxl_encoding_format {
+	PXL_RGB = 0x1,
+	YCBCR_4_4_4 = 0x2,
+	YCBCR_4_2_2 = 0x4,
+	YCBCR_4_2_0 = 0x8,
+	Y_ONLY = 0x10,
+};
+
+struct video_info {
+	bool h_sync_polarity;
+	bool v_sync_polarity;
+	bool interlaced;
+	int color_depth;
+	enum vic_pxl_encoding_format color_fmt;
+};
+
+struct cdn_firmware_header {
+	u32 size_bytes; /* size of the entire header+image(s) in bytes */
+	u32 header_size; /* size of just the header in bytes */
+	u32 iram_size; /* size of iram */
+	u32 dram_size; /* size of dram */
+};
+
+struct cdn_dp_port {
+	struct cdn_dp_device *dp;
+	struct notifier_block event_nb;
+	struct delayed_work event_wq;
+	struct extcon_dev *extcon;
+	struct phy *phy;
+	u8 cap_lanes;
+	bool phy_status;
+	u8 id;
+};
+
+struct cdn_dp_device {
+	struct device *dev;
+	struct drm_device *drm_dev;
+	struct drm_connector connector;
+	struct drm_encoder encoder;
+	struct drm_display_mode mode;
+	struct platform_device *audio_pdev;
+
+	const struct firmware *fw;	/* cdn dp firmware */
+	unsigned int fw_version;	/* cdn fw version */
+	u32 fw_wait;
+	bool fw_loaded;
+	bool fw_actived;
+	void __iomem *regs;
+	struct regmap *grf;
+	struct clk *core_clk;
+	struct clk *pclk;
+	struct clk *spdif_clk;
+	struct reset_control *spdif_rst;
+	struct audio_info audio_info;
+	struct video_info video_info;
+	struct drm_dp_link link;
+	struct cdn_dp_port *port[MAX_PHY];
+	u8 ports;
+
+	u8 dpcd[DP_RECEIVER_CAP_SIZE];
+	enum drm_connector_status hpd_status;
+	int dpms_mode;
+	bool sink_has_audio;
+};
+#endif  /* _CDN_DP_CORE_H */
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
new file mode 100644
index 0000000..4255dac
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
@@ -0,0 +1,916 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Chris Zhong <zyw at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/clk.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/reset.h>
+
+#include "cdn-dp-core.h"
+#include "cdn-dp-reg.h"
+
+#define CDN_DP_SPDIF_CLK		200000000
+#define FW_ALIVE_TIMEOUT_US		1000000
+#define MAILBOX_RETRY_US		1000
+#define MAILBOX_TIMEOUT_US		5000000
+#define LINK_TRAINING_RETRY_MS		20
+#define LINK_TRAINING_TIMEOUT_MS	500
+
+void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, u32 clk)
+{
+	writel(clk / 1000000, dp->regs + SW_CLK_H);
+}
+
+void cdn_dp_clock_reset(struct cdn_dp_device *dp)
+{
+	u32 val;
+
+	val = DPTX_FRMR_DATA_CLK_RSTN_EN |
+	      DPTX_FRMR_DATA_CLK_EN |
+	      DPTX_PHY_DATA_RSTN_EN |
+	      DPTX_PHY_DATA_CLK_EN |
+	      DPTX_PHY_CHAR_RSTN_EN |
+	      DPTX_PHY_CHAR_CLK_EN |
+	      SOURCE_AUX_SYS_CLK_RSTN_EN |
+	      SOURCE_AUX_SYS_CLK_EN |
+	      DPTX_SYS_CLK_RSTN_EN |
+	      DPTX_SYS_CLK_EN |
+	      CFG_DPTX_VIF_CLK_RSTN_EN |
+	      CFG_DPTX_VIF_CLK_EN;
+	writel(val, dp->regs + SOURCE_DPTX_CAR);
+
+	val = SOURCE_PHY_RSTN_EN | SOURCE_PHY_CLK_EN;
+	writel(val, dp->regs + SOURCE_PHY_CAR);
+
+	val = SOURCE_PKT_SYS_RSTN_EN |
+	      SOURCE_PKT_SYS_CLK_EN |
+	      SOURCE_PKT_DATA_RSTN_EN |
+	      SOURCE_PKT_DATA_CLK_EN;
+	writel(val, dp->regs + SOURCE_PKT_CAR);
+
+	val = SPDIF_CDR_CLK_RSTN_EN |
+	      SPDIF_CDR_CLK_EN |
+	      SOURCE_AIF_SYS_RSTN_EN |
+	      SOURCE_AIF_SYS_CLK_EN |
+	      SOURCE_AIF_CLK_RSTN_EN |
+	      SOURCE_AIF_CLK_EN;
+	writel(val, dp->regs + SOURCE_AIF_CAR);
+
+	val = SOURCE_CIPHER_SYSTEM_CLK_RSTN_EN |
+	      SOURCE_CIPHER_SYS_CLK_EN |
+	      SOURCE_CIPHER_CHAR_CLK_RSTN_EN |
+	      SOURCE_CIPHER_CHAR_CLK_EN;
+	writel(val, dp->regs + SOURCE_CIPHER_CAR);
+
+	val = SOURCE_CRYPTO_SYS_CLK_RSTN_EN |
+	      SOURCE_CRYPTO_SYS_CLK_EN;
+	writel(val, dp->regs + SOURCE_CRYPTO_CAR);
+
+	val = ~(MAILBOX_INT_MASK_BIT | PIF_INT_MASK_BIT) & ALL_INT_MASK;
+	writel(val, dp->regs + APB_INT_MASK);
+}
+
+static int cdn_dp_mailbox_read(struct cdn_dp_device *dp, bool force)
+{
+	int val, ret;
+
+	if (!dp->fw_actived && !force)
+		return -EPERM;
+
+	ret = readx_poll_timeout(readl, dp->regs + MAILBOX_EMPTY_ADDR,
+				 val, !val, MAILBOX_RETRY_US,
+				 MAILBOX_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(dp->dev, "failed to read mailbox, keep alive = %x\n",
+			readl(dp->regs + KEEP_ALIVE));
+		return ret;
+	}
+
+	return readl(dp->regs + MAILBOX0_RD_DATA) & 0xff;
+}
+
+static int cdp_dp_mailbox_write(struct cdn_dp_device *dp, u8 val, bool force)
+{
+	int ret, full;
+
+	if (!dp->fw_actived && !force)
+		return -EPERM;
+
+	ret = readx_poll_timeout(readl, dp->regs + MAILBOX_FULL_ADDR,
+				 full, !full, MAILBOX_RETRY_US,
+				 MAILBOX_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(dp->dev, "mailbox is full, keep alive = %x\n",
+			readl(dp->regs + KEEP_ALIVE));
+		return ret;
+	}
+
+	writel(val, dp->regs + MAILBOX0_WR_DATA);
+
+	return 0;
+}
+
+static int cdn_dp_mailbox_validate_receive(struct cdn_dp_device *dp,
+					   u8 module_id, u8 opcode,
+					   u8 req_size)
+{
+	u32 mbox_size, i;
+	u8 header[4];
+	int ret;
+
+	/* read the header of the message */
+	for (i = 0; i < 4; i++)  {
+		ret = cdn_dp_mailbox_read(dp, 0);
+		if (ret < 0)
+			return ret;
+
+		header[i] = ret;
+	}
+
+	mbox_size = (header[2] << 8) | header[3];
+
+	if (opcode != header[0] || module_id != header[1] ||
+	    req_size != mbox_size) {
+		dev_err(dp->dev, "mailbox validate failed");
+
+		/*
+		 * If the message in mailbox is not what we want, we need to
+		 * clear the mailbox by read.
+		 */
+		for (i = 0; i < mbox_size; i++)
+			if (cdn_dp_mailbox_read(dp, 0) < 0)
+				break;
+
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int cdn_dp_mailbox_read_receive(struct cdn_dp_device *dp,
+				       u8 *buff, u8 buff_size)
+{
+	u32 i;
+	int ret;
+
+	for (i = 0; i < buff_size; i++) {
+		ret = cdn_dp_mailbox_read(dp, 0);
+		if (ret < 0)
+			return ret;
+
+		buff[i] = ret;
+	}
+
+	return 0;
+}
+
+static int cdn_dp_mailbox_send(struct cdn_dp_device *dp, u8 module_id,
+			       u8 opcode, u16 size, u8 *message)
+{
+	u8 header[4];
+	int ret, i;
+
+	header[0] = opcode;
+	header[1] = module_id;
+	header[2] = (size >> 8) & 0xff;
+	header[3] = size & 0xff;
+
+	for (i = 0; i < 4; i++) {
+		ret = cdp_dp_mailbox_write(dp, header[i], 0);
+		if (ret)
+			return ret;
+	}
+
+	for (i = 0; i < size; i++) {
+		ret = cdp_dp_mailbox_write(dp, message[i], 0);
+		if (ret) {
+			dev_err(dp->dev, "mailbox send failed: %d", ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
+{
+	u8 msg[6];
+
+	msg[0] = (addr >> 8) & 0xff;
+	msg[1] = addr & 0xff;
+	msg[2] = (val >> 24) & 0xff;
+	msg[3] = (val >> 16) & 0xff;
+	msg[4] = (val >> 8) & 0xff;
+	msg[5] = val & 0xff;
+	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_REGISTER,
+				   sizeof(msg), msg);
+}
+
+int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
+{
+	u8 msg[5], reg[5];
+	int ret;
+
+	msg[0] = (len >> 8) & 0xff;
+	msg[1] = len & 0xff;
+	msg[2] = (addr >> 16) & 0xff;
+	msg[3] = (addr >> 8) & 0xff;
+	msg[4] = addr & 0xff;
+	cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_READ_DPCD,
+			    sizeof(msg), msg);
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_READ_DPCD,
+					      sizeof(reg) + len);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg));
+	if (ret)
+		return ret;
+
+	return cdn_dp_mailbox_read_receive(dp, data, len);
+}
+
+int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
+{
+	u8 msg[6], reg[5];
+	int ret;
+
+	msg[0] = 0;
+	msg[1] = 1;
+	msg[2] = (addr >> 16) & 0xff;
+	msg[3] = (addr >> 8) & 0xff;
+	msg[4] = addr & 0xff;
+	msg[5] = value;
+	cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_DPCD,
+			    sizeof(msg), msg);
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_WRITE_DPCD, sizeof(reg));
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg));
+	if (ret)
+		return ret;
+
+	if (addr != (reg[2] << 16 | reg[3] << 8 | reg[4])) {
+		dev_err(dp->dev, "write dpcd failed\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
+			 u32 i_size, const u32 *d_mem, u32 d_size)
+{
+	int i, ret;
+	u32 reg;
+
+	/* reset ucpu before load firmware*/
+	writel(APB_IRAM_PATH | APB_DRAM_PATH | APB_XT_RESET,
+	       dp->regs + APB_CTRL);
+
+	for (i = 0; i < i_size; i += 4)
+		writel(*i_mem++, dp->regs + ADDR_IMEM + i);
+
+	for (i = 0; i < d_size; i += 4)
+		writel(*d_mem++, dp->regs + ADDR_DMEM + i);
+
+	/* un-reset ucpu */
+	writel(0, dp->regs + APB_CTRL);
+
+	/* check the keep alive register to make sure fw working */
+	ret = readx_poll_timeout(readl, dp->regs + KEEP_ALIVE,
+				 reg, reg, 2000, FW_ALIVE_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(dp->dev, "failed to loaded the FW reg = %x\n", reg);
+		return -EINVAL;
+	}
+
+	reg = readl(dp->regs + VER_L) & 0xff;
+	dp->fw_version = reg;
+	reg = readl(dp->regs + VER_H) & 0xff;
+	dp->fw_version |= reg << 8;
+	reg = readl(dp->regs + VER_LIB_L_ADDR) & 0xff;
+	dp->fw_version |= reg << 16;
+	reg = readl(dp->regs + VER_LIB_H_ADDR) & 0xff;
+	dp->fw_version |= reg << 24;
+
+	dev_dbg(dp->dev, "firmware version: %x\n", dp->fw_version);
+
+	return 0;
+}
+
+int cdn_dp_active(struct cdn_dp_device *dp, bool enable)
+{
+	u8 msg[5];
+	int ret, i;
+
+	msg[0] = GENERAL_MAIN_CONTROL;
+	msg[1] = MB_MODULE_ID_GENERAL;
+	msg[2] = 0;
+	msg[3] = 1;
+	msg[4] = enable ? FW_ACTIVE : FW_STANDBY;
+
+	for (i = 0; i < sizeof(msg); i++) {
+		ret = cdp_dp_mailbox_write(dp, msg[i], 1);
+		if (ret)
+			return ret;
+	}
+
+	/* read the firmware state */
+	for (i = 0; i < sizeof(msg); i++)  {
+		ret = cdn_dp_mailbox_read(dp, 1);
+		if (ret < 0)
+			return ret;
+
+		msg[i] = ret;
+	}
+
+	dp->fw_actived = (msg[4] == FW_ACTIVE);
+	return 0;
+}
+
+int cdn_dp_reg_write_bit(struct cdn_dp_device *dp, u16 addr, u8 start_bit,
+			 u8 bits_no, u32 val)
+{
+	u8 field[8];
+
+	field[0] = (addr >> 8) & 0xff;
+	field[1] = addr & 0xff;
+	field[2] = start_bit;
+	field[3] = bits_no;
+	field[4] = (val >> 24) & 0xff;
+	field[5] = (val >> 16) & 0xff;
+	field[6] = (val >> 8) & 0xff;
+	field[7] = val & 0xff;
+
+	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_FIELD,
+				   sizeof(field), field);
+}
+
+int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes)
+{
+	u8 msg[8];
+	int ret;
+
+	msg[0] = CDN_DP_MAX_LINK_RATE;
+	msg[1] = lanes;
+	msg[2] = VOLTAGE_LEVEL_2;
+	msg[3] = PRE_EMPHASIS_LEVEL_3;
+	msg[4] = PTS1 | PTS2 | PTS3 | PTS4;
+	msg[5] = FAST_LT_NOT_SUPPORT;
+	msg[6] = LANE_MAPPING_NORMAL;
+	msg[7] = ENHANCED;
+
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
+				  DPTX_SET_HOST_CAPABILITIES,
+				  sizeof(msg), msg);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_reg_write(dp, DP_AUX_SWAP_INVERSION_CONTROL,
+			       AUX_HOST_INVERT);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+int cdn_dp_event_config(struct cdn_dp_device *dp)
+{
+	u8 msg[5];
+
+	memset(msg, 0, sizeof(msg));
+
+	msg[0] = DPTX_EVENT_ENABLE_HPD | DPTX_EVENT_ENABLE_TRAINING;
+
+	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_ENABLE_EVENT,
+				   sizeof(msg), msg);
+}
+
+u32 cdn_dp_get_event(struct cdn_dp_device *dp)
+{
+	return readl(dp->regs + SW_EVENTS0);
+}
+
+int cdn_dp_get_hpd_status(struct cdn_dp_device *dp)
+{
+	u8 status;
+	int ret;
+
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_HPD_STATE,
+				  0, NULL);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_HPD_STATE, sizeof(status));
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, &status, sizeof(status));
+	if (ret)
+		return ret;
+
+	return status;
+}
+
+int cdn_dp_get_edid_block(void *data, u8 *edid,
+			  unsigned int block, size_t length)
+{
+	struct cdn_dp_device *dp = data;
+	u8 msg[2], reg[2];
+	int ret;
+
+	msg[0] = block / 2;
+	msg[1] = block % 2;
+
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_GET_EDID,
+				  sizeof(msg), msg);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_GET_EDID,
+					      sizeof(reg) + length);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg));
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, edid, length);
+	if (ret)
+		return ret;
+
+	if (reg[0] != length || reg[1] != block / 2) {
+		dev_err(dp->dev, "edid block size err\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int cdn_dp_training_start(struct cdn_dp_device *dp)
+{
+	u8 msg, event[2];
+	unsigned long timeout;
+	int ret;
+
+	msg = LINK_TRAINING_RUN;
+
+	/* start training */
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_TRAINING_CONTROL,
+				  sizeof(msg), &msg);
+	if (ret)
+		return ret;
+
+	timeout = jiffies + msecs_to_jiffies(LINK_TRAINING_TIMEOUT_MS);
+	while (time_before(jiffies, timeout)) {
+		msleep(LINK_TRAINING_RETRY_MS);
+		ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
+					  DPTX_READ_EVENT, 0, NULL);
+		if (ret)
+			return ret;
+
+		ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+						      DPTX_READ_EVENT,
+						      sizeof(event));
+		if (ret)
+			return ret;
+
+		ret = cdn_dp_mailbox_read_receive(dp, event, sizeof(event));
+		if (ret)
+			return ret;
+
+		if (event[1] & EQ_PHASE_FINISHED)
+			return 0;
+	}
+
+	return -ETIMEDOUT;
+}
+
+int cdn_dp_get_training_status(struct cdn_dp_device *dp)
+{
+	u8 status[10];
+	int ret;
+
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_READ_LINK_STAT,
+				  0, NULL);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_READ_LINK_STAT,
+					      sizeof(status));
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_mailbox_read_receive(dp, status, sizeof(status));
+	if (ret)
+		return ret;
+
+	dp->link.rate = status[0];
+	dp->link.num_lanes = status[1];
+
+	return 0;
+}
+
+int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active)
+{
+	u8 msg;
+
+	msg = !!active;
+
+	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_SET_VIDEO,
+				   sizeof(msg), &msg);
+}
+
+static int cdn_dp_get_msa_misc(struct video_info *video,
+			       struct drm_display_mode *mode)
+{
+	u8 val[2];
+	u32 msa_misc;
+
+	switch (video->color_fmt) {
+	case PXL_RGB:
+	case Y_ONLY:
+		val[0] = 0;
+		break;
+	/* set YUV default color space conversion to BT601 */
+	case YCBCR_4_4_4:
+		val[0] = 6 + BT_601 * 8;
+		break;
+	case YCBCR_4_2_2:
+		val[0] = 5 + BT_601 * 8;
+		break;
+	case YCBCR_4_2_0:
+		val[0] = 5;
+		break;
+	};
+
+	switch (video->color_depth) {
+	case 6:
+		val[1] = 0;
+		break;
+	case 8:
+		val[1] = 1;
+		break;
+	case 10:
+		val[1] = 2;
+		break;
+	case 12:
+		val[1] = 3;
+		break;
+	case 16:
+		val[1] = 4;
+		break;
+	};
+
+	msa_misc = 2 * val[0] + 32 * val[1] +
+		   ((video->color_fmt == Y_ONLY) ? (1 << 14) : 0);
+
+	return msa_misc;
+}
+
+int cdn_dp_config_video(struct cdn_dp_device *dp)
+{
+	struct video_info *video = &dp->video_info;
+	struct drm_display_mode *mode = &dp->mode;
+	u64 symbol, tmp;
+	u32 val, link_rate;
+	u8 bit_per_pix;
+	u8 tu_size_reg = TU_SIZE;
+	int ret;
+
+	bit_per_pix = (video->color_fmt == YCBCR_4_2_2) ?
+		      (video->color_depth * 2) : (video->color_depth * 3);
+
+	link_rate = drm_dp_bw_code_to_link_rate(dp->link.rate) / 1000;
+
+	val = VIF_BYPASS_INTERLACE;
+	ret = cdn_dp_reg_write(dp, BND_HSYNC2VSYNC, val);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_reg_write(dp, HSYNC2VSYNC_POL_CTRL, 0);
+	if (ret)
+		return ret;
+
+	/*
+	 * get a best tu_size and valid symbol:
+	 * 1. chose Lclk freq(162Mhz, 270Mhz, 540Mhz), set TU to 32
+	 * 2. calculate VS(valid symbol) = TU * Pclk * Bpp / (Lclk * Lanes)
+	 * 3. if VS > *.85 or VS < *.1 or VS < 2 or TU < VS + 4, then set
+	 *    TU += 2 and repeat 2nd step.
+	 */
+	do {
+		tu_size_reg += 2;
+		tmp = tu_size_reg * mode->clock * bit_per_pix;
+		tmp /= dp->link.num_lanes * link_rate * 8;
+		symbol = tmp / 1000;
+	} while ((symbol <= 1) || (tu_size_reg - symbol < 4) ||
+		 (tmp % 1000 > 850) || (tmp % 1000 < 100));
+
+	val = symbol + (tu_size_reg << 8);
+	ret = cdn_dp_reg_write(dp, DP_FRAMER_TU, val);
+	if (ret)
+		return ret;
+
+	/* set the FIFO Buffer size */
+	val = ((mode->clock * (symbol + 1) / 1000) + link_rate);
+	val /= (dp->link.num_lanes * link_rate);
+	val = 8 * (symbol + 1) / bit_per_pix - val;
+	val += 2;
+	ret = cdn_dp_reg_write(dp, DP_VC_TABLE(15), val);
+
+	switch (video->color_depth) {
+	case 6:
+		val = BCS_6;
+		break;
+	case 8:
+		val = BCS_8;
+		break;
+	case 10:
+		val = BCS_10;
+		break;
+	case 12:
+		val = BCS_12;
+		break;
+	case 16:
+		val = BCS_16;
+		break;
+	};
+
+	val += video->color_fmt << 8;
+	ret = cdn_dp_reg_write(dp, DP_FRAMER_PXL_REPR, val);
+	if (ret)
+		return ret;
+
+	val = video->h_sync_polarity ? DP_FRAMER_SP_HSP : 0;
+	val |= video->v_sync_polarity ? DP_FRAMER_SP_VSP : 0;
+	ret = cdn_dp_reg_write(dp, DP_FRAMER_SP, val);
+	if (ret)
+		return ret;
+
+	val = (mode->hsync_start - mode->hdisplay) << 16;
+	val |= mode->htotal - mode->hsync_end;
+	ret = cdn_dp_reg_write(dp, DP_FRONT_BACK_PORCH, val);
+	if (ret)
+		return ret;
+
+	val = mode->hdisplay * bit_per_pix / 8;
+	ret = cdn_dp_reg_write(dp, DP_BYTE_COUNT, val);
+	if (ret)
+		return ret;
+
+	val = mode->htotal | ((mode->htotal - mode->hsync_start) << 16);
+	ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_0, val);
+	if (ret)
+		return ret;
+
+	val = mode->hsync_end - mode->hsync_start;
+	val |= (mode->hdisplay << 16) | (video->h_sync_polarity << 15);
+	ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_1, val);
+	if (ret)
+		return ret;
+
+	val = mode->vtotal;
+	val |= ((mode->vtotal - mode->vsync_start) << 16);
+	ret = cdn_dp_reg_write(dp, MSA_VERTICAL_0, val);
+	if (ret)
+		return ret;
+
+	val = mode->vsync_end - mode->vsync_start;
+	val |= mode->vdisplay << 16 | (video->v_sync_polarity << 15);
+	ret = cdn_dp_reg_write(dp, MSA_VERTICAL_1, val);
+	if (ret)
+		return ret;
+
+	val = cdn_dp_get_msa_misc(video, mode);
+	ret = cdn_dp_reg_write(dp, MSA_MISC, val);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_reg_write(dp, STREAM_CONFIG, 1);
+	if (ret)
+		return ret;
+
+	val = mode->hsync_end - mode->hsync_start;
+	val |= (mode->hdisplay << 16);
+	ret = cdn_dp_reg_write(dp, DP_HORIZONTAL, val);
+	if (ret)
+		return ret;
+
+	val = mode->vtotal;
+	val -= (mode->vtotal - mode->vdisplay);
+	val |= (mode->vtotal - mode->vsync_start) << 16;
+	ret = cdn_dp_reg_write(dp, DP_VERTICAL_0, val);
+	if (ret)
+		return ret;
+
+	val = mode->vtotal;
+	ret = cdn_dp_reg_write(dp, DP_VERTICAL_1, val);
+	if (ret)
+		return ret;
+
+	val =  0;
+	return cdn_dp_reg_write_bit(dp, DP_VB_ID, 2, 1, val);
+}
+
+int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio)
+{
+	u32 val;
+	int ret;
+
+	ret = cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, 0);
+	if (ret)
+		return ret;
+
+	val = SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS;
+	val |= SPDIF_FIFO_MID_RANGE(0xe0);
+	val |= SPDIF_JITTER_THRSH(0xe0);
+	val |= SPDIF_JITTER_AVG_WIN(7);
+	writel(val, dp->regs + SPDIF_CTRL_ADDR);
+
+	/* clearn the audio config and reset */
+	writel(0, dp->regs + AUDIO_SRC_CNTL);
+	writel(0, dp->regs + AUDIO_SRC_CNFG);
+	writel(AUDIO_SW_RST, dp->regs + AUDIO_SRC_CNTL);
+	writel(0, dp->regs + AUDIO_SRC_CNTL);
+
+	/* reset smpl2pckt component  */
+	writel(0, dp->regs + SMPL2PKT_CNTL);
+	writel(AUDIO_SW_RST, dp->regs + SMPL2PKT_CNTL);
+	writel(0, dp->regs + SMPL2PKT_CNTL);
+
+	/* reset FIFO */
+	writel(AUDIO_SW_RST, dp->regs + FIFO_CNTL);
+	writel(0, dp->regs + FIFO_CNTL);
+
+	if (audio->format == AFMT_SPDIF)
+		clk_disable_unprepare(dp->spdif_clk);
+
+	return 0;
+}
+
+int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable)
+{
+	return cdn_dp_reg_write_bit(dp, DP_VB_ID, 4, 1, enable);
+}
+
+static void cdn_dp_audio_config_i2s(struct cdn_dp_device *dp,
+				    struct audio_info *audio)
+{
+	int sub_pckt_num = 1, i2s_port_en_val = 0xf, i;
+	u32 val;
+
+	if (audio->channels == 2) {
+		if (dp->link.num_lanes == 1)
+			sub_pckt_num = 2;
+		else
+			sub_pckt_num = 4;
+
+		i2s_port_en_val = 1;
+	} else if (audio->channels == 4) {
+		i2s_port_en_val = 3;
+	}
+
+	writel(0x0, dp->regs + SPDIF_CTRL_ADDR);
+
+	writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL);
+
+	val = MAX_NUM_CH(audio->channels);
+	val |= NUM_OF_I2S_PORTS(audio->channels);
+	val |= AUDIO_TYPE_LPCM;
+	val |= CFG_SUB_PCKT_NUM(sub_pckt_num);
+	writel(val, dp->regs + SMPL2PKT_CNFG);
+
+	if (audio->sample_width == 16)
+		val = 0;
+	else if (audio->sample_width == 24)
+		val = 1 << 9;
+	else
+		val = 2 << 9;
+
+	val |= AUDIO_CH_NUM(audio->channels);
+	val |= I2S_DEC_PORT_EN(i2s_port_en_val);
+	val |= TRANS_SMPL_WIDTH_32;
+	writel(val, dp->regs + AUDIO_SRC_CNFG);
+
+	for (i = 0; i < (audio->channels + 1) / 2; i++) {
+		if (audio->sample_width == 16)
+			val = (0x08 << 8) | (0x08 << 20);
+		else if (audio->sample_width == 24)
+			val = (0x0b << 8) | (0x0b << 20);
+
+		val |= ((2 * i) << 4) | ((2 * i + 1) << 16);
+		writel(val, dp->regs + STTS_BIT_CH(i));
+	}
+
+	switch (audio->sample_rate) {
+	case 32000:
+		val = SAMPLING_FREQ(3) |
+		      ORIGINAL_SAMP_FREQ(0xc);
+		break;
+	case 44100:
+		val = SAMPLING_FREQ(0) |
+		      ORIGINAL_SAMP_FREQ(0xf);
+		break;
+	case 48000:
+		val = SAMPLING_FREQ(2) |
+		      ORIGINAL_SAMP_FREQ(0xd);
+		break;
+	case 88200:
+		val = SAMPLING_FREQ(8) |
+		      ORIGINAL_SAMP_FREQ(0x7);
+		break;
+	case 96000:
+		val = SAMPLING_FREQ(0xa) |
+		      ORIGINAL_SAMP_FREQ(5);
+		break;
+	case 176400:
+		val = SAMPLING_FREQ(0xc) |
+		      ORIGINAL_SAMP_FREQ(3);
+		break;
+	case 192000:
+		val = SAMPLING_FREQ(0xe) |
+		      ORIGINAL_SAMP_FREQ(1);
+		break;
+	}
+	val |= 4;
+	writel(val, dp->regs + COM_CH_STTS_BITS);
+
+	writel(SMPL2PKT_EN, dp->regs + SMPL2PKT_CNTL);
+	writel(I2S_DEC_START, dp->regs + AUDIO_SRC_CNTL);
+}
+
+static void cdn_dp_audio_config_spdif(struct cdn_dp_device *dp)
+{
+	u32 val;
+
+	val = SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS;
+	val |= SPDIF_FIFO_MID_RANGE(0xe0);
+	val |= SPDIF_JITTER_THRSH(0xe0);
+	val |= SPDIF_JITTER_AVG_WIN(7);
+	writel(val, dp->regs + SPDIF_CTRL_ADDR);
+
+	writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL);
+
+	val = MAX_NUM_CH(2) | AUDIO_TYPE_LPCM | CFG_SUB_PCKT_NUM(4);
+	writel(val, dp->regs + SMPL2PKT_CNFG);
+	writel(SMPL2PKT_EN, dp->regs + SMPL2PKT_CNTL);
+
+	val = SPDIF_ENABLE | SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS;
+	val |= SPDIF_FIFO_MID_RANGE(0xe0);
+	val |= SPDIF_JITTER_THRSH(0xe0);
+	val |= SPDIF_JITTER_AVG_WIN(7);
+	writel(val, dp->regs + SPDIF_CTRL_ADDR);
+
+	clk_prepare_enable(dp->spdif_clk);
+	clk_set_rate(dp->spdif_clk, CDN_DP_SPDIF_CLK);
+}
+
+int cdn_dp_audio_config_set(struct cdn_dp_device *dp, struct audio_info *audio)
+{
+	int ret;
+
+	/* reset the spdif clk before config */
+	if (audio->format == AFMT_SPDIF) {
+		reset_control_assert(dp->spdif_rst);
+		reset_control_deassert(dp->spdif_rst);
+	}
+
+	ret = cdn_dp_reg_write(dp, CM_LANE_CTRL, LANE_REF_CYC);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_reg_write(dp, CM_CTRL, 0);
+	if (ret)
+		return ret;
+
+	if (audio->format == AFMT_I2S)
+		cdn_dp_audio_config_i2s(dp, audio);
+	else
+		cdn_dp_audio_config_spdif(dp);
+
+	return cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, AUDIO_PACK_EN);
+}
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
new file mode 100644
index 0000000..e83aebd
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
@@ -0,0 +1,482 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Chris Zhong <zyw at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _CDN_DP_REG_H
+#define _CDN_DP_REG_H
+
+#include <linux/bitops.h>
+
+#define ADDR_IMEM		0x10000
+#define ADDR_DMEM		0x20000
+
+/* APB CFG addr */
+#define APB_CTRL			0
+#define XT_INT_CTRL			0x04
+#define MAILBOX_FULL_ADDR		0x08
+#define MAILBOX_EMPTY_ADDR		0x0c
+#define MAILBOX0_WR_DATA		0x10
+#define MAILBOX0_RD_DATA		0x14
+#define KEEP_ALIVE			0x18
+#define VER_L				0x1c
+#define VER_H				0x20
+#define VER_LIB_L_ADDR			0x24
+#define VER_LIB_H_ADDR			0x28
+#define SW_DEBUG_L			0x2c
+#define SW_DEBUG_H			0x30
+#define MAILBOX_INT_MASK		0x34
+#define MAILBOX_INT_STATUS		0x38
+#define SW_CLK_L			0x3c
+#define SW_CLK_H			0x40
+#define SW_EVENTS0			0x44
+#define SW_EVENTS1			0x48
+#define SW_EVENTS2			0x4c
+#define SW_EVENTS3			0x50
+#define XT_OCD_CTRL			0x60
+#define APB_INT_MASK			0x6c
+#define APB_STATUS_MASK			0x70
+
+/* audio decoder addr */
+#define AUDIO_SRC_CNTL			0x30000
+#define AUDIO_SRC_CNFG			0x30004
+#define COM_CH_STTS_BITS		0x30008
+#define STTS_BIT_CH(x)			(0x3000c + ((x) << 2))
+#define SPDIF_CTRL_ADDR			0x3004c
+#define SPDIF_CH1_CS_3100_ADDR		0x30050
+#define SPDIF_CH1_CS_6332_ADDR		0x30054
+#define SPDIF_CH1_CS_9564_ADDR		0x30058
+#define SPDIF_CH1_CS_12796_ADDR		0x3005c
+#define SPDIF_CH1_CS_159128_ADDR	0x30060
+#define SPDIF_CH1_CS_191160_ADDR	0x30064
+#define SPDIF_CH2_CS_3100_ADDR		0x30068
+#define SPDIF_CH2_CS_6332_ADDR		0x3006c
+#define SPDIF_CH2_CS_9564_ADDR		0x30070
+#define SPDIF_CH2_CS_12796_ADDR		0x30074
+#define SPDIF_CH2_CS_159128_ADDR	0x30078
+#define SPDIF_CH2_CS_191160_ADDR	0x3007c
+#define SMPL2PKT_CNTL			0x30080
+#define SMPL2PKT_CNFG			0x30084
+#define FIFO_CNTL			0x30088
+#define FIFO_STTS			0x3008c
+
+/* source pif addr */
+#define SOURCE_PIF_WR_ADDR		0x30800
+#define SOURCE_PIF_WR_REQ		0x30804
+#define SOURCE_PIF_RD_ADDR		0x30808
+#define SOURCE_PIF_RD_REQ		0x3080c
+#define SOURCE_PIF_DATA_WR		0x30810
+#define SOURCE_PIF_DATA_RD		0x30814
+#define SOURCE_PIF_FIFO1_FLUSH		0x30818
+#define SOURCE_PIF_FIFO2_FLUSH		0x3081c
+#define SOURCE_PIF_STATUS		0x30820
+#define SOURCE_PIF_INTERRUPT_SOURCE	0x30824
+#define SOURCE_PIF_INTERRUPT_MASK	0x30828
+#define SOURCE_PIF_PKT_ALLOC_REG	0x3082c
+#define SOURCE_PIF_PKT_ALLOC_WR_EN	0x30830
+#define SOURCE_PIF_SW_RESET		0x30834
+
+/* bellow registers need access by mailbox */
+/* source car addr */
+#define SOURCE_HDTX_CAR			0x0900
+#define SOURCE_DPTX_CAR			0x0904
+#define SOURCE_PHY_CAR			0x0908
+#define SOURCE_CEC_CAR			0x090c
+#define SOURCE_CBUS_CAR			0x0910
+#define SOURCE_PKT_CAR			0x0918
+#define SOURCE_AIF_CAR			0x091c
+#define SOURCE_CIPHER_CAR		0x0920
+#define SOURCE_CRYPTO_CAR		0x0924
+
+/* clock meters addr */
+#define CM_CTRL				0x0a00
+#define CM_I2S_CTRL			0x0a04
+#define CM_SPDIF_CTRL			0x0a08
+#define CM_VID_CTRL			0x0a0c
+#define CM_LANE_CTRL			0x0a10
+#define I2S_NM_STABLE			0x0a14
+#define I2S_NCTS_STABLE			0x0a18
+#define SPDIF_NM_STABLE			0x0a1c
+#define SPDIF_NCTS_STABLE		0x0a20
+#define NMVID_MEAS_STABLE		0x0a24
+#define I2S_MEAS			0x0a40
+#define SPDIF_MEAS			0x0a80
+#define NMVID_MEAS			0x0ac0
+
+/* source vif addr */
+#define BND_HSYNC2VSYNC			0x0b00
+#define HSYNC2VSYNC_F1_L1		0x0b04
+#define HSYNC2VSYNC_F2_L1		0x0b08
+#define HSYNC2VSYNC_STATUS		0x0b0c
+#define HSYNC2VSYNC_POL_CTRL		0x0b10
+
+/* dptx phy addr */
+#define DP_TX_PHY_CONFIG_REG		0x2000
+#define DP_TX_PHY_STATUS_REG		0x2004
+#define DP_TX_PHY_SW_RESET		0x2008
+#define DP_TX_PHY_SCRAMBLER_SEED	0x200c
+#define DP_TX_PHY_TRAINING_01_04	0x2010
+#define DP_TX_PHY_TRAINING_05_08	0x2014
+#define DP_TX_PHY_TRAINING_09_10	0x2018
+#define TEST_COR			0x23fc
+
+/* dptx hpd addr */
+#define HPD_IRQ_DET_MIN_TIMER		0x2100
+#define HPD_IRQ_DET_MAX_TIMER		0x2104
+#define HPD_UNPLGED_DET_MIN_TIMER	0x2108
+#define HPD_STABLE_TIMER		0x210c
+#define HPD_FILTER_TIMER		0x2110
+#define HPD_EVENT_MASK			0x211c
+#define HPD_EVENT_DET			0x2120
+
+/* dpyx framer addr */
+#define DP_FRAMER_GLOBAL_CONFIG		0x2200
+#define DP_SW_RESET			0x2204
+#define DP_FRAMER_TU			0x2208
+#define DP_FRAMER_PXL_REPR		0x220c
+#define DP_FRAMER_SP			0x2210
+#define AUDIO_PACK_CONTROL		0x2214
+#define DP_VC_TABLE(x)			(0x2218 + ((x) << 2))
+#define DP_VB_ID			0x2258
+#define DP_MTPH_LVP_CONTROL		0x225c
+#define DP_MTPH_SYMBOL_VALUES		0x2260
+#define DP_MTPH_ECF_CONTROL		0x2264
+#define DP_MTPH_ACT_CONTROL		0x2268
+#define DP_MTPH_STATUS			0x226c
+#define DP_INTERRUPT_SOURCE		0x2270
+#define DP_INTERRUPT_MASK		0x2274
+#define DP_FRONT_BACK_PORCH		0x2278
+#define DP_BYTE_COUNT			0x227c
+
+/* dptx stream addr */
+#define MSA_HORIZONTAL_0		0x2280
+#define MSA_HORIZONTAL_1		0x2284
+#define MSA_VERTICAL_0			0x2288
+#define MSA_VERTICAL_1			0x228c
+#define MSA_MISC			0x2290
+#define STREAM_CONFIG			0x2294
+#define AUDIO_PACK_STATUS		0x2298
+#define VIF_STATUS			0x229c
+#define PCK_STUFF_STATUS_0		0x22a0
+#define PCK_STUFF_STATUS_1		0x22a4
+#define INFO_PACK_STATUS		0x22a8
+#define RATE_GOVERNOR_STATUS		0x22ac
+#define DP_HORIZONTAL			0x22b0
+#define DP_VERTICAL_0			0x22b4
+#define DP_VERTICAL_1			0x22b8
+#define DP_BLOCK_SDP			0x22bc
+
+/* dptx glbl addr */
+#define DPTX_LANE_EN			0x2300
+#define DPTX_ENHNCD			0x2304
+#define DPTX_INT_MASK			0x2308
+#define DPTX_INT_STATUS			0x230c
+
+/* dp aux addr */
+#define DP_AUX_HOST_CONTROL		0x2800
+#define DP_AUX_INTERRUPT_SOURCE		0x2804
+#define DP_AUX_INTERRUPT_MASK		0x2808
+#define DP_AUX_SWAP_INVERSION_CONTROL	0x280c
+#define DP_AUX_SEND_NACK_TRANSACTION	0x2810
+#define DP_AUX_CLEAR_RX			0x2814
+#define DP_AUX_CLEAR_TX			0x2818
+#define DP_AUX_TIMER_STOP		0x281c
+#define DP_AUX_TIMER_CLEAR		0x2820
+#define DP_AUX_RESET_SW			0x2824
+#define DP_AUX_DIVIDE_2M		0x2828
+#define DP_AUX_TX_PREACHARGE_LENGTH	0x282c
+#define DP_AUX_FREQUENCY_1M_MAX		0x2830
+#define DP_AUX_FREQUENCY_1M_MIN		0x2834
+#define DP_AUX_RX_PRE_MIN		0x2838
+#define DP_AUX_RX_PRE_MAX		0x283c
+#define DP_AUX_TIMER_PRESET		0x2840
+#define DP_AUX_NACK_FORMAT		0x2844
+#define DP_AUX_TX_DATA			0x2848
+#define DP_AUX_RX_DATA			0x284c
+#define DP_AUX_TX_STATUS		0x2850
+#define DP_AUX_RX_STATUS		0x2854
+#define DP_AUX_RX_CYCLE_COUNTER		0x2858
+#define DP_AUX_MAIN_STATES		0x285c
+#define DP_AUX_MAIN_TIMER		0x2860
+#define DP_AUX_AFE_OUT			0x2864
+
+/* crypto addr */
+#define CRYPTO_HDCP_REVISION		0x5800
+#define HDCP_CRYPTO_CONFIG		0x5804
+#define CRYPTO_INTERRUPT_SOURCE		0x5808
+#define CRYPTO_INTERRUPT_MASK		0x580c
+#define CRYPTO22_CONFIG			0x5818
+#define CRYPTO22_STATUS			0x581c
+#define SHA_256_DATA_IN			0x583c
+#define SHA_256_DATA_OUT_(x)		(0x5850 + ((x) << 2))
+#define AES_32_KEY_(x)			(0x5870 + ((x) << 2))
+#define AES_32_DATA_IN			0x5880
+#define AES_32_DATA_OUT_(x)		(0x5884 + ((x) << 2))
+#define CRYPTO14_CONFIG			0x58a0
+#define CRYPTO14_STATUS			0x58a4
+#define CRYPTO14_PRNM_OUT		0x58a8
+#define CRYPTO14_KM_0			0x58ac
+#define CRYPTO14_KM_1			0x58b0
+#define CRYPTO14_AN_0			0x58b4
+#define CRYPTO14_AN_1			0x58b8
+#define CRYPTO14_YOUR_KSV_0		0x58bc
+#define CRYPTO14_YOUR_KSV_1		0x58c0
+#define CRYPTO14_MI_0			0x58c4
+#define CRYPTO14_MI_1			0x58c8
+#define CRYPTO14_TI_0			0x58cc
+#define CRYPTO14_KI_0			0x58d0
+#define CRYPTO14_KI_1			0x58d4
+#define CRYPTO14_BLOCKS_NUM		0x58d8
+#define CRYPTO14_KEY_MEM_DATA_0		0x58dc
+#define CRYPTO14_KEY_MEM_DATA_1		0x58e0
+#define CRYPTO14_SHA1_MSG_DATA		0x58e4
+#define CRYPTO14_SHA1_V_VALUE_(x)	(0x58e8 + ((x) << 2))
+#define TRNG_CTRL			0x58fc
+#define TRNG_DATA_RDY			0x5900
+#define TRNG_DATA			0x5904
+
+/* cipher addr */
+#define HDCP_REVISION			0x60000
+#define INTERRUPT_SOURCE		0x60004
+#define INTERRUPT_MASK			0x60008
+#define HDCP_CIPHER_CONFIG		0x6000c
+#define AES_128_KEY_0			0x60010
+#define AES_128_KEY_1			0x60014
+#define AES_128_KEY_2			0x60018
+#define AES_128_KEY_3			0x6001c
+#define AES_128_RANDOM_0		0x60020
+#define AES_128_RANDOM_1		0x60024
+#define CIPHER14_KM_0			0x60028
+#define CIPHER14_KM_1			0x6002c
+#define CIPHER14_STATUS			0x60030
+#define CIPHER14_RI_PJ_STATUS		0x60034
+#define CIPHER_MODE			0x60038
+#define CIPHER14_AN_0			0x6003c
+#define CIPHER14_AN_1			0x60040
+#define CIPHER22_AUTH			0x60044
+#define CIPHER14_R0_DP_STATUS		0x60048
+#define CIPHER14_BOOTSTRAP		0x6004c
+
+#define DPTX_FRMR_DATA_CLK_RSTN_EN	BIT(11)
+#define DPTX_FRMR_DATA_CLK_EN		BIT(10)
+#define DPTX_PHY_DATA_RSTN_EN		BIT(9)
+#define DPTX_PHY_DATA_CLK_EN		BIT(8)
+#define DPTX_PHY_CHAR_RSTN_EN		BIT(7)
+#define DPTX_PHY_CHAR_CLK_EN		BIT(6)
+#define SOURCE_AUX_SYS_CLK_RSTN_EN	BIT(5)
+#define SOURCE_AUX_SYS_CLK_EN		BIT(4)
+#define DPTX_SYS_CLK_RSTN_EN		BIT(3)
+#define DPTX_SYS_CLK_EN			BIT(2)
+#define CFG_DPTX_VIF_CLK_RSTN_EN	BIT(1)
+#define CFG_DPTX_VIF_CLK_EN		BIT(0)
+
+#define SOURCE_PHY_RSTN_EN		BIT(1)
+#define SOURCE_PHY_CLK_EN		BIT(0)
+
+#define SOURCE_PKT_SYS_RSTN_EN		BIT(3)
+#define SOURCE_PKT_SYS_CLK_EN		BIT(2)
+#define SOURCE_PKT_DATA_RSTN_EN		BIT(1)
+#define SOURCE_PKT_DATA_CLK_EN		BIT(0)
+
+#define SPDIF_CDR_CLK_RSTN_EN		BIT(5)
+#define SPDIF_CDR_CLK_EN		BIT(4)
+#define SOURCE_AIF_SYS_RSTN_EN		BIT(3)
+#define SOURCE_AIF_SYS_CLK_EN		BIT(2)
+#define SOURCE_AIF_CLK_RSTN_EN		BIT(1)
+#define SOURCE_AIF_CLK_EN		BIT(0)
+
+#define SOURCE_CIPHER_SYSTEM_CLK_RSTN_EN	BIT(3)
+#define SOURCE_CIPHER_SYS_CLK_EN		BIT(2)
+#define SOURCE_CIPHER_CHAR_CLK_RSTN_EN		BIT(1)
+#define SOURCE_CIPHER_CHAR_CLK_EN		BIT(0)
+
+#define SOURCE_CRYPTO_SYS_CLK_RSTN_EN	BIT(1)
+#define SOURCE_CRYPTO_SYS_CLK_EN	BIT(0)
+
+#define APB_IRAM_PATH			BIT(2)
+#define APB_DRAM_PATH			BIT(1)
+#define APB_XT_RESET			BIT(0)
+
+#define MAILBOX_INT_MASK_BIT		BIT(1)
+#define PIF_INT_MASK_BIT		BIT(0)
+#define ALL_INT_MASK			3
+
+/* mailbox */
+#define MB_OPCODE_ID			0
+#define MB_MODULE_ID			1
+#define MB_SIZE_MSB_ID			2
+#define MB_SIZE_LSB_ID			3
+#define MB_DATA_ID			4
+
+#define MB_MODULE_ID_DP_TX		0x01
+#define MB_MODULE_ID_HDCP_TX		0x07
+#define MB_MODULE_ID_HDCP_RX		0x08
+#define MB_MODULE_ID_HDCP_GENERAL	0x09
+#define MB_MODULE_ID_GENERAL		0x0a
+
+/* general opcode */
+#define GENERAL_MAIN_CONTROL            0x01
+#define GENERAL_TEST_ECHO               0x02
+#define GENERAL_BUS_SETTINGS            0x03
+#define GENERAL_TEST_ACCESS             0x04
+
+#define DPTX_SET_POWER_MNG			0x00
+#define DPTX_SET_HOST_CAPABILITIES		0x01
+#define DPTX_GET_EDID				0x02
+#define DPTX_READ_DPCD				0x03
+#define DPTX_WRITE_DPCD				0x04
+#define DPTX_ENABLE_EVENT			0x05
+#define DPTX_WRITE_REGISTER			0x06
+#define DPTX_READ_REGISTER			0x07
+#define DPTX_WRITE_FIELD			0x08
+#define DPTX_TRAINING_CONTROL			0x09
+#define DPTX_READ_EVENT				0x0a
+#define DPTX_READ_LINK_STAT			0x0b
+#define DPTX_SET_VIDEO				0x0c
+#define DPTX_SET_AUDIO				0x0d
+#define DPTX_GET_LAST_AUX_STAUS			0x0e
+#define DPTX_SET_LINK_BREAK_POINT		0x0f
+#define DPTX_FORCE_LANES			0x10
+#define DPTX_HPD_STATE				0x11
+
+#define FW_STANDBY				0
+#define FW_ACTIVE				1
+
+#define DPTX_EVENT_ENABLE_HPD			BIT(0)
+#define DPTX_EVENT_ENABLE_TRAINING		BIT(1)
+
+#define LINK_TRAINING_NOT_ACTIVE		0
+#define LINK_TRAINING_RUN			1
+#define LINK_TRAINING_RESTART			2
+
+#define CONTROL_VIDEO_IDLE			0
+#define CONTROL_VIDEO_VALID			1
+
+#define VIF_BYPASS_INTERLACE			BIT(13)
+#define INTERLACE_FMT_DET			BIT(12)
+#define INTERLACE_DTCT_WIN			0x20
+
+#define DP_FRAMER_SP_INTERLACE_EN		BIT(2)
+#define DP_FRAMER_SP_HSP			BIT(1)
+#define DP_FRAMER_SP_VSP			BIT(0)
+
+/* capability */
+#define AUX_HOST_INVERT				3
+#define	FAST_LT_SUPPORT				1
+#define FAST_LT_NOT_SUPPORT			0
+#define LANE_MAPPING_NORMAL			0xe4
+#define LANE_MAPPING_FLIPPED			0x1b
+#define ENHANCED				1
+
+#define	FULL_LT_STARTED				BIT(0)
+#define FASE_LT_STARTED				BIT(1)
+#define CLK_RECOVERY_FINISHED			BIT(2)
+#define EQ_PHASE_FINISHED			BIT(3)
+#define FASE_LT_START_FINISHED			BIT(4)
+#define CLK_RECOVERY_FAILED			BIT(5)
+#define EQ_PHASE_FAILED				BIT(6)
+#define FASE_LT_FAILED				BIT(7)
+
+#define DPTX_HPD_EVENT				BIT(0)
+#define DPTX_TRAINING_EVENT			BIT(1)
+#define HDCP_TX_STATUS_EVENT			BIT(4)
+#define HDCP2_TX_IS_KM_STORED_EVENT		BIT(5)
+#define HDCP2_TX_STORE_KM_EVENT			BIT(6)
+#define HDCP_TX_IS_RECEIVER_ID_VALID_EVENT	BIT(7)
+
+#define TU_SIZE					30
+#define CDN_DP_MAX_LINK_RATE			DP_LINK_BW_5_4
+
+/* audio */
+#define AUDIO_PACK_EN				BIT(8)
+#define SAMPLING_FREQ(x)			(((x) & 0xf) << 16)
+#define ORIGINAL_SAMP_FREQ(x)			(((x) & 0xf) << 24)
+#define SYNC_WR_TO_CH_ZERO			BIT(1)
+#define I2S_DEC_START				BIT(1)
+#define AUDIO_SW_RST				BIT(0)
+#define SMPL2PKT_EN				BIT(1)
+#define MAX_NUM_CH(x)				(((x) & 0x1f) - 1)
+#define NUM_OF_I2S_PORTS(x)			((((x) / 2 - 1) & 0x3) << 5)
+#define AUDIO_TYPE_LPCM				(2 << 7)
+#define CFG_SUB_PCKT_NUM(x)			((((x) - 1) & 0x7) << 11)
+#define AUDIO_CH_NUM(x)				((((x) - 1) & 0x1f) << 2)
+#define TRANS_SMPL_WIDTH_16			0
+#define TRANS_SMPL_WIDTH_24			BIT(11)
+#define TRANS_SMPL_WIDTH_32			(2 << 11)
+#define I2S_DEC_PORT_EN(x)			(((x) & 0xf) << 17)
+#define SPDIF_ENABLE				BIT(21)
+#define SPDIF_AVG_SEL				BIT(20)
+#define SPDIF_JITTER_BYPASS			BIT(19)
+#define SPDIF_FIFO_MID_RANGE(x)			(((x) & 0xff) << 11)
+#define SPDIF_JITTER_THRSH(x)			(((x) & 0xff) << 3)
+#define SPDIF_JITTER_AVG_WIN(x)			((x) & 0x7)
+
+/* Refernce cycles when using lane clock as refernce */
+#define LANE_REF_CYC				0x8000
+
+enum voltage_swing_level {
+	VOLTAGE_LEVEL_0,
+	VOLTAGE_LEVEL_1,
+	VOLTAGE_LEVEL_2,
+	VOLTAGE_LEVEL_3,
+};
+
+enum pre_emphasis_level {
+	PRE_EMPHASIS_LEVEL_0,
+	PRE_EMPHASIS_LEVEL_1,
+	PRE_EMPHASIS_LEVEL_2,
+	PRE_EMPHASIS_LEVEL_3,
+};
+
+enum pattern_set {
+	PTS1		= BIT(0),
+	PTS2		= BIT(1),
+	PTS3		= BIT(2),
+	PTS4		= BIT(3),
+	DP_NONE		= BIT(4)
+};
+
+enum vic_color_depth {
+	BCS_6 = 0x1,
+	BCS_8 = 0x2,
+	BCS_10 = 0x4,
+	BCS_12 = 0x8,
+	BCS_16 = 0x10,
+};
+
+enum vic_bt_type {
+	BT_601 = 0x0,
+	BT_709 = 0x1,
+};
+
+void cdn_dp_clock_reset(struct cdn_dp_device *dp);
+
+void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, u32 clk);
+int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
+			 u32 i_size, const u32 *d_mem, u32 d_size);
+int cdn_dp_active(struct cdn_dp_device *dp, bool enable);
+int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes);
+int cdn_dp_event_config(struct cdn_dp_device *dp);
+u32 cdn_dp_get_event(struct cdn_dp_device *dp);
+int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
+int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value);
+int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len);
+int cdn_dp_get_edid_block(void *dp, u8 *edid,
+			  unsigned int block, size_t length);
+int cdn_dp_training_start(struct cdn_dp_device *dp);
+int cdn_dp_get_training_status(struct cdn_dp_device *dp);
+int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active);
+int cdn_dp_config_video(struct cdn_dp_device *dp);
+int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio);
+int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable);
+int cdn_dp_audio_config_set(struct cdn_dp_device *dp, struct audio_info *audio);
+#endif /* _CDN_DP_REG_H */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index edd7ec2..d723d8e 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -969,9 +969,11 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
 		vop_dsp_hold_valid_irq_disable(vop);
 	}
 
-	pin_pol = 0x8;
-	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1;
-	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : (1 << 1);
+	pin_pol = BIT(DCLK_INVERT);
+	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ?
+		   0 : BIT(HSYNC_POSITIVE);
+	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ?
+		   0 : BIT(VSYNC_POSITIVE);
 	VOP_CTRL_SET(vop, pin_pol, pin_pol);
 
 	switch (s->output_type) {
@@ -991,6 +993,11 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
 		VOP_CTRL_SET(vop, mipi_pin_pol, pin_pol);
 		VOP_CTRL_SET(vop, mipi_en, 1);
 		break;
+	case DRM_MODE_CONNECTOR_DisplayPort:
+		pin_pol &= ~BIT(DCLK_INVERT);
+		VOP_CTRL_SET(vop, dp_pin_pol, pin_pol);
+		VOP_CTRL_SET(vop, dp_en, 1);
+		break;
 	default:
 		DRM_ERROR("unsupport connector_type[%d]\n", s->output_type);
 	}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index ff4f52e..4820a8b 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -45,6 +45,7 @@ struct vop_ctrl {
 	struct vop_reg edp_en;
 	struct vop_reg hdmi_en;
 	struct vop_reg mipi_en;
+	struct vop_reg dp_en;
 	struct vop_reg out_mode;
 	struct vop_reg dither_down;
 	struct vop_reg dither_up;
@@ -53,6 +54,7 @@ struct vop_ctrl {
 	struct vop_reg hdmi_pin_pol;
 	struct vop_reg edp_pin_pol;
 	struct vop_reg mipi_pin_pol;
+	struct vop_reg dp_pin_pol;
 
 	struct vop_reg htotal_pw;
 	struct vop_reg hact_st_end;
@@ -242,6 +244,13 @@ enum scale_down_mode {
 	SCALE_DOWN_AVG = 0x1
 };
 
+enum vop_pol {
+	HSYNC_POSITIVE = 0,
+	VSYNC_POSITIVE = 1,
+	DEN_NEGATIVE   = 2,
+	DCLK_INVERT    = 3
+};
+
 #define FRAC_16_16(mult, div)    (((mult) << 16) / (div))
 #define SCL_FT_DEFAULT_FIXPOINT_SHIFT	12
 #define SCL_MAX_VSKIPLINES		4
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 5b1ae1f..dcf172e 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -281,6 +281,7 @@ static const struct vop_data rk3288_vop = {
 static const struct vop_ctrl rk3399_ctrl_data = {
 	.standby = VOP_REG(RK3399_SYS_CTRL, 0x1, 22),
 	.gate_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 23),
+	.dp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 11),
 	.rgb_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 12),
 	.hdmi_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 13),
 	.edp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 14),
@@ -290,6 +291,7 @@ static const struct vop_ctrl rk3399_ctrl_data = {
 	.data_blank = VOP_REG(RK3399_DSP_CTRL0, 0x1, 19),
 	.out_mode = VOP_REG(RK3399_DSP_CTRL0, 0xf, 0),
 	.rgb_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16),
+	.dp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16),
 	.hdmi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 20),
 	.edp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 24),
 	.mipi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 28),
-- 
2.6.3



From wxt at rock-chips.com  Wed Jul 27 07:10:42 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Wed, 27 Jul 2016 22:10:42 +0800
Subject: [PATCH 1/2] thermal: rockchip: enhance the tsadc's bandgap feature
 for rk3399
Message-ID: <1469628643-29661-1-git-send-email-wxt@rock-chips.com>

From: Rocky Hao <rocky.hao at rock-chips.com>

Due to the voltage ripple, the sensing data of the tsadc is not accurate.
And in this patch, the bandgap feature is enhanced to remove the voltage
ripple, and then the tsadc can sense the temperature more precisely.

Obsolete codes are removed as well.

Signed-off-by: Rocky Hao <rocky.hao at rock-chips.com>
Signed-off-by: Caesar Wang <wxt at rock-chips.com>
Cc: Eduardo Valentin <edubezval at gmail.com>
Cc: Zhang Rui <rui.zhang at intel.com>
Cc: Heiko Stuebner <heiko at sntech.de>
Cc: linux-pm at vger.kernel.org
---

 drivers/thermal/rockchip_thermal.c | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c
index db5ecc5..a90423d 100644
--- a/drivers/thermal/rockchip_thermal.c
+++ b/drivers/thermal/rockchip_thermal.c
@@ -222,10 +222,10 @@ struct rockchip_thermal_data {
 #define GRF_TSADC_TESTBIT_L			0x0e648
 #define GRF_TSADC_TESTBIT_H			0x0e64c
 
-#define GRF_TSADC_TSEN_PD_ON			(0x30003 << 0)
-#define GRF_TSADC_TSEN_PD_OFF			(0x30000 << 0)
 #define GRF_SARADC_TESTBIT_ON			(0x10001 << 2)
 #define GRF_TSADC_TESTBIT_H_ON			(0x10001 << 2)
+#define GRF_TSADC_VCM_EN_L			(0x10001 << 7)
+#define GRF_TSADC_VCM_EN_H			(0x10001 << 7)
 
 /**
  * struct tsadc_table - code to temperature conversion table
@@ -567,9 +567,10 @@ static void rk_tsadcv3_initialize(struct regmap *grf, void __iomem *regs,
 			       regs + TSADCV2_HIGHT_TSHUT_DEBOUNCE);
 
 	} else {
-		regmap_write(grf, GRF_TSADC_TESTBIT_L, GRF_TSADC_TSEN_PD_ON);
-		mdelay(10);
-		regmap_write(grf, GRF_TSADC_TESTBIT_L, GRF_TSADC_TSEN_PD_OFF);
+		/* Enable the voltage common mode feature */
+		regmap_write(grf, GRF_TSADC_TESTBIT_L, GRF_TSADC_VCM_EN_L);
+		regmap_write(grf, GRF_TSADC_TESTBIT_H, GRF_TSADC_VCM_EN_H);
+
 		usleep_range(15, 100); /* The spec note says at least 15 us */
 		regmap_write(grf, GRF_SARADC_TESTBIT, GRF_SARADC_TESTBIT_ON);
 		regmap_write(grf, GRF_TSADC_TESTBIT_H, GRF_TSADC_TESTBIT_H_ON);
-- 
1.9.1



From wxt at rock-chips.com  Wed Jul 27 07:10:43 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Wed, 27 Jul 2016 22:10:43 +0800
Subject: [PATCH 2/2] thermal: rockchip: optimize sensor auto accessing period
In-Reply-To: <1469628643-29661-1-git-send-email-wxt@rock-chips.com>
References: <1469628643-29661-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1469628643-29661-2-git-send-email-wxt@rock-chips.com>

From: Rocky Hao <rocky.hao at rock-chips.com>

In less than 10 ms, the temperature of soc will arise 10 degree. 250 ms
is too big for soc tempeture control. Setting 2.5 ms will speed up
temperature accessing speed but introduce no more cpu's computing overhead.
We set AUTO_PERIOD_TIME and TSADCV3_AUTO_PERIOD_HT_TIME the same value,
because normal temperature update speed is also our consern in IPA.

Signed-off-by: Rocky Hao <rocky.hao at rock-chips.com>
Signed-off-by: Caesar Wang <wxt at rock-chips.com>
Cc: Zhang Rui <rui.zhang at intel.com>
Cc: Eduardo Valentin <edubezval at gmail.com>
Cc: Heiko Stuebner <heiko at sntech.de>
Cc: linux-pm at vger.kernel.org

---

 drivers/thermal/rockchip_thermal.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c
index a90423d..1f165c9 100644
--- a/drivers/thermal/rockchip_thermal.c
+++ b/drivers/thermal/rockchip_thermal.c
@@ -213,8 +213,8 @@ struct rockchip_thermal_data {
 #define TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT	4
 #define TSADCV2_AUTO_PERIOD_TIME		250 /* 250ms */
 #define TSADCV2_AUTO_PERIOD_HT_TIME		50  /* 50ms */
-#define TSADCV3_AUTO_PERIOD_TIME		187500 /* 250ms */
-#define TSADCV3_AUTO_PERIOD_HT_TIME		37500  /* 50ms */
+#define TSADCV3_AUTO_PERIOD_TIME		1875 /* 2.5ms */
+#define TSADCV3_AUTO_PERIOD_HT_TIME		1875 /* 2.5ms */
 
 #define TSADCV2_USER_INTER_PD_SOC		0x340 /* 13 clocks */
 
-- 
1.9.1



From wxt at rock-chips.com  Wed Jul 27 07:24:04 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Wed, 27 Jul 2016 22:24:04 +0800
Subject: [PATCH v3 1/4] iio: adc: rockchip_saradc: reset saradc controller
 before programming it
Message-ID: <1469629447-544-1-git-send-email-wxt@rock-chips.com>

SARADC controller needs to be reset before programming it, otherwise
it will not function properly.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>
Cc: Jonathan Cameron <jic23 at kernel.org>
Cc: Heiko Stuebner <heiko at sntech.de>
Cc: Rob Herring <robh+dt at kernel.org>
Cc: linux-iio at vger.kernel.org
Cc: linux-rockchip at lists.infradead.org
Tested-by: Guenter Roeck <linux at roeck-us.net>

---

Changes in v3:
- %s/devm_reset_control_get_optional()/devm_reset_control_get()
- add Guente's test tag.

Changes in v2:
- Make the reset as an optional property, since it should work
with old devicetrees as well.

 .../bindings/iio/adc/rockchip-saradc.txt           |  7 +++++
 drivers/iio/adc/Kconfig                            |  1 +
 drivers/iio/adc/rockchip_saradc.c                  | 30 ++++++++++++++++++++++
 3 files changed, 38 insertions(+)

diff --git a/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt b/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt
index bf99e2f..205593f 100644
--- a/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt
+++ b/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt
@@ -16,6 +16,11 @@ Required properties:
 - vref-supply: The regulator supply ADC reference voltage.
 - #io-channel-cells: Should be 1, see ../iio-bindings.txt
 
+Optional properties:
+- resets: Must contain an entry for each entry in reset-names if need support
+	  this option. See ../reset/reset.txt for details.
+- reset-names: Must include the name "saradc-apb".
+
 Example:
 	saradc: saradc at 2006c000 {
 		compatible = "rockchip,saradc";
@@ -23,6 +28,8 @@ Example:
 		interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
 		clocks = <&cru SCLK_SARADC>, <&cru PCLK_SARADC>;
 		clock-names = "saradc", "apb_pclk";
+		resets = <&cru SRST_SARADC>;
+		reset-names = "saradc-apb";
 		#io-channel-cells = <1>;
 		vref-supply = <&vcc18>;
 	};
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 1de31bd..7675772 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -389,6 +389,7 @@ config QCOM_SPMI_VADC
 config ROCKCHIP_SARADC
 	tristate "Rockchip SARADC driver"
 	depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST)
+	depends on RESET_CONTROLLER
 	help
 	  Say yes here to build support for the SARADC found in SoCs from
 	  Rockchip.
diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c
index f9ad6c2..85d7012 100644
--- a/drivers/iio/adc/rockchip_saradc.c
+++ b/drivers/iio/adc/rockchip_saradc.c
@@ -21,6 +21,8 @@
 #include <linux/of_device.h>
 #include <linux/clk.h>
 #include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/reset.h>
 #include <linux/regulator/consumer.h>
 #include <linux/iio/iio.h>
 
@@ -53,6 +55,7 @@ struct rockchip_saradc {
 	struct clk		*clk;
 	struct completion	completion;
 	struct regulator	*vref;
+	struct reset_control	*reset;
 	const struct rockchip_saradc_data *data;
 	u16			last_val;
 };
@@ -190,6 +193,16 @@ static const struct of_device_id rockchip_saradc_match[] = {
 };
 MODULE_DEVICE_TABLE(of, rockchip_saradc_match);
 
+/**
+ * Reset SARADC Controller.
+ */
+static void rockchip_saradc_reset_controller(struct reset_control *reset)
+{
+	reset_control_assert(reset);
+	usleep_range(10, 20);
+	reset_control_deassert(reset);
+}
+
 static int rockchip_saradc_probe(struct platform_device *pdev)
 {
 	struct rockchip_saradc *info = NULL;
@@ -218,6 +231,20 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
 	if (IS_ERR(info->regs))
 		return PTR_ERR(info->regs);
 
+	/*
+	 * The reset should be an optional property, as it should work
+	 * with old devicetrees as well
+	 */
+	info->reset = devm_reset_control_get(&pdev->dev, "saradc-apb");
+	if (IS_ERR(info->reset)) {
+		ret = PTR_ERR(info->reset);
+		if (ret != -ENOENT)
+			return ret;
+
+		dev_dbg(&pdev->dev, "no reset control found\n");
+		info->reset = NULL;
+	}
+
 	init_completion(&info->completion);
 
 	irq = platform_get_irq(pdev, 0);
@@ -252,6 +279,9 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
 		return PTR_ERR(info->vref);
 	}
 
+	if (info->reset)
+		rockchip_saradc_reset_controller(info->reset);
+
 	/*
 	 * Use a default value for the converter clock.
 	 * This may become user-configurable in the future.
-- 
1.9.1



From wxt at rock-chips.com  Wed Jul 27 07:24:05 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Wed, 27 Jul 2016 22:24:05 +0800
Subject: [PATCH v3 2/4] arm64: dts: rockchip: add the saradc for rk3399
In-Reply-To: <1469629447-544-1-git-send-email-wxt@rock-chips.com>
References: <1469629447-544-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1469629447-544-2-git-send-email-wxt@rock-chips.com>

This patch adds saradc needed information on rk3399 SoCs.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>
Reviewed-by: Douglas Anderson <dianders at chromium.org>
---

Changes in v3: None
Changes in v2: None

 arch/arm64/boot/dts/rockchip/rk3399.dtsi | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index 4c84229..b81f84b 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -299,6 +299,18 @@
 		};
 	};
 
+	saradc: saradc at ff100000 {
+		compatible = "rockchip,rk3399-saradc";
+		reg = <0x0 0xff100000 0x0 0x100>;
+		interrupts = <GIC_SPI 62 IRQ_TYPE_LEVEL_HIGH>;
+		#io-channel-cells = <1>;
+		clocks = <&cru SCLK_SARADC>, <&cru PCLK_SARADC>;
+		clock-names = "saradc", "apb_pclk";
+		resets = <&cru SRST_P_SARADC>;
+		reset-names = "saradc-apb";
+		status = "disabled";
+	};
+
 	i2c1: i2c at ff110000 {
 		compatible = "rockchip,rk3399-i2c";
 		reg = <0x0 0xff110000 0x0 0x1000>;
-- 
1.9.1



From wxt at rock-chips.com  Wed Jul 27 07:24:06 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Wed, 27 Jul 2016 22:24:06 +0800
Subject: [PATCH v3 3/4] arm64: dts: rockchip: add reset saradc node for rk3368
 SoCs
In-Reply-To: <1469629447-544-1-git-send-email-wxt@rock-chips.com>
References: <1469629447-544-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1469629447-544-3-git-send-email-wxt@rock-chips.com>

SARADC controller needs to be reset before programming it, otherwise
it will not function properly.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>
---

Changes in v3:
- add Doug's reviewed tag.

Changes in v2: None

 arch/arm64/boot/dts/rockchip/rk3368.dtsi | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3368.dtsi b/arch/arm64/boot/dts/rockchip/rk3368.dtsi
index d02a9003..4f44d11 100644
--- a/arch/arm64/boot/dts/rockchip/rk3368.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3368.dtsi
@@ -270,6 +270,8 @@
 		#io-channel-cells = <1>;
 		clocks = <&cru SCLK_SARADC>, <&cru PCLK_SARADC>;
 		clock-names = "saradc", "apb_pclk";
+		resets = <&cru SRST_SARADC>;
+		reset-names = "saradc-apb";
 		status = "disabled";
 	};
 
-- 
1.9.1



From wxt at rock-chips.com  Wed Jul 27 07:24:07 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Wed, 27 Jul 2016 22:24:07 +0800
Subject: [PATCH v3 4/4] arm: dts: rockchip: add reset node for the exist
 saradc SoCs
In-Reply-To: <1469629447-544-1-git-send-email-wxt@rock-chips.com>
References: <1469629447-544-1-git-send-email-wxt@rock-chips.com>
Message-ID: <1469629447-544-4-git-send-email-wxt@rock-chips.com>

SARADC controller needs to be reset before programming it, otherwise
it will not function properly.

Signed-off-by: Caesar Wang <wxt at rock-chips.com>
---

Changes in v3: None
Changes in v2: None

 arch/arm/boot/dts/rk3066a.dtsi | 2 ++
 arch/arm/boot/dts/rk3288.dtsi  | 2 ++
 arch/arm/boot/dts/rk3xxx.dtsi  | 2 ++
 3 files changed, 6 insertions(+)

diff --git a/arch/arm/boot/dts/rk3066a.dtsi b/arch/arm/boot/dts/rk3066a.dtsi
index c0ba86c..0d0dae3 100644
--- a/arch/arm/boot/dts/rk3066a.dtsi
+++ b/arch/arm/boot/dts/rk3066a.dtsi
@@ -197,6 +197,8 @@
 		clock-names = "saradc", "apb_pclk";
 		interrupts = <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
 		#io-channel-cells = <1>;
+		resets = <&cru SRST_SARADC>;
+		reset-names = "saradc-apb";
 		status = "disabled";
 	};
 
diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi
index cd33f01..91c4b3c 100644
--- a/arch/arm/boot/dts/rk3288.dtsi
+++ b/arch/arm/boot/dts/rk3288.dtsi
@@ -279,6 +279,8 @@
 		#io-channel-cells = <1>;
 		clocks = <&cru SCLK_SARADC>, <&cru PCLK_SARADC>;
 		clock-names = "saradc", "apb_pclk";
+		resets = <&cru SRST_SARADC>;
+		reset-names = "saradc-apb";
 		status = "disabled";
 	};
 
diff --git a/arch/arm/boot/dts/rk3xxx.dtsi b/arch/arm/boot/dts/rk3xxx.dtsi
index 99bbcc2..e2cd683 100644
--- a/arch/arm/boot/dts/rk3xxx.dtsi
+++ b/arch/arm/boot/dts/rk3xxx.dtsi
@@ -399,6 +399,8 @@
 		#io-channel-cells = <1>;
 		clocks = <&cru SCLK_SARADC>, <&cru PCLK_SARADC>;
 		clock-names = "saradc", "apb_pclk";
+		resets = <&cru SRST_SARADC>;
+		reset-names = "saradc-apb";
 		status = "disabled";
 	};
 
-- 
1.9.1



From pmeerw at pmeerw.net  Wed Jul 27 07:47:48 2016
From: pmeerw at pmeerw.net (Peter Meerwald-Stadler)
Date: Wed, 27 Jul 2016 16:47:48 +0200 (CEST)
Subject: [PATCH v3 1/4] iio: adc: rockchip_saradc: reset saradc controller
 before programming it
In-Reply-To: <1469629447-544-1-git-send-email-wxt@rock-chips.com>
References: <1469629447-544-1-git-send-email-wxt@rock-chips.com>
Message-ID: <alpine.DEB.2.02.1607271642110.22647@pmeerw.net>


> SARADC controller needs to be reset before programming it, otherwise
> it will not function properly.

nitpicking on wording below
 
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
> Cc: Jonathan Cameron <jic23 at kernel.org>
> Cc: Heiko Stuebner <heiko at sntech.de>
> Cc: Rob Herring <robh+dt at kernel.org>
> Cc: linux-iio at vger.kernel.org
> Cc: linux-rockchip at lists.infradead.org
> Tested-by: Guenter Roeck <linux at roeck-us.net>
> 
> ---
> 
> Changes in v3:
> - %s/devm_reset_control_get_optional()/devm_reset_control_get()
> - add Guente's test tag.
> 
> Changes in v2:
> - Make the reset as an optional property, since it should work
> with old devicetrees as well.
> 
>  .../bindings/iio/adc/rockchip-saradc.txt           |  7 +++++
>  drivers/iio/adc/Kconfig                            |  1 +
>  drivers/iio/adc/rockchip_saradc.c                  | 30 ++++++++++++++++++++++
>  3 files changed, 38 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt b/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt
> index bf99e2f..205593f 100644
> --- a/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt
> +++ b/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt
> @@ -16,6 +16,11 @@ Required properties:
>  - vref-supply: The regulator supply ADC reference voltage.
>  - #io-channel-cells: Should be 1, see ../iio-bindings.txt
>  
> +Optional properties:
> +- resets: Must contain an entry for each entry in reset-names if need support
> +	  this option. See ../reset/reset.txt for details.

'... if need support this option.' doesn't sound right, maybe 
simply: '... if needed.' or drop this clause.

> +- reset-names: Must include the name "saradc-apb".
> +
>  Example:
>  	saradc: saradc at 2006c000 {
>  		compatible = "rockchip,saradc";
> @@ -23,6 +28,8 @@ Example:
>  		interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
>  		clocks = <&cru SCLK_SARADC>, <&cru PCLK_SARADC>;
>  		clock-names = "saradc", "apb_pclk";
> +		resets = <&cru SRST_SARADC>;
> +		reset-names = "saradc-apb";
>  		#io-channel-cells = <1>;
>  		vref-supply = <&vcc18>;
>  	};
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 1de31bd..7675772 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -389,6 +389,7 @@ config QCOM_SPMI_VADC
>  config ROCKCHIP_SARADC
>  	tristate "Rockchip SARADC driver"
>  	depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST)
> +	depends on RESET_CONTROLLER
>  	help
>  	  Say yes here to build support for the SARADC found in SoCs from
>  	  Rockchip.
> diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c
> index f9ad6c2..85d7012 100644
> --- a/drivers/iio/adc/rockchip_saradc.c
> +++ b/drivers/iio/adc/rockchip_saradc.c
> @@ -21,6 +21,8 @@
>  #include <linux/of_device.h>
>  #include <linux/clk.h>
>  #include <linux/completion.h>
> +#include <linux/delay.h>
> +#include <linux/reset.h>
>  #include <linux/regulator/consumer.h>
>  #include <linux/iio/iio.h>
>  
> @@ -53,6 +55,7 @@ struct rockchip_saradc {
>  	struct clk		*clk;
>  	struct completion	completion;
>  	struct regulator	*vref;
> +	struct reset_control	*reset;
>  	const struct rockchip_saradc_data *data;
>  	u16			last_val;
>  };
> @@ -190,6 +193,16 @@ static const struct of_device_id rockchip_saradc_match[] = {
>  };
>  MODULE_DEVICE_TABLE(of, rockchip_saradc_match);
>  
> +/**
> + * Reset SARADC Controller.
> + */
> +static void rockchip_saradc_reset_controller(struct reset_control *reset)
> +{
> +	reset_control_assert(reset);
> +	usleep_range(10, 20);
> +	reset_control_deassert(reset);
> +}
> +
>  static int rockchip_saradc_probe(struct platform_device *pdev)
>  {
>  	struct rockchip_saradc *info = NULL;
> @@ -218,6 +231,20 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
>  	if (IS_ERR(info->regs))
>  		return PTR_ERR(info->regs);
>  
> +	/*
> +	 * The reset should be an optional property, as it should work
> +	 * with old devicetrees as well
> +	 */
> +	info->reset = devm_reset_control_get(&pdev->dev, "saradc-apb");
> +	if (IS_ERR(info->reset)) {
> +		ret = PTR_ERR(info->reset);
> +		if (ret != -ENOENT)
> +			return ret;
> +
> +		dev_dbg(&pdev->dev, "no reset control found\n");
> +		info->reset = NULL;
> +	}
> +
>  	init_completion(&info->completion);
>  
>  	irq = platform_get_irq(pdev, 0);
> @@ -252,6 +279,9 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
>  		return PTR_ERR(info->vref);
>  	}
>  
> +	if (info->reset)
> +		rockchip_saradc_reset_controller(info->reset);
> +
>  	/*
>  	 * Use a default value for the converter clock.
>  	 * This may become user-configurable in the future.
> 

-- 

Peter Meerwald-Stadler
+43-664-2444418 (mobile)


From linux at roeck-us.net  Wed Jul 27 07:50:27 2016
From: linux at roeck-us.net (Guenter Roeck)
Date: Wed, 27 Jul 2016 07:50:27 -0700
Subject: [PATCH v3 1/4] iio: adc: rockchip_saradc: reset saradc controller
 before programming it
In-Reply-To: <1469629447-544-1-git-send-email-wxt@rock-chips.com>
References: <1469629447-544-1-git-send-email-wxt@rock-chips.com>
Message-ID: <5798CA33.1070404@roeck-us.net>

On 07/27/2016 07:24 AM, Caesar Wang wrote:
> SARADC controller needs to be reset before programming it, otherwise
> it will not function properly.
>
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
> Cc: Jonathan Cameron <jic23 at kernel.org>
> Cc: Heiko Stuebner <heiko at sntech.de>
> Cc: Rob Herring <robh+dt at kernel.org>
> Cc: linux-iio at vger.kernel.org
> Cc: linux-rockchip at lists.infradead.org
> Tested-by: Guenter Roeck <linux at roeck-us.net>

Reviewed-by: Guenter Roeck <linux at roeck-us.net>

>
> ---
>
> Changes in v3:
> - %s/devm_reset_control_get_optional()/devm_reset_control_get()
> - add Guente's test tag.
>
> Changes in v2:
> - Make the reset as an optional property, since it should work
> with old devicetrees as well.
>
>   .../bindings/iio/adc/rockchip-saradc.txt           |  7 +++++
>   drivers/iio/adc/Kconfig                            |  1 +
>   drivers/iio/adc/rockchip_saradc.c                  | 30 ++++++++++++++++++++++
>   3 files changed, 38 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt b/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt
> index bf99e2f..205593f 100644
> --- a/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt
> +++ b/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt
> @@ -16,6 +16,11 @@ Required properties:
>   - vref-supply: The regulator supply ADC reference voltage.
>   - #io-channel-cells: Should be 1, see ../iio-bindings.txt
>
> +Optional properties:
> +- resets: Must contain an entry for each entry in reset-names if need support
> +	  this option. See ../reset/reset.txt for details.
> +- reset-names: Must include the name "saradc-apb".
> +
>   Example:
>   	saradc: saradc at 2006c000 {
>   		compatible = "rockchip,saradc";
> @@ -23,6 +28,8 @@ Example:
>   		interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
>   		clocks = <&cru SCLK_SARADC>, <&cru PCLK_SARADC>;
>   		clock-names = "saradc", "apb_pclk";
> +		resets = <&cru SRST_SARADC>;
> +		reset-names = "saradc-apb";
>   		#io-channel-cells = <1>;
>   		vref-supply = <&vcc18>;
>   	};
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 1de31bd..7675772 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -389,6 +389,7 @@ config QCOM_SPMI_VADC
>   config ROCKCHIP_SARADC
>   	tristate "Rockchip SARADC driver"
>   	depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST)
> +	depends on RESET_CONTROLLER
>   	help
>   	  Say yes here to build support for the SARADC found in SoCs from
>   	  Rockchip.
> diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c
> index f9ad6c2..85d7012 100644
> --- a/drivers/iio/adc/rockchip_saradc.c
> +++ b/drivers/iio/adc/rockchip_saradc.c
> @@ -21,6 +21,8 @@
>   #include <linux/of_device.h>
>   #include <linux/clk.h>
>   #include <linux/completion.h>
> +#include <linux/delay.h>
> +#include <linux/reset.h>
>   #include <linux/regulator/consumer.h>
>   #include <linux/iio/iio.h>
>
> @@ -53,6 +55,7 @@ struct rockchip_saradc {
>   	struct clk		*clk;
>   	struct completion	completion;
>   	struct regulator	*vref;
> +	struct reset_control	*reset;
>   	const struct rockchip_saradc_data *data;
>   	u16			last_val;
>   };
> @@ -190,6 +193,16 @@ static const struct of_device_id rockchip_saradc_match[] = {
>   };
>   MODULE_DEVICE_TABLE(of, rockchip_saradc_match);
>
> +/**
> + * Reset SARADC Controller.
> + */
> +static void rockchip_saradc_reset_controller(struct reset_control *reset)
> +{
> +	reset_control_assert(reset);
> +	usleep_range(10, 20);
> +	reset_control_deassert(reset);
> +}
> +
>   static int rockchip_saradc_probe(struct platform_device *pdev)
>   {
>   	struct rockchip_saradc *info = NULL;
> @@ -218,6 +231,20 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
>   	if (IS_ERR(info->regs))
>   		return PTR_ERR(info->regs);
>
> +	/*
> +	 * The reset should be an optional property, as it should work
> +	 * with old devicetrees as well
> +	 */
> +	info->reset = devm_reset_control_get(&pdev->dev, "saradc-apb");
> +	if (IS_ERR(info->reset)) {
> +		ret = PTR_ERR(info->reset);
> +		if (ret != -ENOENT)
> +			return ret;
> +
> +		dev_dbg(&pdev->dev, "no reset control found\n");
> +		info->reset = NULL;
> +	}
> +
>   	init_completion(&info->completion);
>
>   	irq = platform_get_irq(pdev, 0);
> @@ -252,6 +279,9 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
>   		return PTR_ERR(info->vref);
>   	}
>
> +	if (info->reset)
> +		rockchip_saradc_reset_controller(info->reset);
> +
>   	/*
>   	 * Use a default value for the converter clock.
>   	 * This may become user-configurable in the future.
>



From linux at roeck-us.net  Wed Jul 27 07:50:52 2016
From: linux at roeck-us.net (Guenter Roeck)
Date: Wed, 27 Jul 2016 07:50:52 -0700
Subject: [PATCH v3 2/4] arm64: dts: rockchip: add the saradc for rk3399
In-Reply-To: <1469629447-544-2-git-send-email-wxt@rock-chips.com>
References: <1469629447-544-1-git-send-email-wxt@rock-chips.com>
 <1469629447-544-2-git-send-email-wxt@rock-chips.com>
Message-ID: <5798CA4C.9030400@roeck-us.net>

On 07/27/2016 07:24 AM, Caesar Wang wrote:
> This patch adds saradc needed information on rk3399 SoCs.
>
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
> Reviewed-by: Douglas Anderson <dianders at chromium.org>

Reviewed-by: Guenter Roeck <linux at roeck-us.net>

> ---
>
> Changes in v3: None
> Changes in v2: None
>
>   arch/arm64/boot/dts/rockchip/rk3399.dtsi | 12 ++++++++++++
>   1 file changed, 12 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> index 4c84229..b81f84b 100644
> --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> @@ -299,6 +299,18 @@
>   		};
>   	};
>
> +	saradc: saradc at ff100000 {
> +		compatible = "rockchip,rk3399-saradc";
> +		reg = <0x0 0xff100000 0x0 0x100>;
> +		interrupts = <GIC_SPI 62 IRQ_TYPE_LEVEL_HIGH>;
> +		#io-channel-cells = <1>;
> +		clocks = <&cru SCLK_SARADC>, <&cru PCLK_SARADC>;
> +		clock-names = "saradc", "apb_pclk";
> +		resets = <&cru SRST_P_SARADC>;
> +		reset-names = "saradc-apb";
> +		status = "disabled";
> +	};
> +
>   	i2c1: i2c at ff110000 {
>   		compatible = "rockchip,rk3399-i2c";
>   		reg = <0x0 0xff110000 0x0 0x1000>;
>



From linux at roeck-us.net  Wed Jul 27 07:51:33 2016
From: linux at roeck-us.net (Guenter Roeck)
Date: Wed, 27 Jul 2016 07:51:33 -0700
Subject: [PATCH v3 3/4] arm64: dts: rockchip: add reset saradc node for
 rk3368 SoCs
In-Reply-To: <1469629447-544-3-git-send-email-wxt@rock-chips.com>
References: <1469629447-544-1-git-send-email-wxt@rock-chips.com>
 <1469629447-544-3-git-send-email-wxt@rock-chips.com>
Message-ID: <5798CA75.3010202@roeck-us.net>

On 07/27/2016 07:24 AM, Caesar Wang wrote:
> SARADC controller needs to be reset before programming it, otherwise
> it will not function properly.
>
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>

Reviewed-by: Guenter Roeck <linux at roeck-us.net>

> ---
>
> Changes in v3:
> - add Doug's reviewed tag.
>
Not to this patch ?

> Changes in v2: None
>
>   arch/arm64/boot/dts/rockchip/rk3368.dtsi | 2 ++
>   1 file changed, 2 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/rockchip/rk3368.dtsi b/arch/arm64/boot/dts/rockchip/rk3368.dtsi
> index d02a9003..4f44d11 100644
> --- a/arch/arm64/boot/dts/rockchip/rk3368.dtsi
> +++ b/arch/arm64/boot/dts/rockchip/rk3368.dtsi
> @@ -270,6 +270,8 @@
>   		#io-channel-cells = <1>;
>   		clocks = <&cru SCLK_SARADC>, <&cru PCLK_SARADC>;
>   		clock-names = "saradc", "apb_pclk";
> +		resets = <&cru SRST_SARADC>;
> +		reset-names = "saradc-apb";
>   		status = "disabled";
>   	};
>
>



From linux at roeck-us.net  Wed Jul 27 07:52:56 2016
From: linux at roeck-us.net (Guenter Roeck)
Date: Wed, 27 Jul 2016 07:52:56 -0700
Subject: [PATCH v3 4/4] arm: dts: rockchip: add reset node for the exist
 saradc SoCs
In-Reply-To: <1469629447-544-4-git-send-email-wxt@rock-chips.com>
References: <1469629447-544-1-git-send-email-wxt@rock-chips.com>
 <1469629447-544-4-git-send-email-wxt@rock-chips.com>
Message-ID: <5798CAC8.6030403@roeck-us.net>

On 07/27/2016 07:24 AM, Caesar Wang wrote:
> SARADC controller needs to be reset before programming it, otherwise
> it will not function properly.
>
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>

Reviewed-by: Guenter Roeck <linux at roeck-us.net>

> ---
>
> Changes in v3: None
> Changes in v2: None
>
>   arch/arm/boot/dts/rk3066a.dtsi | 2 ++
>   arch/arm/boot/dts/rk3288.dtsi  | 2 ++
>   arch/arm/boot/dts/rk3xxx.dtsi  | 2 ++
>   3 files changed, 6 insertions(+)
>
> diff --git a/arch/arm/boot/dts/rk3066a.dtsi b/arch/arm/boot/dts/rk3066a.dtsi
> index c0ba86c..0d0dae3 100644
> --- a/arch/arm/boot/dts/rk3066a.dtsi
> +++ b/arch/arm/boot/dts/rk3066a.dtsi
> @@ -197,6 +197,8 @@
>   		clock-names = "saradc", "apb_pclk";
>   		interrupts = <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
>   		#io-channel-cells = <1>;
> +		resets = <&cru SRST_SARADC>;
> +		reset-names = "saradc-apb";
>   		status = "disabled";
>   	};
>
> diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi
> index cd33f01..91c4b3c 100644
> --- a/arch/arm/boot/dts/rk3288.dtsi
> +++ b/arch/arm/boot/dts/rk3288.dtsi
> @@ -279,6 +279,8 @@
>   		#io-channel-cells = <1>;
>   		clocks = <&cru SCLK_SARADC>, <&cru PCLK_SARADC>;
>   		clock-names = "saradc", "apb_pclk";
> +		resets = <&cru SRST_SARADC>;
> +		reset-names = "saradc-apb";
>   		status = "disabled";
>   	};
>
> diff --git a/arch/arm/boot/dts/rk3xxx.dtsi b/arch/arm/boot/dts/rk3xxx.dtsi
> index 99bbcc2..e2cd683 100644
> --- a/arch/arm/boot/dts/rk3xxx.dtsi
> +++ b/arch/arm/boot/dts/rk3xxx.dtsi
> @@ -399,6 +399,8 @@
>   		#io-channel-cells = <1>;
>   		clocks = <&cru SCLK_SARADC>, <&cru PCLK_SARADC>;
>   		clock-names = "saradc", "apb_pclk";
> +		resets = <&cru SRST_SARADC>;
> +		reset-names = "saradc-apb";
>   		status = "disabled";
>   	};
>
>



From robh at kernel.org  Wed Jul 27 08:12:33 2016
From: robh at kernel.org (Rob Herring)
Date: Wed, 27 Jul 2016 10:12:33 -0500
Subject: [PATCH v2 1/4] iio: adc: rockchip_saradc: reset saradc
 controller before programming it
In-Reply-To: <1469535195-5227-1-git-send-email-wxt@rock-chips.com>
References: <1469535195-5227-1-git-send-email-wxt@rock-chips.com>
Message-ID: <20160727151233.GA31731@rob-hp-laptop>

On Tue, Jul 26, 2016 at 08:13:12PM +0800, Caesar Wang wrote:
> SARADC controller needs to be reset before programming it, otherwise
> it will not function properly.
> 
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
> Cc: Jonathan Cameron <jic23 at kernel.org>
> Cc: Heiko Stuebner <heiko at sntech.de>
> Cc: Rob Herring <robh+dt at kernel.org>
> Cc: linux-iio at vger.kernel.org
> Cc: linux-rockchip at lists.infradead.org
> 
> ---
> 
> Changes in v2:
> - Make the reset as an optional property, since it should work
> with old devicetrees as well.
> 
>  .../bindings/iio/adc/rockchip-saradc.txt           |  7 +++++

Acked-by: Rob Herring <robh at kernel.org>

>  drivers/iio/adc/Kconfig                            |  1 +
>  drivers/iio/adc/rockchip_saradc.c                  | 31 ++++++++++++++++++++++
>  3 files changed, 39 insertions(+)


From helgaas at kernel.org  Wed Jul 27 11:22:33 2016
From: helgaas at kernel.org (Bjorn Helgaas)
Date: Wed, 27 Jul 2016 13:22:33 -0500
Subject: [PATCH v7 2/2] PCI: Rockchip: Add Rockchip PCIe controller support
In-Reply-To: <1468802533-17699-2-git-send-email-shawn.lin@rock-chips.com>
References: <1468802533-17699-1-git-send-email-shawn.lin@rock-chips.com>
 <1468802533-17699-2-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <20160727182233.GB8901@localhost>

Hi Shawn,

I have some relatively minor comments below.

On Mon, Jul 18, 2016 at 08:42:13AM +0800, Shawn Lin wrote:
> This patch adds Rockchip PCIe controller support found
> on RK3399 Soc platform.
> 
> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
> 
> ---
> 
> Changes in v7:
> - make it as a build-in driver
> - improve gen1/2 training timeout checking
> - only clear known interrupt
> - fix INTx for 0-base index
> 
> Changes in v6:
> - use "depends on PCI_MSI_IRQ_DOMAIN" suggested by Arnd
> 
> Changes in v5:
> - handle multiple pending INTx at the same time
>   suggested by Marc
> 
> Changes in v4:
> - address the comments from Brain
> 
> Changes in v3:
> - remove header file
> - remove struct msi_controller and move most of variables
>   of rockchip_pcie_port to become the local ones.
> - allow legacy int even if enabling MSI
> - drop regulator set voltage operation suggested by Doug
> 
> Changes in v2:
> - remove phy related stuff and call phy API
> - add new head file and define lots of macro to make
>   the code more readable
> - remove lots msi related code suggested by Marc
> - add IO window address translation
> - init_port and parse_dt reconstruction suggested by Bharat
> - improve wr_own_conf suggested by Arnd
> - make pcie as an interrupt controller and fix wrong int handler
>   suggested by Marc
> - remove PCI_PROBE_ONLY suggested by Lorenzo
> 
>  drivers/pci/host/Kconfig         |   11 +
>  drivers/pci/host/Makefile        |    1 +
>  drivers/pci/host/pcie-rockchip.c | 1233 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 1245 insertions(+)
>  create mode 100644 drivers/pci/host/pcie-rockchip.c
> 
> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
> index c917e2f..4d714d2 100644
> --- a/drivers/pci/host/Kconfig
> +++ b/drivers/pci/host/Kconfig
> @@ -264,4 +264,15 @@ config PCIE_ARTPEC6
>  	  Say Y here to enable PCIe controller support on Axis ARTPEC-6
>  	  SoCs.  This PCIe controller uses the DesignWare core.
>  
> +config PCIE_ROCKCHIP
> +	bool "Rockchip PCIe controller"
> +	depends on ARM64 && ARCH_ROCKCHIP
> +	depends on OF
> +	depends on PCI_MSI_IRQ_DOMAIN
> +	select MFD_SYSCON
> +	help
> +	  Say Y here if you want internal PCI support on Rockchip SoC.
> +	  There is 1 internal PCIe port available to support GEN2 with
> +	  4 slots.
> +
>  endmenu
> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> index 5bc0af2..695f716 100644
> --- a/drivers/pci/host/Makefile
> +++ b/drivers/pci/host/Makefile
> @@ -30,3 +30,4 @@ obj-$(CONFIG_PCI_HOST_THUNDER_ECAM) += pci-thunder-ecam.o
>  obj-$(CONFIG_PCI_HOST_THUNDER_PEM) += pci-thunder-pem.o
>  obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o
>  obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o
> +obj-$(CONFIG_PCIE_ROCKCHIP) += pcie-rockchip.o
> diff --git a/drivers/pci/host/pcie-rockchip.c b/drivers/pci/host/pcie-rockchip.c
> new file mode 100644
> index 0000000..8a1fef1
> --- /dev/null
> +++ b/drivers/pci/host/pcie-rockchip.c
> @@ -0,0 +1,1233 @@
> +/*
> + * Rockchip AXI PCIe host controller driver
> + *
> + * Copyright (c) 2016 Rockchip, Inc.
> + *
> + * Author: Shawn Lin <shawn.lin at rock-chips.com>
> + *         Wenrui Li <wenrui.li at rock-chips.com>
> + *
> + * Bits taken from Synopsys Designware Host controller driver and
> + * ARM PCI Host generic driver.
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/irqdomain.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/of_pci.h>
> +#include <linux/of_platform.h>
> +#include <linux/of_irq.h>
> +#include <linux/pci.h>
> +#include <linux/pci_ids.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/reset.h>
> +#include <linux/regmap.h>
> +
> +#define PCIE_CLIENT_BASE                0x0
> +#define PCIE_RC_CONFIG_NORMAL_BASE      0x800000
> +#define PCIE_RC_CONFIG_BASE             0xa00000
> +#define PCIE_CORE_LINK_CTRL_STATUS      0x8000d0
> +#define PCIE_CORE_CTRL_MGMT_BASE        0x900000
> +#define PCIE_CORE_AXI_CONF_BASE         0xc00000
> +#define PCIE_CORE_AXI_INBOUND_BASE      0xc00800
> +#define PCIE_CLIENT_BASIC_STATUS0       0x44
> +#define PCIE_CLIENT_BASIC_STATUS1       0x48
> +#define PCIE_CLIENT_INT_MASK            0x4c
> +#define PCIE_CLIENT_INT_STATUS          0x50
> +#define PCIE_RC_CONFIG_RID_CCR          0x8
> +#define PCIE_RC_CONFIG_LCS              0xd0
> +#define PCIE_RC_BAR_CONF                0x300
> +#define PCIE_CORE_OB_REGION_ADDR1       0x4
> +#define PCIE_CORE_OB_REGION_DESC0       0x8
> +#define PCIE_CORE_OB_REGION_DESC1       0xc
> +#define PCIE_RP_IB_ADDR_TRANS           0x4
> +#define PCIE_CORE_INT_MASK              0x900210
> +#define PCIE_CORE_INT_STATUS            0x90020c
> +
> +/* Size of one AXI Region (not Region 0) */
> +#define AXI_REGION_SIZE                 BIT(20)
> +/* Size of Region 0, equal to sum of sizes of other regions */
> +#define AXI_REGION_0_SIZE              (32 * (0x1 << 20))
> +#define OB_REG_SIZE_SHIFT               5
> +#define IB_ROOT_PORT_REG_SIZE_SHIFT     3
> +#define AXI_WRAPPER_IO_WRITE            0x6
> +#define AXI_WRAPPER_MEM_WRITE           0x2
> +#define MAX_AXI_IB_ROOTPORT_REGION_NUM  3
> +#define MIN_AXI_ADDR_BITS_PASSED        8
> +#define ROCKCHIP_VENDOR_ID              0x1d87
> +#define PCIE_ECAM_BUS(x)		(((x) & 0xFF) << 20)

Use lowercase consistently for hex constants (or uppercase, but you
started with lowercase above).

> +#define PCIE_ECAM_DEV(x)		(((x) & 0x1F) << 15)
> +#define PCIE_ECAM_FUNC(x)		(((x) & 0x7) << 12)
> +#define PCIE_ECAM_REG(x)		(((x) & 0xFFF) << 0)
> +#define PCIE_ECAM_ADDR(bus, dev, func, reg) \
> +	  (PCIE_ECAM_BUS(bus) | PCIE_ECAM_DEV(dev) | \
> +	   PCIE_ECAM_FUNC(func) | PCIE_ECAM_REG(reg))
> +
> +/*
> +  * The higher 16-bit of this register is used for write protection
> +  * only if BIT(x + 16) set to 1 the BIT(x) can be written.
> +  */
> +#define HIWORD_UPDATE(val, mask, shift) \
> +	((val) << (shift) | (mask) << ((shift) + 16))
> +
> +#define RC_REGION_0_ADDR_TRANS_H        0x00000000
> +#define RC_REGION_0_ADDR_TRANS_L        0x00000000
> +#define RC_REGION_0_PASS_BITS           (25 - 1)
> +#define RC_REGION_1_ADDR_TRANS_H        0x00000000
> +#define RC_REGION_1_ADDR_TRANS_L        0x00400000
> +#define RC_REGION_1_PASS_BITS           (20 - 1)
> +#define MAX_AXI_WRAPPER_REGION_NUM      33
> +#define PCIE_CORE_LCSR_RETRAIN_LINK     BIT(5)
> +#define PCIE_CLIENT_CONF_ENABLE         1
> +#define PCIE_CLIENT_CONF_ENABLE_SHIFT   0
> +#define PCIE_CLIENT_CONF_ENABLE_MASK    0x1
> +#define PCIE_CLIENT_LINK_TRAIN_ENABLE   1
> +#define PCIE_CLIENT_LINK_TRAIN_SHIFT    1
> +#define PCIE_CLIENT_LINK_TRAIN_MASK     0x1
> +#define PCIE_CLIENT_ARI_ENABLE          1
> +#define PCIE_CLIENT_ARI_ENABLE_SHIFT    3
> +#define PCIE_CLIENT_ARI_ENABLE_MASK     0x1
> +#define PCIE_CLIENT_CONF_LANE_NUM(x)    (x / 2)
> +#define PCIE_CLIENT_CONF_LANE_NUM_SHIFT 4
> +#define PCIE_CLIENT_CONF_LANE_NUM_MASK  0x3
> +#define PCIE_CLIENT_MODE_RC             1
> +#define PCIE_CLIENT_MODE_SHIFT          6
> +#define PCIE_CLIENT_MODE_MASK           0x1
> +#define PCIE_CLIENT_GEN_SEL_2           1
> +#define PCIE_CLIENT_GEN_SEL_1           0
> +#define PCIE_CLIENT_GEN_SEL_SHIFT       7
> +#define PCIE_CLIENT_GEN_SEL_MASK        0x1
> +#define PCIE_CLIENT_LINK_STATUS_UP      0x3
> +#define PCIE_CLIENT_LINK_STATUS_SHIFT   20
> +#define PCIE_CLIENT_LINK_STATUS_MASK    0x3
> +#define PCIE_CORE_PL_CONF_SPEED_2_5G     0x0
> +#define PCIE_CORE_PL_CONF_SPEED_5G     0x1
> +#define PCIE_CORE_PL_CONF_SPEED_8G     0x2
> +#define PCIE_CORE_PL_CONF_SPEED_SHIFT   3
> +#define PCIE_CORE_PL_CONF_SPEED_MASK    0x3
> +#define PCIE_CORE_PL_CONF_LANE_SHIFT    1
> +#define PCIE_CORE_PL_CONF_LANE_MASK     0x3
> +#define PCIE_CORE_RC_CONF_SCC_SHIFT     16
> +
> +/* PCIE_CLIENT_INT_STATUS */
> +#define PCIE_CLIENT_INT_LEGACY_DONE     BIT(15)
> +#define PCIE_CLIENT_INT_MSG             BIT(14)
> +#define PCIE_CLIENT_INT_HOT_RST         BIT(13)
> +#define PCIE_CLIENT_INT_DPA             BIT(12)
> +#define PCIE_CLIENT_INT_FATAL_ERR       BIT(11)
> +#define PCIE_CLIENT_INT_NFATAL_ERR      BIT(10)
> +#define PCIE_CLIENT_INT_CORR_ERR        BIT(9)
> +#define PCIE_CLIENT_INT_INTD            BIT(8)
> +#define PCIE_CLIENT_INT_INTC            BIT(7)
> +#define PCIE_CLIENT_INT_INTB            BIT(6)
> +#define PCIE_CLIENT_INT_INTA            BIT(5)
> +#define PCIE_CLIENT_INT_LOCAL           BIT(4)
> +#define PCIE_CLIENT_INT_UDMA            BIT(3)
> +#define PCIE_CLIENT_INT_PHY             BIT(2)
> +#define PCIE_CLIENT_INT_HOT_PLUG        BIT(1)
> +#define PCIE_CLIENT_INT_PWR_STCG        BIT(0)
> +#define PCIE_CORE_INT_PRFPE             BIT(0)
> +#define PCIE_CORE_INT_CRFPE             BIT(1)
> +#define PCIE_CORE_INT_RRPE              BIT(2)
> +#define PCIE_CORE_INT_PRFO              BIT(3)
> +#define PCIE_CORE_INT_CRFO              BIT(4)
> +#define PCIE_CORE_INT_RT                BIT(5)
> +#define PCIE_CORE_INT_RTR               BIT(6)
> +#define PCIE_CORE_INT_PE                BIT(7)
> +#define PCIE_CORE_INT_MTR               BIT(8)
> +#define PCIE_CORE_INT_UCR               BIT(9)
> +#define PCIE_CORE_INT_FCE               BIT(10)
> +#define PCIE_CORE_INT_CT                BIT(11)
> +#define PCIE_CORE_INT_UTC               BIT(18)
> +#define PCIE_CORE_INT_MMVC              BIT(19)

It's conventional to put the register bits immediately below the
register offset itself, e.g.,

  #define PCIE_CLIENT_INT_STATUS          0x50
  #define  PCIE_CLIENT_INT_LEGACY_DONE     BIT(15)
  #define  PCIE_CLIENT_INT_MSG             BIT(14)
  ...

> +#define ROCKCHIP_PCIE_RPIFR1_INTR_MASK  GENMASK(8, 5)
> +#define ROCKCHIP_PCIE_RPIFR1_INTR_SHIFT 5
> +
> +#define PCIE_CORE_INT \
> +		(PCIE_CORE_INT_PRFPE | PCIE_CORE_INT_CRFPE | \
> +		 PCIE_CORE_INT_RRPE | PCIE_CORE_INT_CRFO | \
> +		 PCIE_CORE_INT_RT | PCIE_CORE_INT_RTR | \
> +		 PCIE_CORE_INT_PE | PCIE_CORE_INT_MTR | \
> +		 PCIE_CORE_INT_UCR | PCIE_CORE_INT_FCE | \
> +		 PCIE_CORE_INT_CT | PCIE_CORE_INT_UTC | \
> +		 PCIE_CORE_INT_MMVC)
> +
> +#define PCIE_CLIENT_INT_SUBSYSTEM \
> +	(PCIE_CLIENT_INT_PWR_STCG | PCIE_CLIENT_INT_HOT_PLUG | \
> +	PCIE_CLIENT_INT_PHY | PCIE_CLIENT_INT_UDMA | \
> +	PCIE_CLIENT_INT_LOCAL)
> +
> +#define PCIE_CLIENT_INT_LEGACY \
> +	(PCIE_CLIENT_INT_INTA | PCIE_CLIENT_INT_INTB | \
> +	PCIE_CLIENT_INT_INTC | PCIE_CLIENT_INT_INTD)
> +
> +#define PCIE_CLIENT_INT_CLI \
> +	(PCIE_CLIENT_INT_CORR_ERR | PCIE_CLIENT_INT_NFATAL_ERR | \
> +	PCIE_CLIENT_INT_FATAL_ERR | PCIE_CLIENT_INT_DPA | \
> +	PCIE_CLIENT_INT_HOT_RST | PCIE_CLIENT_INT_MSG | \
> +	PCIE_CLIENT_INT_LEGACY_DONE | PCIE_CLIENT_INT_LEGACY)
> +
> +struct rockchip_pcie_port {
> +	void	__iomem *reg_base;
> +	void	__iomem *apb_base;
> +	struct	phy *phy;
> +	struct	reset_control *core_rst;
> +	struct	reset_control *mgmt_rst;
> +	struct	reset_control *mgmt_sticky_rst;
> +	struct	reset_control *pipe_rst;
> +	struct	clk *aclk_pcie;
> +	struct	clk *aclk_perf_pcie;
> +	struct	clk *hclk_pcie;
> +	struct	clk *clk_pcie_pm;
> +	struct	regulator *vpcie3v3; /* 3.3V power supply */
> +	struct	regulator *vpcie1v8; /* 1.8V power supply */
> +	struct	regulator *vpcie0v9; /* 0.9V power supply */
> +	struct	gpio_desc *ep_gpio;
> +	u32	lanes;
> +	u8	root_bus_nr;
> +	struct	device *dev;
> +	struct	irq_domain *irq_domain;
> +};
> +
> +static irqreturn_t rockchip_pcie_subsys_irq_handler(int irq, void *arg);
> +static irqreturn_t rockchip_pcie_client_irq_handler(int irq, void *arg);
> +static void rockchip_pcie_legacy_int_handler(struct irq_desc *desc);

If you reorder the code slightly, you can probably get rid of these
forward declarations.

> +static inline u32 pcie_read(struct rockchip_pcie_port *port, u32 reg)
> +{
> +	return readl(port->apb_base + reg);
> +}
> +
> +static inline void pcie_write(struct rockchip_pcie_port *port,
> +			      u32 val, u32 reg)
> +{
> +	writel(val, port->apb_base + reg);
> +}
> +
> +static int rockchip_pcie_valid_config(struct rockchip_pcie_port *pp,
> +				      struct pci_bus *bus, int dev)
> +{
> +	/* access only one slot on each root port */
> +	if (bus->number == pp->root_bus_nr && dev > 0)
> +		return 0;
> +
> +	/*
> +	 * do not read more than one device on the bus directly attached
> +	 * to RC's (Virtual Bridge's) DS side.

Does "DS" mean "downstream"?  Spell it out, please.  Unless "Virtual
Bridge" means something specific to Rockchip, you can omit that.  My guess
is that this is just the same as every other Root Complex and we don't need
a "Virtual Bridge" reminder.

> +	 */
> +	if (bus->primary == pp->root_bus_nr && dev > 0)
> +		return 0;
> +
> +	return 1;
> +}
> +
> +static int rockchip_pcie_rd_own_conf(struct rockchip_pcie_port *pp,
> +				     int where, int size,
> +				     u32 *val)
> +{
> +	void __iomem *addr = pp->apb_base + PCIE_RC_CONFIG_BASE + where;
> +
> +	if (!IS_ALIGNED((uintptr_t)addr, size)) {
> +		*val = 0;
> +		return PCIBIOS_BAD_REGISTER_NUMBER;
> +	}
> +
> +	if (size == 4) {
> +		*val = readl(addr);
> +	} else if (size == 2) {
> +		*val = readw(addr);
> +	} else if (size == 1) {
> +		*val = readb(addr);
> +	} else {
> +		*val = 0;
> +		return PCIBIOS_BAD_REGISTER_NUMBER;
> +	}
> +	return PCIBIOS_SUCCESSFUL;
> +}
> +
> +static int rockchip_pcie_wr_own_conf(struct rockchip_pcie_port *pp,
> +				     int where, int size, u32 val)
> +{
> +	u32 mask, tmp, offset;
> +
> +	offset = (where & (~0x3));
> +
> +	if (size == 4) {
> +		writel(val, pp->apb_base + PCIE_RC_CONFIG_BASE + offset);
> +		return PCIBIOS_SUCCESSFUL;
> +	}
> +
> +	mask = ~(((1 << (size * 8)) - 1) << ((where & 0x3) * 8));
> +
> +	tmp = readl(pp->apb_base + PCIE_RC_CONFIG_BASE + offset) & mask;
> +	tmp |= val << ((where & 0x3) * 8);
> +	writel(tmp, pp->apb_base + PCIE_RC_CONFIG_BASE + offset);

This read/modify/write is not safe in general for config space because
it may inadvertently clear RW1C bits.

If your hardware actually does support 1, 2, and 4-byte accesses,
please use those instead.  Or, if this is used for only built-in
hardware and you know where every RW1C bit is, you can do something
like thunder_pem_bridge_write() does to handle them correctly.

If the hardware doesn't support small writes, and you have to support
arbitrary devices, this is a hardware quality of implementation issue
that may cause hard-to-reproduce problems, so please add a dev_warn in
the probe function similar to that in pcie-hisi.c.

> +	return PCIBIOS_SUCCESSFUL;
> +}
> +
> +static int rockchip_pcie_rd_other_conf(struct rockchip_pcie_port *pp,
> +				       struct pci_bus *bus, u32 devfn,
> +				       int where, int size, u32 *val)
> +{
> +	u32 busdev;
> +
> +	busdev = PCIE_ECAM_ADDR(bus->number, PCI_SLOT(devfn),
> +				PCI_FUNC(devfn), where);
> +
> +	if (!IS_ALIGNED(busdev, size)) {
> +		*val = 0;
> +		return PCIBIOS_BAD_REGISTER_NUMBER;
> +	}
> +
> +	if (size == 4) {
> +		*val = readl(pp->reg_base + busdev);
> +	} else if (size == 2) {
> +		*val = readw(pp->reg_base + busdev);
> +	} else if (size == 1) {
> +		*val = readb(pp->reg_base + busdev);
> +	} else {
> +		*val = 0;
> +		return PCIBIOS_BAD_REGISTER_NUMBER;
> +	}
> +	return PCIBIOS_SUCCESSFUL;
> +}
> +
> +static int rockchip_pcie_wr_other_conf(struct rockchip_pcie_port *pp,
> +				       struct pci_bus *bus, u32 devfn,
> +				       int where, int size, u32 val)
> +{
> +	u32 busdev;
> +
> +	busdev = PCIE_ECAM_ADDR(bus->number, PCI_SLOT(devfn),
> +				PCI_FUNC(devfn), where);
> +	if (!IS_ALIGNED(busdev, size))
> +		return PCIBIOS_BAD_REGISTER_NUMBER;
> +
> +	if (size == 4)
> +		writel(val, pp->reg_base + busdev);
> +	else if (size == 2)
> +		writew(val, pp->reg_base + busdev);
> +	else if (size == 1)
> +		writeb(val, pp->reg_base + busdev);
> +	else
> +		return PCIBIOS_BAD_REGISTER_NUMBER;
> +
> +	return PCIBIOS_SUCCESSFUL;
> +}
> +
> +static int rockchip_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
> +				 int size, u32 *val)
> +{
> +	struct rockchip_pcie_port *pp = bus->sysdata;
> +	int ret;
> +
> +	if (rockchip_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) {
> +		*val = 0xffffffff;
> +		return PCIBIOS_DEVICE_NOT_FOUND;
> +	}
> +
> +	if (bus->number != pp->root_bus_nr)
> +		ret = rockchip_pcie_rd_other_conf(pp, bus, devfn,
> +						  where, size, val);
> +	else
> +		ret = rockchip_pcie_rd_own_conf(pp, where, size, val);

Reorganize these so the test is positive and no temporary "ret" is
needed, i.e.,

  if (bus->number == pp->root_bus_nr)
    return rockchip_pcie_rd_own_conf(pp, where, size, val);

  return rockchip_pcie_rd_other_conf(pp, bus, devfn, ...)

> +
> +	return ret;
> +}
> +
> +static int rockchip_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
> +				 int where, int size, u32 val)
> +{
> +	struct rockchip_pcie_port *pp = bus->sysdata;
> +	int ret;
> +
> +	if (rockchip_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0)
> +		return PCIBIOS_DEVICE_NOT_FOUND;
> +
> +	if (bus->number != pp->root_bus_nr)
> +		ret = rockchip_pcie_wr_other_conf(pp, bus, devfn,
> +						  where, size, val);
> +	else
> +		ret = rockchip_pcie_wr_own_conf(pp, where, size, val);
> +
> +	return ret;
> +}
> +
> +static struct pci_ops rockchip_pcie_ops = {
> +	.read = rockchip_pcie_rd_conf,
> +	.write = rockchip_pcie_wr_conf,
> +};
> +
> +/**
> + * rockchip_pcie_init_port - Initialize hardware
> + * @port: PCIe port information
> + */
> +static int rockchip_pcie_init_port(struct rockchip_pcie_port *port)
> +{
> +	int err;
> +	u32 status;
> +	unsigned long timeout;
> +
> +	gpiod_set_value(port->ep_gpio, 0);
> +
> +	err = phy_init(port->phy);
> +	if (err < 0) {
> +		dev_err(port->dev, "fail to init phy, err %d\n", err);
> +		return err;
> +	}
> +
> +	err = reset_control_assert(port->core_rst);
> +	if (err) {
> +		dev_err(port->dev, "assert core_rst err %d\n", err);
> +		return err;
> +	}
> +
> +	err = reset_control_assert(port->mgmt_rst);
> +	if (err) {
> +		dev_err(port->dev, "assert mgmt_rst err %d\n", err);
> +		return err;
> +	}
> +
> +	err = reset_control_assert(port->mgmt_sticky_rst);
> +	if (err) {
> +		dev_err(port->dev, "assert mgmt_sticky_rst err %d\n", err);
> +		return err;
> +	}
> +
> +	err = reset_control_assert(port->pipe_rst);
> +	if (err) {
> +		dev_err(port->dev, "assert pipe_rst err %d\n", err);
> +		return err;
> +	}
> +
> +	pcie_write(port,
> +		   HIWORD_UPDATE(PCIE_CLIENT_CONF_ENABLE,
> +				 PCIE_CLIENT_CONF_ENABLE_MASK,
> +				 PCIE_CLIENT_CONF_ENABLE_SHIFT) |
> +		   HIWORD_UPDATE(PCIE_CLIENT_CONF_LANE_NUM(port->lanes),
> +				 PCIE_CLIENT_CONF_LANE_NUM_MASK,
> +				 PCIE_CLIENT_CONF_LANE_NUM_SHIFT) |
> +		   HIWORD_UPDATE(PCIE_CLIENT_MODE_RC,
> +				 PCIE_CLIENT_MODE_MASK,
> +				 PCIE_CLIENT_MODE_SHIFT) |
> +		   HIWORD_UPDATE(PCIE_CLIENT_ARI_ENABLE,
> +				 PCIE_CLIENT_ARI_ENABLE_MASK,
> +				 PCIE_CLIENT_ARI_ENABLE_SHIFT) |
> +		   HIWORD_UPDATE(PCIE_CLIENT_GEN_SEL_2,
> +				 PCIE_CLIENT_GEN_SEL_MASK,
> +				 PCIE_CLIENT_GEN_SEL_SHIFT),
> +		   PCIE_CLIENT_BASE);
> +
> +	err = phy_power_on(port->phy);
> +	if (err) {
> +		dev_err(port->dev, "fail to power on phy, err %d\n", err);
> +		return err;
> +	}
> +
> +	err = reset_control_deassert(port->core_rst);
> +	if (err) {
> +		dev_err(port->dev, "deassert core_rst err %d\n", err);
> +		return err;
> +	}
> +
> +	err = reset_control_deassert(port->mgmt_rst);
> +	if (err) {
> +		dev_err(port->dev, "deassert mgmt_rst err %d\n", err);
> +		return err;
> +	}
> +
> +	err = reset_control_deassert(port->mgmt_sticky_rst);
> +	if (err) {
> +		dev_err(port->dev, "deassert mgmt_sticky_rst err %d\n", err);
> +		return err;
> +	}
> +
> +	err = reset_control_deassert(port->pipe_rst);
> +	if (err) {
> +		dev_err(port->dev, "deassert pipe_rst err %d\n", err);
> +		return err;
> +	}
> +
> +	/* Enable Gen1 training */
> +	pcie_write(port,
> +		   HIWORD_UPDATE(PCIE_CLIENT_LINK_TRAIN_ENABLE,
> +				 PCIE_CLIENT_LINK_TRAIN_MASK,
> +				 PCIE_CLIENT_LINK_TRAIN_SHIFT),
> +		   PCIE_CLIENT_BASE);
> +
> +	gpiod_set_value(port->ep_gpio, 1);
> +
> +	/* 500ms timeout value should be enough for Gen1/2 training */
> +	timeout = jiffies + msecs_to_jiffies(500);
> +
> +	for (;;) {
> +		status = pcie_read(port, PCIE_CLIENT_BASIC_STATUS1);
> +		if (((status >> PCIE_CLIENT_LINK_STATUS_SHIFT) &
> +		      PCIE_CLIENT_LINK_STATUS_MASK) ==
> +		      PCIE_CLIENT_LINK_STATUS_UP) {
> +			dev_dbg(port->dev, "pcie link training gen1 pass!\n");

Capitalize as "PCIe" consistently in comments and console messages.
Similarly for "IRQ" and "ATU".

> +			break;
> +		}
> +
> +		msleep(20);
> +
> +		if (!time_before(jiffies, timeout)) {
> +			err = -ETIMEDOUT;
> +			break;
> +		}
> +
> +	}
> +
> +	/* Double check gen1 training */
> +	if (err) {
> +		status = pcie_read(port, PCIE_CLIENT_BASIC_STATUS1);
> +		err = (((status >> PCIE_CLIENT_LINK_STATUS_SHIFT) &
> +			PCIE_CLIENT_LINK_STATUS_MASK) ==
> +			PCIE_CLIENT_LINK_STATUS_UP) ? 0 : -ETIMEDOUT;
> +		if (err) {
> +			dev_err(port->dev, "pcie link training gen1 timeout!\n");
> +			return err;
> +		}
> +	}
> +
> +	/*
> +	 * Enable retrain for gen2. This should be configured only after
> +	 * gen1 finished.
> +	 */
> +	status = pcie_read(port,
> +			   PCIE_RC_CONFIG_LCS + PCIE_RC_CONFIG_BASE);
> +	status |= PCIE_CORE_LCSR_RETRAIN_LINK;
> +	pcie_write(port, status,
> +		   PCIE_RC_CONFIG_LCS + PCIE_RC_CONFIG_BASE);
> +
> +	timeout = jiffies + msecs_to_jiffies(500);
> +	for (;;) {
> +		status = pcie_read(port, PCIE_CORE_CTRL_MGMT_BASE);
> +		if (((status >> PCIE_CORE_PL_CONF_SPEED_SHIFT) &
> +		     PCIE_CORE_PL_CONF_SPEED_MASK) ==
> +		     PCIE_CORE_PL_CONF_SPEED_5G) {
> +			dev_dbg(port->dev, "pcie link training gen2 pass!\n");
> +			break;
> +		}
> +
> +		msleep(20);
> +
> +		if (!time_before(jiffies, timeout)) {
> +			err = -ETIMEDOUT;
> +			break;
> +		}
> +	}
> +
> +	/* Double check gen2 training */
> +	if (err) {
> +		status = pcie_read(port, PCIE_CORE_CTRL_MGMT_BASE);
> +		err = (((status >> PCIE_CORE_PL_CONF_SPEED_SHIFT) &
> +			PCIE_CORE_PL_CONF_SPEED_MASK) ==
> +			PCIE_CORE_PL_CONF_SPEED_5G) ? 0 : -ETIMEDOUT;
> +		if (err)
> +			dev_dbg(port->dev, "pcie link training gen2 timeout, fall back to gen1!\n");
> +	}
> +
> +	/* Check the final link with from negotiated lane counter from MGMT */

s/with/width/

> +	status = pcie_read(port, PCIE_CORE_CTRL_MGMT_BASE);
> +	status =  0x1 << ((status >> PCIE_CORE_PL_CONF_LANE_SHIFT) &
> +			   PCIE_CORE_PL_CONF_LANE_MASK);
> +	dev_dbg(port->dev, "current link width is x%d\n", status);
> +
> +	pcie_write(port, ROCKCHIP_VENDOR_ID, PCIE_RC_CONFIG_BASE);
> +	pcie_write(port, PCI_CLASS_BRIDGE_PCI << PCIE_CORE_RC_CONF_SCC_SHIFT,
> +		   PCIE_RC_CONFIG_BASE + PCIE_RC_CONFIG_RID_CCR);
> +	pcie_write(port, 0x0, PCIE_CORE_CTRL_MGMT_BASE + PCIE_RC_BAR_CONF);
> +
> +	pcie_write(port, (RC_REGION_0_ADDR_TRANS_L + RC_REGION_0_PASS_BITS),
> +		   PCIE_CORE_AXI_CONF_BASE);
> +	pcie_write(port, RC_REGION_0_ADDR_TRANS_H,
> +		   PCIE_CORE_AXI_CONF_BASE + PCIE_CORE_OB_REGION_ADDR1);
> +	pcie_write(port, 0x0080000a,
> +		   PCIE_CORE_AXI_CONF_BASE + PCIE_CORE_OB_REGION_DESC0);
> +	pcie_write(port, 0x0,
> +		   PCIE_CORE_AXI_CONF_BASE + PCIE_CORE_OB_REGION_DESC1);
> +
> +	return 0;
> +}
> +
> +/**
> + * rockchip_pcie_parse_dt - Parse Device tree
> + * @port: PCIe port information
> + *
> + * Return: '0' on success and error value on failure
> + */
> +static int rockchip_pcie_parse_dt(struct rockchip_pcie_port *port)
> +{
> +	struct device *dev = port->dev;
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct device_node *node = dev->of_node;
> +	struct resource *regs;
> +	int irq;
> +	int err = -ENODEV;
> +
> +	regs = platform_get_resource_byname(pdev,
> +					    IORESOURCE_MEM,
> +					    "axi-base");
> +	if (!regs) {
> +		dev_err(dev, "missing axi-base property\n");
> +		return err;

"return -ENODEV" so we don't have to look up to see what "err" was
initialized to.  Then you can remove the init.

> +	}
> +
> +	port->reg_base = devm_ioremap_resource(dev, regs);
> +	if (IS_ERR(port->reg_base))
> +		return PTR_ERR(port->reg_base);
> +
> +	regs = platform_get_resource_byname(pdev,
> +					    IORESOURCE_MEM,
> +					    "apb-base");
> +	if (!regs) {
> +		dev_err(dev, "missing apb-base property\n");
> +		return err;

Ditto.

> +	}
> +
> +	port->apb_base = devm_ioremap_resource(dev, regs);
> +	if (IS_ERR(port->apb_base))
> +		return PTR_ERR(port->apb_base);
> +
> +	port->phy = devm_phy_get(dev, "pcie-phy");
> +	if (IS_ERR(port->phy)) {
> +		if (PTR_ERR(port->phy) != -EPROBE_DEFER)
> +			dev_err(dev, "Missing phy\n");
> +		return PTR_ERR(port->phy);
> +	}
> +
> +	port->lanes = 1;
> +	err = of_property_read_u32(node, "num-lanes", &port->lanes);
> +	if (!err && ((port->lanes == 0) ||
> +		     (port->lanes == 3) ||
> +		     (port->lanes > 4))) {
> +		dev_warn(dev, "invalid num-lanes, default use one lane\n");
> +		port->lanes = 1;
> +	}
> +
> +	port->core_rst = devm_reset_control_get(dev, "core");
> +	if (IS_ERR(port->core_rst)) {
> +		if (PTR_ERR(port->core_rst) != -EPROBE_DEFER)
> +			dev_err(dev, "missing core rst property in node %s\n",
> +				node->name);
> +		return PTR_ERR(port->core_rst);
> +	}
> +
> +	port->mgmt_rst = devm_reset_control_get(dev, "mgmt");
> +	if (IS_ERR(port->mgmt_rst)) {
> +		if (PTR_ERR(port->mgmt_rst) != -EPROBE_DEFER)
> +			dev_err(dev, "missing mgmt rst property in node %s\n",
> +				node->name);
> +		return PTR_ERR(port->mgmt_rst);
> +	}
> +
> +	port->mgmt_sticky_rst = devm_reset_control_get(dev, "mgmt-sticky");
> +	if (IS_ERR(port->mgmt_sticky_rst)) {
> +		if (PTR_ERR(port->mgmt_sticky_rst) != -EPROBE_DEFER)
> +			dev_err(dev, "missing mgmt-sticky rst property in node %s\n",
> +				node->name);
> +		return PTR_ERR(port->mgmt_sticky_rst);
> +	}
> +
> +	port->pipe_rst = devm_reset_control_get(dev, "pipe");
> +	if (IS_ERR(port->pipe_rst)) {
> +		if (PTR_ERR(port->pipe_rst) != -EPROBE_DEFER)
> +			dev_err(dev, "missing pipe rst property in node %s\n",
> +				node->name);
> +		return PTR_ERR(port->pipe_rst);
> +	}
> +
> +	port->ep_gpio = devm_gpiod_get(dev, "ep", GPIOD_OUT_HIGH);
> +	if (IS_ERR(port->ep_gpio)) {
> +		dev_err(dev, "missing ep-gpios property in node %s\n",
> +			node->name);
> +		return PTR_ERR(port->ep_gpio);
> +	}
> +
> +	port->aclk_pcie = devm_clk_get(dev, "aclk");
> +	if (IS_ERR(port->aclk_pcie)) {
> +		dev_err(dev, "aclk clock not found.\n");

Drop trailing periods, so these match the other messages.

> +		return PTR_ERR(port->aclk_pcie);
> +	}
> +
> +	port->aclk_perf_pcie = devm_clk_get(dev, "aclk-perf");
> +	if (IS_ERR(port->aclk_perf_pcie)) {
> +		dev_err(dev, "aclk_perf clock not found.\n");
> +		return PTR_ERR(port->aclk_perf_pcie);
> +	}
> +
> +	port->hclk_pcie = devm_clk_get(dev, "hclk");
> +	if (IS_ERR(port->hclk_pcie)) {
> +		dev_err(dev, "hclk clock not found.\n");
> +		return PTR_ERR(port->hclk_pcie);
> +	}
> +
> +	port->clk_pcie_pm = devm_clk_get(dev, "pm");
> +	if (IS_ERR(port->clk_pcie_pm)) {
> +		dev_err(dev, "pm clock not found.\n");
> +		return PTR_ERR(port->clk_pcie_pm);
> +	}
> +
> +	irq = platform_get_irq_byname(pdev, "sys");
> +	if (irq < 0) {
> +		dev_err(dev, "missing pcie_sys IRQ resource\n");
> +		return -EINVAL;
> +	}
> +
> +	err = devm_request_irq(dev, irq, rockchip_pcie_subsys_irq_handler,
> +			       IRQF_SHARED, "pcie-sys", port);
> +	if (err) {
> +		dev_err(dev, "failed to request pcie subsystem irq\n");
> +		return err;
> +	}
> +
> +	irq = platform_get_irq_byname(pdev, "legacy");
> +	if (irq < 0) {
> +		dev_err(dev, "missing pcie_legacy IRQ resource\n");
> +		return -EINVAL;
> +	}
> +
> +	irq_set_chained_handler_and_data(irq,
> +					 rockchip_pcie_legacy_int_handler,
> +					 port);
> +
> +	irq = platform_get_irq_byname(pdev, "client");
> +	if (irq < 0) {
> +		dev_err(dev, "missing pcie-client IRQ resource\n");
> +		return -EINVAL;
> +	}
> +
> +	err = devm_request_irq(dev, irq, rockchip_pcie_client_irq_handler,
> +			       IRQF_SHARED, "pcie-client", port);
> +	if (err) {
> +		dev_err(dev, "failed to request pcie client irq\n");
> +		return err;
> +	}
> +
> +	port->vpcie3v3 = devm_regulator_get_optional(dev, "vpcie3v3");
> +	if (IS_ERR(port->vpcie3v3)) {
> +		if (PTR_ERR(port->vpcie3v3) == -EPROBE_DEFER)
> +			return -EPROBE_DEFER;
> +		dev_info(dev, "No vpcie3v3 regulator found.\n");

Lower-case "No" and drop period to match other messages.

> +	}
> +
> +	port->vpcie1v8 = devm_regulator_get_optional(dev, "vpcie1v8");
> +	if (IS_ERR(port->vpcie1v8)) {
> +		if (PTR_ERR(port->vpcie1v8) == -EPROBE_DEFER)
> +			return -EPROBE_DEFER;
> +		dev_info(dev, "No vpcie1v8 regulator found.\n");
> +	}
> +
> +	port->vpcie0v9 = devm_regulator_get_optional(dev, "vpcie0v9");
> +	if (IS_ERR(port->vpcie0v9)) {
> +		if (PTR_ERR(port->vpcie0v9) == -EPROBE_DEFER)
> +			return -EPROBE_DEFER;
> +		dev_info(dev, "No vpcie0v9 regulator found.\n");
> +	}
> +
> +	return 0;
> +}
> +
> +static int rockchip_pcie_set_vpcie(struct rockchip_pcie_port *port)
> +{
> +	int err;
> +
> +	if (!IS_ERR(port->vpcie3v3)) {
> +		err = regulator_enable(port->vpcie3v3);
> +		if (err) {
> +			dev_err(port->dev, "Fail to enable vpcie3v3 regulator.\n");
> +			goto err_out;
> +		}
> +	}
> +
> +	if (!IS_ERR(port->vpcie1v8)) {
> +		err = regulator_enable(port->vpcie1v8);
> +		if (err) {
> +			dev_err(port->dev, "Fail to enable vpcie1v8 regulator.\n");
> +			goto err_disable_3v3;
> +		}
> +	}
> +
> +	if (!IS_ERR(port->vpcie0v9)) {
> +		err = regulator_enable(port->vpcie0v9);
> +		if (err) {
> +			dev_err(port->dev, "Fail to enable vpcie0v9 regulator.\n");
> +			goto err_disable_1v8;
> +		}
> +	}
> +
> +	return 0;
> +
> +err_disable_1v8:
> +	if (!IS_ERR(port->vpcie1v8))
> +		regulator_disable(port->vpcie1v8);
> +err_disable_3v3:
> +	if (!IS_ERR(port->vpcie3v3))
> +		regulator_disable(port->vpcie3v3);
> +err_out:
> +	return err;
> +}
> +
> +static void rockchip_pcie_enable_interrupts(struct rockchip_pcie_port *port)
> +{
> +	pcie_write(port, (PCIE_CLIENT_INT_CLI << 16) &
> +		   (~PCIE_CLIENT_INT_CLI), PCIE_CLIENT_INT_MASK);
> +	pcie_write(port, PCIE_CORE_INT, PCIE_CORE_INT_MASK);
> +}
> +
> +static int rockchip_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
> +				  irq_hw_number_t hwirq)
> +{
> +	irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq);
> +	irq_set_chip_data(irq, domain->host_data);
> +
> +	return 0;
> +}
> +
> +static const struct irq_domain_ops intx_domain_ops = {
> +	.map = rockchip_pcie_intx_map,
> +};
> +
> +static int rockchip_pcie_init_irq_domain(struct rockchip_pcie_port *pp)
> +{
> +	struct device *dev = pp->dev;
> +	struct device_node *intc = of_get_next_child(dev->of_node, NULL);
> +
> +	if (!intc) {
> +		dev_err(dev, "Missing child interrupt-controller node\n");
> +		return -EINVAL;
> +	}
> +
> +	pp->irq_domain = irq_domain_add_linear(intc, 4, &intx_domain_ops, pp);
> +	if (!pp->irq_domain) {
> +		dev_err(dev, "Failed to get a INTx IRQ domain\n");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static irqreturn_t rockchip_pcie_subsys_irq_handler(int irq, void *arg)
> +{
> +	struct rockchip_pcie_port *pp = arg;
> +	u32 reg;
> +	u32 sub_reg;
> +
> +	reg = pcie_read(pp, PCIE_CLIENT_INT_STATUS);
> +	if (reg & PCIE_CLIENT_INT_LOCAL) {
> +		dev_dbg(pp->dev, "local interrupt recived\n");
> +		sub_reg = pcie_read(pp, PCIE_CORE_INT_STATUS);
> +		if (sub_reg & PCIE_CORE_INT_PRFPE)
> +			dev_dbg(pp->dev, "Parity error detected while reading from the PNP Receive FIFO RAM\n");

Lower-case these to match other messages (except for acronyms like
MSI).

> +
> +		if (sub_reg & PCIE_CORE_INT_CRFPE)
> +			dev_dbg(pp->dev, "Parity error detected while reading from the Completion Receive FIFO RAM\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_RRPE)
> +			dev_dbg(pp->dev, "Parity error detected while reading from Replay Buffer RAM\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_PRFO)
> +			dev_dbg(pp->dev, "Overflow occurred in the PNP Receive FIFO\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_CRFO)
> +			dev_dbg(pp->dev, "Overflow occurred in the Completion Receive FIFO\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_RT)
> +			dev_dbg(pp->dev, "Replay timer timed out\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_RTR)
> +			dev_dbg(pp->dev, "Replay timer rolled over after 4 transmissions of the same TLP\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_PE)
> +			dev_dbg(pp->dev, "Phy error detected on receive side\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_MTR)
> +			dev_dbg(pp->dev, "Malformed TLP received from the link\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_UCR)
> +			dev_dbg(pp->dev, "Malformed TLP received from the link\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_FCE)
> +			dev_dbg(pp->dev, "An error was observed in the flow control advertisements from the other side\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_CT)
> +			dev_dbg(pp->dev, "A request timed out waiting for completion\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_UTC)
> +			dev_dbg(pp->dev, "Unmapped TC error\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_MMVC)
> +			dev_dbg(pp->dev, "MSI mask register changes\n");
> +
> +		pcie_write(pp, sub_reg, PCIE_CORE_INT_STATUS);
> +	}
> +
> +	pcie_write(pp, reg & PCIE_CLIENT_INT_LOCAL, PCIE_CLIENT_INT_STATUS);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t rockchip_pcie_client_irq_handler(int irq, void *arg)
> +{
> +	struct rockchip_pcie_port *pp = arg;
> +	u32 reg;
> +
> +	reg = pcie_read(pp, PCIE_CLIENT_INT_STATUS);
> +	if (reg & PCIE_CLIENT_INT_LEGACY_DONE)
> +		dev_dbg(pp->dev, "legacy done interrupt recived\n");

s/recived/received/ here and below.

> +
> +	if (reg & PCIE_CLIENT_INT_MSG)
> +		dev_dbg(pp->dev, "message done interrupt recived\n");
> +
> +	if (reg & PCIE_CLIENT_INT_HOT_RST)
> +		dev_dbg(pp->dev, "hot reset interrupt recived\n");
> +
> +	if (reg & PCIE_CLIENT_INT_DPA)
> +		dev_dbg(pp->dev, "dpa interrupt recived\n");
> +
> +	if (reg & PCIE_CLIENT_INT_FATAL_ERR)
> +		dev_dbg(pp->dev, "fatal error interrupt recived\n");
> +
> +	if (reg & PCIE_CLIENT_INT_NFATAL_ERR)
> +		dev_dbg(pp->dev, "no fatal error interrupt recived\n");
> +
> +	if (reg & PCIE_CLIENT_INT_CORR_ERR)
> +		dev_dbg(pp->dev, "correctable error interrupt recived\n");
> +
> +	pcie_write(pp, reg & (PCIE_CLIENT_INT_LEGACY_DONE |
> +			      PCIE_CLIENT_INT_MSG | PCIE_CLIENT_INT_HOT_RST |
> +			      PCIE_CLIENT_INT_DPA | PCIE_CLIENT_INT_FATAL_ERR |
> +			      PCIE_CLIENT_INT_NFATAL_ERR |
> +			      PCIE_CLIENT_INT_CORR_ERR),
> +			      PCIE_CLIENT_INT_STATUS);
> +	return IRQ_HANDLED;
> +}
> +
> +static void rockchip_pcie_legacy_int_handler(struct irq_desc *desc)
> +{
> +	struct irq_chip *chip = irq_desc_get_chip(desc);
> +	struct rockchip_pcie_port *port;
> +	u32 reg;
> +	u32 hwirq;
> +	u32 virq;
> +
> +	chained_irq_enter(chip, desc);
> +	port = irq_desc_get_handler_data(desc);
> +
> +	reg = pcie_read(port, PCIE_CLIENT_INT_STATUS);
> +	reg = (reg & ROCKCHIP_PCIE_RPIFR1_INTR_MASK) >>
> +	       ROCKCHIP_PCIE_RPIFR1_INTR_SHIFT;
> +
> +	while (reg) {
> +		hwirq = ffs(reg) - 1;
> +		reg &= ~BIT(hwirq);
> +
> +		virq = irq_find_mapping(port->irq_domain, hwirq);
> +		if (virq)
> +			generic_handle_irq(virq);
> +		else
> +			dev_err(port->dev, "unexpected IRQ, INT%d\n", hwirq);
> +	}
> +
> +	chained_irq_exit(chip, desc);
> +}
> +
> +static int rockchip_pcie_prog_ob_atu(struct rockchip_pcie_port *pp,
> +				     int region_no,
> +				     int type, u8 num_pass_bits,
> +				     u32 lower_addr, u32 upper_addr)
> +{
> +	u32 ob_addr_0 = 0;
> +	u32 ob_addr_1 = 0;
> +	u32 ob_desc_0 = 0;

The above are initialized below before being used, so they don't need
to be initialized here.

> +	u32 ob_desc_1 = 0;

This is only read below.  I'd just get rid of ob_desc_1 completely and
use a "0" directly in the writel().  I don't think the variable
improves readability.

> +	void __iomem *aw_base;
> +
> +	if (!pp)
> +		return -EINVAL;

I don't think it's possible to get here with "pp == NULL", so drop the
test (also in rockchip_pcie_prog_ib_atu()).

> +	if (region_no >= MAX_AXI_WRAPPER_REGION_NUM)
> +		return -EINVAL;
> +	if ((num_pass_bits + 1) < 8)
> +		return -EINVAL;
> +	if (num_pass_bits > 63)
> +		return -EINVAL;
> +	if (region_no == 0) {
> +		if (AXI_REGION_0_SIZE < (2ULL << num_pass_bits))
> +		return -EINVAL;
> +	}
> +	if (region_no != 0) {
> +		if (AXI_REGION_SIZE < (2ULL << num_pass_bits))
> +		return -EINVAL;
> +	}
> +	aw_base = pp->apb_base + PCIE_CORE_AXI_CONF_BASE;
> +	aw_base += (region_no << OB_REG_SIZE_SHIFT);
> +
> +	ob_addr_0 = (ob_addr_0 &
> +		     ~0x0000003fU) | (num_pass_bits &
> +		     0x0000003fU);
> +	ob_addr_0 = (ob_addr_0 &
> +		     ~0xffffff00U) | (lower_addr & 0xffffff00U);

The masks with ~0x3f and ~0xffffff00 don't really make sense:
ob_addr_0 starts out as zero, so these masks are unnecessary.  You
could do something like the following and drop the initializations at
the declarations.

  ob_addr_0 = num_pass_bits & PCIE_CORE_OB_REGION_ADDR0_NUM_BITS;
  ob_addr_0 |= lower_addr & PCIE_CORE_OB_REGION_ADDR0_LO_ADDR;

> +	ob_addr_1 = upper_addr;
> +	ob_desc_0 = (1 << 23 | type);
> +
> +	writel(ob_addr_0, aw_base);

There should probably be a "#define PCIE_CORE_OB_REGION_ADDR0 0" above.

> +	writel(ob_addr_1, aw_base + PCIE_CORE_OB_REGION_ADDR1);
> +	writel(ob_desc_0, aw_base + PCIE_CORE_OB_REGION_DESC0);
> +	writel(ob_desc_1, aw_base + PCIE_CORE_OB_REGION_DESC1);
> +
> +	return 0;
> +}
> +
> +static int rockchip_pcie_prog_ib_atu(struct rockchip_pcie_port *pp,
> +				     int region_no,
> +				     u8 num_pass_bits,
> +				     u32 lower_addr,
> +				     u32 upper_addr)
> +{
> +	u32 ib_addr_0 = 0;
> +	u32 ib_addr_1 = 0;
> +	void __iomem *aw_base;
> +
> +	if (!pp)
> +		return -EINVAL;
> +	if (region_no > MAX_AXI_IB_ROOTPORT_REGION_NUM)
> +		return -EINVAL;
> +	if ((num_pass_bits + 1) < MIN_AXI_ADDR_BITS_PASSED)
> +		return -EINVAL;
> +	if (num_pass_bits > 63)
> +		return -EINVAL;
> +	aw_base = pp->apb_base + PCIE_CORE_AXI_INBOUND_BASE;
> +	aw_base += (region_no << IB_ROOT_PORT_REG_SIZE_SHIFT);
> +	ib_addr_0 = (ib_addr_0 &
> +		     ~0x0000003fU) | (num_pass_bits &
> +		     0x0000003fU);
> +
> +	ib_addr_0 = (ib_addr_0 & ~0xffffff00U) |
> +		     ((lower_addr << 8) & 0xffffff00U);

Unnecessary masking here, too.  Drop the initializations and the
masking.

> +	ib_addr_1 = upper_addr;
> +
> +	writel(ib_addr_0, aw_base);
> +	writel(ib_addr_1, aw_base + PCIE_RP_IB_ADDR_TRANS);
> +
> +	return 0;
> +}
> +
> +static int rockchip_pcie_probe(struct platform_device *pdev)
> +{
> +	struct rockchip_pcie_port *port;
> +	struct device *dev = &pdev->dev;
> +	struct pci_bus *bus, *child;
> +	struct resource_entry *win;
> +	resource_size_t io_base;
> +	struct resource	*busn = NULL;
> +	struct resource	*mem = NULL;

Unnecessary initialization.

> +	struct resource	*io = NULL;

Unnecessary initialization.

> +	phys_addr_t io_bus_addr = 0;

Unnecessary initialization.

> +	u32 io_size = 0;
> +	phys_addr_t mem_bus_addr = 0;

Unnecessary initialization.

> +	u32 mem_size = 0;
> +	int reg_no = 0;

Unnecessary initialization.

> +	int err = 0;

Unnecessary initialization.

> +	int offset = 0;

Unnecessary initialization.  Initializing them unnecessarily doesn't
*break* anything, obviously, but it is a burden on the code reader,
because we have to remember, "oh, offset was initialized above, which
means there's something special about how it's used below".

> +
> +	LIST_HEAD(res);
> +
> +	if (!dev->of_node)
> +		return -ENODEV;
> +
> +	port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
> +	if (!port)
> +		return -ENOMEM;
> +
> +	port->dev = dev;
> +
> +	err = rockchip_pcie_parse_dt(port);
> +	if (err)
> +		return err;
> +
> +	err = clk_prepare_enable(port->aclk_pcie);
> +	if (err) {
> +		dev_err(dev, "Unable to enable aclk_pcie clock.\n");
> +		goto err_aclk_pcie;
> +	}
> +
> +	err = clk_prepare_enable(port->aclk_perf_pcie);
> +	if (err) {
> +		dev_err(dev, "Unable to enable aclk_perf_pcie clock.\n");
> +		goto err_aclk_perf_pcie;
> +	}
> +
> +	err = clk_prepare_enable(port->hclk_pcie);
> +	if (err) {
> +		dev_err(dev, "Unable to enable hclk_pcie clock.\n");
> +		goto err_hclk_pcie;
> +	}
> +
> +	err = clk_prepare_enable(port->clk_pcie_pm);
> +	if (err) {
> +		dev_err(dev, "Unable to enable hclk_pcie clock.\n");
> +		goto err_pcie_pm;
> +	}
> +
> +	err = rockchip_pcie_set_vpcie(port);
> +	if (err) {
> +		dev_err(port->dev, "Fail to set vpcie regulator.\n");
> +		goto err_set_vpcie;
> +	}
> +
> +	err = rockchip_pcie_init_port(port);
> +	if (err)
> +		goto err_vpcie;
> +
> +	platform_set_drvdata(pdev, port);
> +
> +	rockchip_pcie_enable_interrupts(port);
> +
> +	err = rockchip_pcie_init_irq_domain(port);
> +	if (err < 0)
> +		goto err_vpcie;
> +
> +	err = of_pci_get_host_bridge_resources(dev->of_node, 0, 0xff,
> +					       &res, &io_base);
> +	if (err)
> +		goto err_vpcie;

Use devm_request_pci_bus_resources() so the windows show up in
/proc/iomem.  I posted patches to do this with subject lines like
"Request host bridge window resources with core function".

> +	/* Get the I/O and memory ranges from DT */
> +	resource_list_for_each_entry(win, &res) {
> +		switch (resource_type(win->res)) {
> +		case IORESOURCE_IO:
> +			io = win->res;
> +			io->name = "I/O";
> +			io_size = resource_size(io);
> +			io_bus_addr = io->start - win->offset;
> +			err = pci_remap_iospace(io, io_base);
> +			if (err) {
> +				dev_warn(port->dev, "error %d: failed to map resource %pR\n",
> +					 err, io);
> +				continue;
> +			}
> +			break;
> +		case IORESOURCE_MEM:
> +			mem = win->res;
> +			mem->name = "MEM";
> +			mem_size = resource_size(mem);
> +			mem_bus_addr = mem->start - win->offset;
> +			break;
> +		case IORESOURCE_BUS:
> +			busn = win->res;
> +			break;
> +		default:
> +			continue;
> +		}
> +	}
> +
> +	if (mem_size)
> +		for (reg_no = 0; reg_no < (mem_size >> 20); reg_no++) {
> +			err = rockchip_pcie_prog_ob_atu(port, reg_no + 1,
> +							AXI_WRAPPER_MEM_WRITE,
> +							20 - 1,
> +							mem_bus_addr +
> +							(reg_no << 20),
> +							0);
> +			if (err) {
> +				dev_err(dev, "Program RC mem outbound atu failed\n");
> +				goto err_vpcie;
> +			}
> +		}
> +
> +	err = rockchip_pcie_prog_ib_atu(port, 2, 32 - 1, 0x0, 0);
> +	if (err) {
> +		dev_err(dev, "Program RC mem inbound atu failed\n");
> +		goto err_vpcie;
> +	}
> +
> +	offset = mem_size >> 20;
> +
> +	if (io_size)
> +		for (reg_no = 0; reg_no < (io_size >> 20); reg_no++) {
> +			err = rockchip_pcie_prog_ob_atu(port,
> +							reg_no + 1 + offset,
> +							AXI_WRAPPER_IO_WRITE,
> +							20 - 1,
> +							io_bus_addr +
> +							(reg_no << 20),
> +							0);
> +			if (err) {
> +				dev_err(dev, "Program RC io outbound atu failed\n");
> +				goto err_vpcie;
> +			}
> +		}
> +
> +	port->root_bus_nr = busn->start;

NULL pointer dereference here if the DT doesn't have an IORESOURCE_BUS
resource.

> +	bus = pci_scan_root_bus(&pdev->dev, 0,
> +				&rockchip_pcie_ops, port, &res);
> +
> +	if (!bus) {
> +		err = -ENOMEM;
> +		goto err_vpcie;
> +	}
> +
> +	pci_bus_size_bridges(bus);
> +	pci_bus_assign_resources(bus);
> +	list_for_each_entry(child, &bus->children, node)
> +		pcie_bus_configure_settings(child);
> +
> +	pci_bus_add_devices(bus);
> +
> +	return err;
> +
> +err_vpcie:
> +	if (!IS_ERR(port->vpcie3v3))
> +		regulator_disable(port->vpcie3v3);
> +	if (!IS_ERR(port->vpcie1v8))
> +		regulator_disable(port->vpcie1v8);
> +	if (!IS_ERR(port->vpcie0v9))
> +		regulator_disable(port->vpcie0v9);
> +err_set_vpcie:
> +	clk_disable_unprepare(port->clk_pcie_pm);
> +err_pcie_pm:
> +	clk_disable_unprepare(port->hclk_pcie);
> +err_hclk_pcie:
> +	clk_disable_unprepare(port->aclk_perf_pcie);
> +err_aclk_perf_pcie:
> +	clk_disable_unprepare(port->aclk_pcie);
> +err_aclk_pcie:
> +	return err;
> +}
> +
> +static const struct of_device_id rockchip_pcie_of_match[] = {
> +	{ .compatible = "rockchip,rk3399-pcie", },
> +	{}
> +};
> +
> +static struct platform_driver rockchip_pcie_driver = {
> +	.driver = {
> +		.name = "rockchip-pcie",
> +		.of_match_table = rockchip_pcie_of_match,
> +	},
> +	.probe = rockchip_pcie_probe,
> +
> +};
> +module_platform_driver(rockchip_pcie_driver);
> +
> +MODULE_AUTHOR("Rockchip Inc");
> +MODULE_DESCRIPTION("Rockchip AXI PCIe driver");
> +MODULE_LICENSE("GPL v2");

Per your Kconfig, this driver cannot be a module, so remove these MODULE_*
annotations.  This is to follow Paul Gortmaker's recent "Make explicitly
non-modular" work.  Also change the include of <linux/module.h> to
<linux/init.h>.


From paul.gortmaker at windriver.com  Wed Jul 27 12:56:36 2016
From: paul.gortmaker at windriver.com (Paul Gortmaker)
Date: Wed, 27 Jul 2016 15:56:36 -0400
Subject: [PATCH v7 2/2] PCI: Rockchip: Add Rockchip PCIe controller support
In-Reply-To: <20160727182233.GB8901@localhost>
References: <1468802533-17699-1-git-send-email-shawn.lin@rock-chips.com>
 <1468802533-17699-2-git-send-email-shawn.lin@rock-chips.com>
 <20160727182233.GB8901@localhost>
Message-ID: <CAP=VYLqPj=81Q_8kV1wi=FHP-CB6arrXFAC0ktJrLqR-1TSCjw@mail.gmail.com>

On Wed, Jul 27, 2016 at 2:22 PM, Bjorn Helgaas <helgaas at kernel.org> wrote:
> Hi Shawn,
>
> I have some relatively minor comments below.
>
> On Mon, Jul 18, 2016 at 08:42:13AM +0800, Shawn Lin wrote:
>> This patch adds Rockchip PCIe controller support found
>> on RK3399 Soc platform.
>>
>> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
>>
>> ---
>>
>> Changes in v7:
>> - make it as a build-in driver

[...]

>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>> index c917e2f..4d714d2 100644
>> --- a/drivers/pci/host/Kconfig
>> +++ b/drivers/pci/host/Kconfig
>> @@ -264,4 +264,15 @@ config PCIE_ARTPEC6
>>         Say Y here to enable PCIe controller support on Axis ARTPEC-6
>>         SoCs.  This PCIe controller uses the DesignWare core.
>>
>> +config PCIE_ROCKCHIP
>> +     bool "Rockchip PCIe controller"
>> +     depends on ARM64 && ARCH_ROCKCHIP
>> +     depends on OF
>> +     depends on PCI_MSI_IRQ_DOMAIN
>> +     select MFD_SYSCON
>> +     help
>> +       Say Y here if you want internal PCI support on Rockchip SoC.
>> +       There is 1 internal PCIe port available to support GEN2 with
>> +       4 slots.
>> +
>>  endmenu
>> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
>> index 5bc0af2..695f716 100644
>> --- a/drivers/pci/host/Makefile
>> +++ b/drivers/pci/host/Makefile
>> @@ -30,3 +30,4 @@ obj-$(CONFIG_PCI_HOST_THUNDER_ECAM) += pci-thunder-ecam.o
>>  obj-$(CONFIG_PCI_HOST_THUNDER_PEM) += pci-thunder-pem.o
>>  obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o
>>  obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o
>> +obj-$(CONFIG_PCIE_ROCKCHIP) += pcie-rockchip.o
>> diff --git a/drivers/pci/host/pcie-rockchip.c b/drivers/pci/host/pcie-rockchip.c
>> new file mode 100644
>> index 0000000..8a1fef1
>> --- /dev/null
>> +++ b/drivers/pci/host/pcie-rockchip.c
>> @@ -0,0 +1,1233 @@
>> +/*
>> + * Rockchip AXI PCIe host controller driver
>> + *
>> + * Copyright (c) 2016 Rockchip, Inc.
>> + *
>> + * Author: Shawn Lin <shawn.lin at rock-chips.com>
>> + *         Wenrui Li <wenrui.li at rock-chips.com>
>> + *
>> + * Bits taken from Synopsys Designware Host controller driver and
>> + * ARM PCI Host generic driver.
>> + *
>> + * This program is free software: you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation, either version 2 of the License, or
>> + * (at your option) any later version.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/gpio/consumer.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/irq.h>
>> +#include <linux/irqchip/chained_irq.h>
>> +#include <linux/irqdomain.h>
>> +#include <linux/kernel.h>
>> +#include <linux/mfd/syscon.h>
>> +#include <linux/module.h>

[...]

>> +static struct platform_driver rockchip_pcie_driver = {
>> +     .driver = {
>> +             .name = "rockchip-pcie",
>> +             .of_match_table = rockchip_pcie_of_match,
>> +     },
>> +     .probe = rockchip_pcie_probe,
>> +
>> +};
>> +module_platform_driver(rockchip_pcie_driver);
>> +
>> +MODULE_AUTHOR("Rockchip Inc");
>> +MODULE_DESCRIPTION("Rockchip AXI PCIe driver");
>> +MODULE_LICENSE("GPL v2");
>
> Per your Kconfig, this driver cannot be a module, so remove these MODULE_*
> annotations.  This is to follow Paul Gortmaker's recent "Make explicitly
> non-modular" work.  Also change the include of <linux/module.h> to
> <linux/init.h>.

Thanks!  You beat me to it.    I didn't see any EXPORT_SYMBOL so it doesn't
look like we need export.h  added here.   Also, at the risk of stating
the obvious,
the module_platform_driver becomes builtin_platform_driver.

Paul.


From briannorris at chromium.org  Wed Jul 27 13:04:44 2016
From: briannorris at chromium.org (Brian Norris)
Date: Wed, 27 Jul 2016 13:04:44 -0700
Subject: [PATCH v7 2/2] PCI: Rockchip: Add Rockchip PCIe controller support
In-Reply-To: <CAP=VYLqPj=81Q_8kV1wi=FHP-CB6arrXFAC0ktJrLqR-1TSCjw@mail.gmail.com>
References: <1468802533-17699-1-git-send-email-shawn.lin@rock-chips.com>
 <1468802533-17699-2-git-send-email-shawn.lin@rock-chips.com>
 <20160727182233.GB8901@localhost>
 <CAP=VYLqPj=81Q_8kV1wi=FHP-CB6arrXFAC0ktJrLqR-1TSCjw@mail.gmail.com>
Message-ID: <20160727200443.GA30281@google.com>

+ Guenter

Hi,

On Wed, Jul 27, 2016 at 03:56:36PM -0400, Paul Gortmaker wrote:
> On Wed, Jul 27, 2016 at 2:22 PM, Bjorn Helgaas <helgaas at kernel.org> wrote:
> > On Mon, Jul 18, 2016 at 08:42:13AM +0800, Shawn Lin wrote:
> >> This patch adds Rockchip PCIe controller support found
> >> on RK3399 Soc platform.
> >>
> >> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
> >>
> >> ---
> >>
> >> Changes in v7:
> >> - make it as a build-in driver
> 
> [...]
> 

...

> [...]
> 
> >> +static struct platform_driver rockchip_pcie_driver = {
> >> +     .driver = {
> >> +             .name = "rockchip-pcie",
> >> +             .of_match_table = rockchip_pcie_of_match,
> >> +     },
> >> +     .probe = rockchip_pcie_probe,
> >> +
> >> +};
> >> +module_platform_driver(rockchip_pcie_driver);
> >> +
> >> +MODULE_AUTHOR("Rockchip Inc");
> >> +MODULE_DESCRIPTION("Rockchip AXI PCIe driver");
> >> +MODULE_LICENSE("GPL v2");
> >
> > Per your Kconfig, this driver cannot be a module, so remove these MODULE_*
> > annotations.  This is to follow Paul Gortmaker's recent "Make explicitly
> > non-modular" work.  Also change the include of <linux/module.h> to
> > <linux/init.h>.
> 
> Thanks!  You beat me to it.    I didn't see any EXPORT_SYMBOL so it doesn't
> look like we need export.h  added here.   Also, at the risk of stating
> the obvious,
> the module_platform_driver becomes builtin_platform_driver.

Just for reference (not to necessarily disagree here): we've kinda
ping-ponged on this one. I suggested it could be built as a module, so
Shawn added it, but apparently we're missing an export:

https://lkml.org/lkml/2016/7/8/704

so he reverted back. It's probably best to just leave this built-in and
resolve the comments Paul and Bjorn made, to avoid too much more
flip-flopping. If we still want to patch up the __weak
pci_remap_iospace(), then we can make this a module at a later time.

Regards,
Brian


From groeck at google.com  Wed Jul 27 14:31:05 2016
From: groeck at google.com (Guenter Roeck)
Date: Wed, 27 Jul 2016 14:31:05 -0700
Subject: [v7 PATCH 1/6] extcon: Add EXTCON_DISP_DP and the property for
 USB Type-C
In-Reply-To: <1469624836-22792-2-git-send-email-zyw@rock-chips.com>
References: <1469624836-22792-1-git-send-email-zyw@rock-chips.com>
 <1469624836-22792-2-git-send-email-zyw@rock-chips.com>
Message-ID: <CABXOdTfzZ8KD+W4GQBCoi_xhoX1Hu5USyHXorKY-rqLcB3gEpA@mail.gmail.com>

On Wed, Jul 27, 2016 at 6:07 AM, Chris Zhong <zyw at rock-chips.com> wrote:
> Add EXTCON_DISP_DP for the Display external connector. For Type-C
> connector the DisplayPort can work as an Alternate Mode(VESA DisplayPort
> Alt Mode on USB Type-C Standard). The Type-C support both normal
> and flipped orientation, so add a property to extcon.
>
> Signed-off-by: Chris Zhong <zyw at rock-chips.com>
> Signed-off-by: Chanwoo Choi <cw00.choi at samsung.com>

Reviewed-by: Guenter Roeck <groeck at chromium.org>

> ---
>
> Changes in v7: None
> Changes in v6: None
> Changes in v5: None
> Changes in v4: None
> Changes in v3: None
> Changes in v2: None
> Changes in v1: None
>
>  drivers/extcon/extcon.c | 5 +++++
>  include/linux/extcon.h  | 8 +++++++-
>  2 files changed, 12 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c
> index 68890bb..6c7488a 100644
> --- a/drivers/extcon/extcon.c
> +++ b/drivers/extcon/extcon.c
> @@ -157,6 +157,11 @@ struct __extcon_info {
>                 .id = EXTCON_DISP_VGA,
>                 .name = "VGA",
>         },
> +       [EXTCON_DISP_DP] = {
> +               .type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB,
> +               .id = EXTCON_DISP_DP,
> +               .name = "DP",
> +       },
>
>         /* Miscellaneous external connector */
>         [EXTCON_DOCK] = {
> diff --git a/include/linux/extcon.h b/include/linux/extcon.h
> index f686204..9670d3f 100644
> --- a/include/linux/extcon.h
> +++ b/include/linux/extcon.h
> @@ -69,6 +69,7 @@
>  #define EXTCON_DISP_MHL                41      /* Mobile High-Definition Link */
>  #define EXTCON_DISP_DVI                42      /* Digital Visual Interface */
>  #define EXTCON_DISP_VGA                43      /* Video Graphics Array */
> +#define EXTCON_DISP_DP         44      /* Display Port */
>
>  /* Miscellaneous external connector */
>  #define EXTCON_DOCK            60
> @@ -106,12 +107,17 @@
>   * @type:      integer (intval)
>   * @value:     0 (low) or 1 (high)
>   * @default:   0 (low)
> + * - EXTCON_PROP_USB_TYPEC_POLARITY
> + * @type:      integer (intval)
> + * @value:     0 (normal) or 1 (flip)
> + * @default:   0 (normal)
>   */
>  #define EXTCON_PROP_USB_ID             0
>  #define EXTCON_PROP_USB_VBUS           1
> +#define EXTCON_PROP_USB_TYPEC_POLARITY 2
>
>  #define EXTCON_PROP_USB_MIN            0
> -#define EXTCON_PROP_USB_MAX            1
> +#define EXTCON_PROP_USB_MAX            2
>  #define EXTCON_PROP_USB_CNT    (EXTCON_PROP_USB_MAX - EXTCON_PROP_USB_MIN + 1)
>
>  /* Properties of EXTCON_TYPE_CHG. */
> --
> 2.6.3
>


From groeck at google.com  Wed Jul 27 14:47:42 2016
From: groeck at google.com (Guenter Roeck)
Date: Wed, 27 Jul 2016 14:47:42 -0700
Subject: [v7 PATCH 3/6] phy: Add USB Type-C PHY driver for rk3399
In-Reply-To: <1469624836-22792-4-git-send-email-zyw@rock-chips.com>
References: <1469624836-22792-1-git-send-email-zyw@rock-chips.com>
 <1469624836-22792-4-git-send-email-zyw@rock-chips.com>
Message-ID: <CABXOdTeLy1O=2psjT8wqZWdAPFnVVd=TLFCEo9xdD2=BMqS_bw@mail.gmail.com>

On Wed, Jul 27, 2016 at 6:07 AM, Chris Zhong <zyw at rock-chips.com> wrote:
> Add a PHY provider driver for the rk3399 SoC Type-c PHY. The USB
> Type-C PHY is designed to support the USB3 and DP applications. The
> PHY basically has two main components: USB3 and DisplyPort. USB3
> operates in SuperSpeed mode and the DP can operate at RBR, HBR and
> HBR2 data rates.
>
> Signed-off-by: Chris Zhong <zyw at rock-chips.com>
> Signed-off-by: Kever Yang <kever.yang at rock-chips.com>
>
> ---
>
> Changes in v7:
> - support new API of extcon
>
> Changes in v6:
> - delete the support of PIN_ASSIGN_A/B
> - set the default mode to MODE_DFP_USB
> - disable DP PLL at USB3 only mode
>
> Changes in v5:
> - support get property from extcon
> - remove PIN ASSIGN A/B support
>
> Changes in v4:
> - select EXTCON
> - use phy framework to control the USB3 and DP function
> - rename PIN_MAP_ to PIN_ASSIGN_
>
> Changes in v3:
> - remove the phy framework(Kishon Vijay Abraham I)
> - add parentheses around the macro
> - use a single space between type and name
> - add spaces after opening and before closing braces.
> - use u16 for register value
> - remove type-c phy header file
> - CodingStyle optimization
> - use some cable extcon to get type-c port information
> - add a extcon to notify Display Port
>
> Changes in v2:
> - select RESET_CONTROLLER
> - alphabetic order
> - modify some spelling mistakes
> - make mode cleaner
> - use bool for enable/disable
> - check all of the return value
> - return a better err number
> - use more readx_poll_timeout()
> - clk_disable_unprepare(tcphy->clk_ref);
> - remove unuse functions, rockchip_typec_phy_power_on/off
> - remove unnecessary typecast from void *
> - use dts node to distinguish between phys.
>
> Changes in v1:
> - update the licence note
> - init core clock to 50MHz
> - use extcon API
> - remove unused global
> - add some comments for magic num
> - change usleep_range(1000, 2000) tousleep_range(1000, 1050)
> - remove __func__ from dev_err
> - return err number when get clk failed
> - remove ADDR_ADJ define
> - use devm_clk_get(&pdev->dev, "tcpdcore")
>
>  drivers/phy/Kconfig              |   9 +
>  drivers/phy/Makefile             |   1 +
>  drivers/phy/phy-rockchip-typec.c | 961 +++++++++++++++++++++++++++++++++++++++
>  3 files changed, 971 insertions(+)
>  create mode 100644 drivers/phy/phy-rockchip-typec.c
>
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index 26566db..83706a5 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -351,6 +351,15 @@ config PHY_ROCKCHIP_DP
>         help
>           Enable this to support the Rockchip Display Port PHY.
>
> +config PHY_ROCKCHIP_TYPEC
> +       tristate "Rockchip TYPEC PHY Driver"
> +       depends on ARCH_ROCKCHIP && OF
> +       select EXTCON
> +       select GENERIC_PHY
> +       select RESET_CONTROLLER
> +       help
> +         Enable this to support the Rockchip USB TYPEC PHY.
> +
>  config PHY_ST_SPEAR1310_MIPHY
>         tristate "ST SPEAR1310-MIPHY driver"
>         select GENERIC_PHY
> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
> index 24596a9..91fa413 100644
> --- a/drivers/phy/Makefile
> +++ b/drivers/phy/Makefile
> @@ -39,6 +39,7 @@ obj-$(CONFIG_PHY_QCOM_APQ8064_SATA)   += phy-qcom-apq8064-sata.o
>  obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o
>  obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o
>  obj-$(CONFIG_PHY_ROCKCHIP_DP)          += phy-rockchip-dp.o
> +obj-$(CONFIG_PHY_ROCKCHIP_TYPEC) += phy-rockchip-typec.o
>  obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA)    += phy-qcom-ipq806x-sata.o
>  obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY)   += phy-spear1310-miphy.o
>  obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY)   += phy-spear1340-miphy.o
> diff --git a/drivers/phy/phy-rockchip-typec.c b/drivers/phy/phy-rockchip-typec.c
> new file mode 100644
> index 0000000..278fc0c
> --- /dev/null
> +++ b/drivers/phy/phy-rockchip-typec.c
> @@ -0,0 +1,961 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author: Chris Zhong <zyw at rock-chips.com>
> + *         Kever Yang <kever.yang at rock-chips.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * 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/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/delay.h>
> +#include <linux/extcon.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/phy/phy.h>

The last two includes files are not in alphabetic order.

> +
> +#define CMN_SSM_BANDGAP                        (0x21 << 2)
> +#define CMN_SSM_BIAS                   (0x22 << 2)
> +#define CMN_PLLSM0_PLLEN               (0x29 << 2)
> +#define CMN_PLLSM0_PLLPRE              (0x2a << 2)
> +#define CMN_PLLSM0_PLLVREF             (0x2b << 2)
> +#define CMN_PLLSM0_PLLLOCK             (0x2c << 2)
> +#define CMN_PLLSM1_PLLEN               (0x31 << 2)
> +#define CMN_PLLSM1_PLLPRE              (0x32 << 2)
> +#define CMN_PLLSM1_PLLVREF             (0x33 << 2)
> +#define CMN_PLLSM1_PLLLOCK             (0x34 << 2)
> +#define CMN_PLLSM1_USER_DEF_CTRL       (0x37 << 2)
> +#define CMN_ICAL_OVRD                  (0xc1 << 2)
> +#define CMN_PLL0_VCOCAL_OVRD           (0x83 << 2)
> +#define CMN_PLL0_VCOCAL_INIT           (0x84 << 2)
> +#define CMN_PLL0_VCOCAL_ITER           (0x85 << 2)
> +#define CMN_PLL0_LOCK_REFCNT_START     (0x90 << 2)
> +#define CMN_PLL0_LOCK_PLLCNT_START     (0x92 << 2)
> +#define CMN_PLL0_LOCK_PLLCNT_THR       (0x93 << 2)
> +#define CMN_PLL0_INTDIV                        (0x94 << 2)
> +#define CMN_PLL0_FRACDIV               (0x95 << 2)
> +#define CMN_PLL0_HIGH_THR              (0x96 << 2)
> +#define CMN_PLL0_DSM_DIAG              (0x97 << 2)
> +#define CMN_PLL0_SS_CTRL1              (0x98 << 2)
> +#define CMN_PLL0_SS_CTRL2              (0x99 << 2)
> +#define CMN_PLL1_VCOCAL_START          (0xa1 << 2)
> +#define CMN_PLL1_VCOCAL_OVRD           (0xa3 << 2)
> +#define CMN_PLL1_VCOCAL_INIT           (0xa4 << 2)
> +#define CMN_PLL1_VCOCAL_ITER           (0xa5 << 2)
> +#define CMN_PLL1_LOCK_REFCNT_START     (0xb0 << 2)
> +#define CMN_PLL1_LOCK_PLLCNT_START     (0xb2 << 2)
> +#define CMN_PLL1_LOCK_PLLCNT_THR       (0xb3 << 2)
> +#define CMN_PLL1_INTDIV                        (0xb4 << 2)
> +#define CMN_PLL1_FRACDIV               (0xb5 << 2)
> +#define CMN_PLL1_HIGH_THR              (0xb6 << 2)
> +#define CMN_PLL1_DSM_DIAG              (0xb7 << 2)
> +#define CMN_PLL1_SS_CTRL1              (0xb8 << 2)
> +#define CMN_PLL1_SS_CTRL2              (0xb9 << 2)
> +#define CMN_RXCAL_OVRD                 (0xd1 << 2)
> +#define CMN_TXPUCAL_CTRL               (0xe0 << 2)
> +#define CMN_TXPUCAL_OVRD               (0xe1 << 2)
> +#define CMN_TXPDCAL_OVRD               (0xf1 << 2)
> +#define CMN_DIAG_PLL0_FBH_OVRD         (0x1c0 << 2)
> +#define CMN_DIAG_PLL0_FBL_OVRD         (0x1c1 << 2)
> +#define CMN_DIAG_PLL0_OVRD             (0x1c2 << 2)
> +#define CMN_DIAG_PLL0_V2I_TUNE         (0x1c5 << 2)
> +#define CMN_DIAG_PLL0_CP_TUNE          (0x1c6 << 2)
> +#define CMN_DIAG_PLL0_LF_PROG          (0x1c7 << 2)
> +#define CMN_DIAG_PLL1_FBH_OVRD         (0x1d0 << 2)
> +#define CMN_DIAG_PLL1_FBL_OVRD         (0x1d1 << 2)
> +#define CMN_DIAG_PLL1_OVRD             (0x1d2 << 2)
> +#define CMN_DIAG_PLL1_V2I_TUNE         (0x1d5 << 2)
> +#define CMN_DIAG_PLL1_CP_TUNE          (0x1d6 << 2)
> +#define CMN_DIAG_PLL1_LF_PROG          (0x1d7 << 2)
> +#define CMN_DIAG_PLL1_PTATIS_TUNE1     (0x1d8 << 2)
> +#define CMN_DIAG_PLL1_PTATIS_TUNE2     (0x1d9 << 2)
> +#define CMN_DIAG_PLL1_INCLK_CTRL       (0x1da << 2)
> +#define CMN_DIAG_HSCLK_SEL             (0x1e0 << 2)
> +
> +#define XCVR_PSM_RCTRL(n)              ((0x4001 | ((n) << 9)) << 2)
> +#define XCVR_PSM_CAL_TMR(n)            ((0x4002 | ((n) << 9)) << 2)
> +#define XCVR_PSM_A0IN_TMR(n)           ((0x4003 | ((n) << 9)) << 2)
> +#define TX_TXCC_CAL_SCLR_MULT(n)       ((0x4047 | ((n) << 9)) << 2)
> +#define TX_TXCC_CPOST_MULT_00(n)       ((0x404c | ((n) << 9)) << 2)
> +#define TX_TXCC_CPOST_MULT_01(n)       ((0x404d | ((n) << 9)) << 2)
> +#define TX_TXCC_CPOST_MULT_10(n)       ((0x404e | ((n) << 9)) << 2)
> +#define TX_TXCC_CPOST_MULT_11(n)       ((0x404f | ((n) << 9)) << 2)
> +#define TX_TXCC_MGNFS_MULT_000(n)      ((0x4050 | ((n) << 9)) << 2)
> +#define TX_TXCC_MGNFS_MULT_001(n)      ((0x4051 | ((n) << 9)) << 2)
> +#define TX_TXCC_MGNFS_MULT_010(n)      ((0x4052 | ((n) << 9)) << 2)
> +#define TX_TXCC_MGNFS_MULT_011(n)      ((0x4053 | ((n) << 9)) << 2)
> +#define TX_TXCC_MGNFS_MULT_100(n)      ((0x4054 | ((n) << 9)) << 2)
> +#define TX_TXCC_MGNFS_MULT_101(n)      ((0x4055 | ((n) << 9)) << 2)
> +#define TX_TXCC_MGNFS_MULT_110(n)      ((0x4056 | ((n) << 9)) << 2)
> +#define TX_TXCC_MGNFS_MULT_111(n)      ((0x4057 | ((n) << 9)) << 2)
> +#define XCVR_DIAG_PLLDRC_CTRL(n)       ((0x40e0 | ((n) << 9)) << 2)
> +#define XCVR_DIAG_BIDI_CTRL(n)         ((0x40e8 | ((n) << 9)) << 2)
> +#define XCVR_DIAG_LANE_FCM_EN_MGN(n)   ((0x40f2 | ((n) << 9)) << 2)
> +#define TX_PSC_A0(n)                   ((0x4100 | ((n) << 9)) << 2)
> +#define TX_PSC_A1(n)                   ((0x4101 | ((n) << 9)) << 2)
> +#define TX_PSC_A2(n)                   ((0x4102 | ((n) << 9)) << 2)
> +#define TX_PSC_A3(n)                   ((0x4103 | ((n) << 9)) << 2)
> +#define TX_RCVDET_CTRL(n)              ((0x4120 | ((n) << 9)) << 2)
> +#define TX_RCVDET_EN_TMR(n)            ((0x4122 | ((n) << 9)) << 2)
> +#define TX_RCVDET_ST_TMR(n)            ((0x4123 | ((n) << 9)) << 2)
> +#define TX_DIAG_TX_DRV(n)              ((0x41e1 | ((n) << 9)) << 2)
> +#define TX_DIAG_BGREF_PREDRV_DELAY     (0x41e7 << 2)
> +#define TX_ANA_CTRL_REG_1              (0x5020 << 2)
> +#define TX_ANA_CTRL_REG_2              (0x5021 << 2)
> +#define TXDA_COEFF_CALC_CTRL           (0x5022 << 2)
> +#define TX_DIG_CTRL_REG_2              (0x5024 << 2)
> +#define TXDA_CYA_AUXDA_CYA             (0x5025 << 2)
> +#define TX_ANA_CTRL_REG_3              (0x5026 << 2)
> +#define TX_ANA_CTRL_REG_4              (0x5027 << 2)
> +#define TX_ANA_CTRL_REG_5              (0x5029 << 2)
> +
> +#define RX_PSC_A0(n)                   ((0x8000 | ((n) << 9)) << 2)
> +#define RX_PSC_A1(n)                   ((0x8001 | ((n) << 9)) << 2)
> +#define RX_PSC_A2(n)                   ((0x8002 | ((n) << 9)) << 2)
> +#define RX_PSC_A3(n)                   ((0x8003 | ((n) << 9)) << 2)
> +#define RX_PSC_CAL(n)                  ((0x8006 | ((n) << 9)) << 2)
> +#define RX_PSC_RDY(n)                  ((0x8007 | ((n) << 9)) << 2)
> +#define RX_IQPI_ILL_CAL_OVRD           (0x8023 << 2)
> +#define RX_EPI_ILL_CAL_OVRD            (0x8033 << 2)
> +#define RX_SDCAL0_OVRD                 (0x8041 << 2)
> +#define RX_SDCAL1_OVRD                 (0x8049 << 2)
> +#define RX_SLC_INIT                    (0x806d << 2)
> +#define RX_SLC_RUN                     (0x806e << 2)
> +#define RX_CDRLF_CNFG2                 (0x8081 << 2)
> +#define RX_SIGDET_HL_FILT_TMR(n)       ((0x8090 | ((n) << 9)) << 2)
> +#define RX_SLC_IOP0_OVRD               (0x8101 << 2)
> +#define RX_SLC_IOP1_OVRD               (0x8105 << 2)
> +#define RX_SLC_QOP0_OVRD               (0x8109 << 2)
> +#define RX_SLC_QOP1_OVRD               (0x810d << 2)
> +#define RX_SLC_EOP0_OVRD               (0x8111 << 2)
> +#define RX_SLC_EOP1_OVRD               (0x8115 << 2)
> +#define RX_SLC_ION0_OVRD               (0x8119 << 2)
> +#define RX_SLC_ION1_OVRD               (0x811d << 2)
> +#define RX_SLC_QON0_OVRD               (0x8121 << 2)
> +#define RX_SLC_QON1_OVRD               (0x8125 << 2)
> +#define RX_SLC_EON0_OVRD               (0x8129 << 2)
> +#define RX_SLC_EON1_OVRD               (0x812d << 2)
> +#define RX_SLC_IEP0_OVRD               (0x8131 << 2)
> +#define RX_SLC_IEP1_OVRD               (0x8135 << 2)
> +#define RX_SLC_QEP0_OVRD               (0x8139 << 2)
> +#define RX_SLC_QEP1_OVRD               (0x813d << 2)
> +#define RX_SLC_EEP0_OVRD               (0x8141 << 2)
> +#define RX_SLC_EEP1_OVRD               (0x8145 << 2)
> +#define RX_SLC_IEN0_OVRD               (0x8149 << 2)
> +#define RX_SLC_IEN1_OVRD               (0x814d << 2)
> +#define RX_SLC_QEN0_OVRD               (0x8151 << 2)
> +#define RX_SLC_QEN1_OVRD               (0x8155 << 2)
> +#define RX_SLC_EEN0_OVRD               (0x8159 << 2)
> +#define RX_SLC_EEN1_OVRD               (0x815d << 2)
> +#define RX_DIAG_SIGDET_TUNE(n)         ((0x81dc | ((n) << 9)) << 2)
> +#define RX_DIAG_SC2C_DELAY             (0x81e1 << 2)
> +
> +#define PMA_LANE_CFG                   (0xc000 << 2)
> +#define PIPE_CMN_CTRL1                 (0xc001 << 2)
> +#define PIPE_CMN_CTRL2                 (0xc002 << 2)
> +#define PIPE_COM_LOCK_CFG1             (0xc003 << 2)
> +#define PIPE_COM_LOCK_CFG2             (0xc004 << 2)
> +#define PIPE_RCV_DET_INH               (0xc005 << 2)
> +#define DP_MODE_CTL                    (0xc008 << 2)
> +#define DP_CLK_CTL                     (0xc009 << 2)
> +#define STS                            (0xc00F << 2)
> +#define PHY_ISO_CMN_CTRL               (0xc010 << 2)
> +#define PHY_DP_TX_CTL                  (0xc408 << 2)
> +#define PMA_CMN_CTRL1                  (0xc800 << 2)
> +#define PHY_PMA_ISO_CMN_CTRL           (0xc810 << 2)
> +#define PHY_ISOLATION_CTRL             (0xc81f << 2)
> +#define PHY_PMA_ISO_XCVR_CTRL(n)       ((0xcc11 | ((n) << 6)) << 2)
> +#define PHY_PMA_ISO_LINK_MODE(n)       ((0xcc12 | ((n) << 6)) << 2)
> +#define PHY_PMA_ISO_PWRST_CTRL(n)      ((0xcc13 | ((n) << 6)) << 2)
> +#define PHY_PMA_ISO_TX_DATA_LO(n)      ((0xcc14 | ((n) << 6)) << 2)
> +#define PHY_PMA_ISO_TX_DATA_HI(n)      ((0xcc15 | ((n) << 6)) << 2)
> +#define PHY_PMA_ISO_RX_DATA_LO(n)      ((0xcc16 | ((n) << 6)) << 2)
> +#define PHY_PMA_ISO_RX_DATA_HI(n)      ((0xcc17 | ((n) << 6)) << 2)
> +#define TX_BIST_CTRL(n)                        ((0x4140 | ((n) << 9)) << 2)
> +#define TX_BIST_UDDWR(n)               ((0x4141 | ((n) << 9)) << 2)
> +
> +/*
> + * Selects which PLL clock will be driven on the analog high speed
> + * clock 0: PLL 0 div 1
> + * clock 1: PLL 1 div 2
> + */
> +#define CLK_PLL_CONFIG                 0X30
> +#define CLK_PLL_MASK                   0x33
> +
> +#define CMN_READY                      BIT(0)
> +
> +#define DP_PLL_CLOCK_ENABLE            BIT(2)
> +#define DP_PLL_ENABLE                  BIT(0)
> +#define DP_PLL_DATA_RATE_RBR           ((2 << 12) | (4 << 8))
> +#define DP_PLL_DATA_RATE_HBR           ((2 << 12) | (4 << 8))
> +#define DP_PLL_DATA_RATE_HBR2          ((1 << 12) | (2 << 8))
> +
> +#define GRF_SOC_CON26                  0x6268
> +#define UPHY_DP_SEL                    BIT(3)
> +#define UPHY_DP_SEL_MASK               BIT(19)
> +#define DPTX_HPD_SEL                   (3 << 12)
> +#define DPTX_HPD_DEL                   (2 << 12)
> +#define DPTX_HPD_SEL_MASK              (3 << 28)
> +
> +#define PHY_MODE_SET_TIMEOUT           1000000
> +
> +#define PIN_ASSIGN_A                   BIT(0)
> +#define PIN_ASSIGN_B                   BIT(1)
> +#define PIN_ASSIGN_C                   BIT(2)
> +#define PIN_ASSIGN_D                   BIT(3)
> +#define PIN_ASSIGN_E                   BIT(4)
> +#define PIN_ASSIGN_F                   BIT(5)
> +
> +#define MODE_DISCONNECT                        0
> +#define MODE_UFP_USB                   BIT(0)
> +#define MODE_DFP_USB                   BIT(1)
> +#define MODE_DFP_DP                    BIT(2)
> +
> +struct usb3phy_reg {
> +       u32 offset;
> +       u32 enable_bit;
> +       u32 write_enable;
> +};
> +
> +struct rockchip_usb3phy_port_cfg {
> +       struct usb3phy_reg typec_conn_dir;
> +       struct usb3phy_reg usb3tousb2_en;
> +       struct usb3phy_reg external_psm;
> +       struct usb3phy_reg pipe_status;
> +       struct usb3phy_reg uphy_dp_sel;
> +};
> +
> +struct rockchip_typec_phy {
> +       struct device *dev;
> +       void __iomem *base;
> +       struct extcon_dev *extcon;
> +       struct phy *phy;
> +       struct regmap *grf_regs;
> +       struct clk *clk_core;
> +       struct clk *clk_ref;
> +       struct reset_control *uphy_rst;
> +       struct reset_control *pipe_rst;
> +       struct reset_control *tcphy_rst;
> +       struct rockchip_usb3phy_port_cfg port_cfgs;
> +
> +       bool flip;
> +       u8 mode;
> +       u8 pin_assign;
> +};
> +
> +struct phy_reg {
> +       u16 value;
> +       u32 addr;
> +};
> +
> +struct phy_reg usb_pll_cfg[] = {
> +       { 0xf0,         CMN_PLL0_VCOCAL_INIT },
> +       { 0x18,         CMN_PLL0_VCOCAL_ITER },
> +       { 0xd0,         CMN_PLL0_INTDIV },
> +       { 0x4a4a,       CMN_PLL0_FRACDIV },
> +       { 0x34,         CMN_PLL0_HIGH_THR },
> +       { 0x1ee,        CMN_PLL0_SS_CTRL1 },
> +       { 0x7f03,       CMN_PLL0_SS_CTRL2 },
> +       { 0x20,         CMN_PLL0_DSM_DIAG },
> +       { 0,            CMN_DIAG_PLL0_OVRD },
> +       { 0,            CMN_DIAG_PLL0_FBH_OVRD },
> +       { 0,            CMN_DIAG_PLL0_FBL_OVRD },
> +       { 0x7,          CMN_DIAG_PLL0_V2I_TUNE },
> +       { 0x45,         CMN_DIAG_PLL0_CP_TUNE },
> +       { 0x8,          CMN_DIAG_PLL0_LF_PROG },
> +};
> +
> +struct phy_reg dp_pll_cfg[] = {
> +       { 0xf0,         CMN_PLL1_VCOCAL_INIT },
> +       { 0x18,         CMN_PLL1_VCOCAL_ITER },
> +       { 0x30b9,       CMN_PLL1_VCOCAL_START },
> +       { 0x21c,        CMN_PLL1_INTDIV },
> +       { 0,            CMN_PLL1_FRACDIV },
> +       { 0x5,          CMN_PLL1_HIGH_THR },
> +       { 0x35,         CMN_PLL1_SS_CTRL1 },
> +       { 0x7f1e,       CMN_PLL1_SS_CTRL2 },
> +       { 0x20,         CMN_PLL1_DSM_DIAG },
> +       { 0,            CMN_PLLSM1_USER_DEF_CTRL },
> +       { 0,            CMN_DIAG_PLL1_OVRD },
> +       { 0,            CMN_DIAG_PLL1_FBH_OVRD },
> +       { 0,            CMN_DIAG_PLL1_FBL_OVRD },
> +       { 0x6,          CMN_DIAG_PLL1_V2I_TUNE },
> +       { 0x45,         CMN_DIAG_PLL1_CP_TUNE },
> +       { 0x8,          CMN_DIAG_PLL1_LF_PROG },
> +       { 0x100,        CMN_DIAG_PLL1_PTATIS_TUNE1 },
> +       { 0x7,          CMN_DIAG_PLL1_PTATIS_TUNE2 },
> +       { 0x4,          CMN_DIAG_PLL1_INCLK_CTRL },
> +};
> +
> +static void tcphy_cfg_24m(struct rockchip_typec_phy *tcphy,
> +                         u32 num_lanes)
> +{
> +       u32 i, rdata;
> +
> +       /*
> +        * cmn_ref_clk_sel = 3, select the 24Mhz for clk parent
> +        * cmn_psm_clk_dig_div = 2, set the clk division to 2
> +        */
> +       writel(0x830, tcphy->base + PMA_CMN_CTRL1);
> +       for (i = 0; i < num_lanes; i++) {
> +               /*
> +                * The following PHY configuration assumes a 24 MHz reference
> +                * clock.
> +                */
> +               writel(0x90, tcphy->base + XCVR_DIAG_LANE_FCM_EN_MGN(i));
> +               writel(0x960, tcphy->base + TX_RCVDET_EN_TMR(i));
> +               writel(0x30, tcphy->base + TX_RCVDET_ST_TMR(i));
> +       }
> +
> +       rdata = readl(tcphy->base + CMN_DIAG_HSCLK_SEL);
> +       rdata &= ~CLK_PLL_MASK;
> +       rdata |= CLK_PLL_CONFIG;
> +       writel(rdata, tcphy->base + CMN_DIAG_HSCLK_SEL);
> +

Unnecessary empty line

> +}
> +
> +static void tcphy_cfg_usb_pll(struct rockchip_typec_phy *tcphy)
> +{
> +       u32 i;
> +
> +       /* load the configuration of PLL0 */
> +       for (i = 0; i < ARRAY_SIZE(usb_pll_cfg); i++)
> +               writel(usb_pll_cfg[i].value, tcphy->base + usb_pll_cfg[i].addr);
> +}
> +
> +static void tcphy_cfg_dp_pll(struct rockchip_typec_phy *tcphy)
> +{
> +       u32 i;
> +
> +       /* set the default mode to RBR */
> +       writel(DP_PLL_CLOCK_ENABLE | DP_PLL_ENABLE | DP_PLL_DATA_RATE_RBR,
> +              tcphy->base + DP_CLK_CTL);
> +
> +       /* load the configuration of PLL1 */
> +       for (i = 0; i < ARRAY_SIZE(dp_pll_cfg); i++)
> +               writel(dp_pll_cfg[i].value, tcphy->base + dp_pll_cfg[i].addr);
> +}
> +
> +static void tcphy_tx_usb_cfg_lane(struct rockchip_typec_phy *tcphy,
> +                                 u32 lane)
> +{
> +       writel(0x7799, tcphy->base + TX_PSC_A0(lane));
> +       writel(0x7798, tcphy->base + TX_PSC_A1(lane));
> +       writel(0x5098, tcphy->base + TX_PSC_A2(lane));
> +       writel(0x5098, tcphy->base + TX_PSC_A3(lane));
> +       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
> +       writel(0xbf, tcphy->base + XCVR_DIAG_BIDI_CTRL(lane));
> +}
> +
> +static void tcphy_rx_usb_cfg_lane(struct rockchip_typec_phy *tcphy,
> +                                 u32 lane)

Unnecessary line split

> +{
> +       writel(0xa6fd, tcphy->base + RX_PSC_A0(lane));
> +       writel(0xa6fd, tcphy->base + RX_PSC_A1(lane));
> +       writel(0xa410, tcphy->base + RX_PSC_A2(lane));
> +       writel(0x2410, tcphy->base + RX_PSC_A3(lane));
> +       writel(0x23ff, tcphy->base + RX_PSC_CAL(lane));
> +       writel(0x13, tcphy->base + RX_SIGDET_HL_FILT_TMR(lane));
> +       writel(0x1004, tcphy->base + RX_DIAG_SIGDET_TUNE(lane));
> +       writel(0x2010, tcphy->base + RX_PSC_RDY(lane));
> +       writel(0xfb, tcphy->base + XCVR_DIAG_BIDI_CTRL(lane));
> +}
> +
> +static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy,
> +                             u32 lane)

Unnecessary line split

> +{
> +       u16 rdata;
> +
> +       writel(0xbefc, tcphy->base + XCVR_PSM_RCTRL(lane));
> +       writel(0x6799, tcphy->base + TX_PSC_A0(lane));
> +       writel(0x6798, tcphy->base + TX_PSC_A1(lane));
> +       writel(0x98, tcphy->base + TX_PSC_A2(lane));
> +       writel(0x98, tcphy->base + TX_PSC_A3(lane));
> +
> +       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
> +       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_001(lane));
> +       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_010(lane));
> +       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_011(lane));
> +       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_100(lane));
> +       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_101(lane));
> +       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_110(lane));
> +       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_111(lane));
> +       writel(0, tcphy->base + TX_TXCC_CPOST_MULT_10(lane));
> +       writel(0, tcphy->base + TX_TXCC_CPOST_MULT_01(lane));
> +       writel(0, tcphy->base + TX_TXCC_CPOST_MULT_00(lane));
> +       writel(0, tcphy->base + TX_TXCC_CPOST_MULT_11(lane));
> +
> +       writel(0x128, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
> +       writel(0x400, tcphy->base + TX_DIAG_TX_DRV(lane));
> +
> +       rdata = readl(tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
> +       rdata = (rdata & 0x8fff) | 0x6000;
> +       writel(rdata, tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
> +}
> +
> +static void tcphy_cfg_pin_assign(struct rockchip_typec_phy *tcphy)
> +{
> +       switch (tcphy->pin_assign) {
> +       case PIN_ASSIGN_A:
> +               writel(0x19d5, tcphy->base + PMA_LANE_CFG);
> +               break;
> +       case PIN_ASSIGN_B:
> +               writel(0x1500, tcphy->base + PMA_LANE_CFG);
> +               break;
> +       case PIN_ASSIGN_C:
> +       case PIN_ASSIGN_E:
> +               writel(0x51d9, tcphy->base + PMA_LANE_CFG);
> +               break;
> +       case PIN_ASSIGN_D:
> +       case PIN_ASSIGN_F:
> +               writel(0x5100, tcphy->base + PMA_LANE_CFG);
> +               break;
> +       };
> +}
> +
> +static inline int property_enable(struct rockchip_typec_phy *tcphy,
> +                                 const struct usb3phy_reg *reg, bool en)
> +{
> +       u32 mask = 1 << reg->write_enable;
> +       u32 val = en << reg->enable_bit;
> +
> +       return regmap_write(tcphy->grf_regs, reg->offset, val | mask);
> +}
> +
> +static void tcphy_lanes_config(struct rockchip_typec_phy *tcphy)
> +{
> +       struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
> +       u32 i;
> +
> +       property_enable(tcphy, &cfg->typec_conn_dir, tcphy->flip);
> +
> +       tcphy_cfg_24m(tcphy, 0x4);
> +
> +       switch (tcphy->mode) {
> +       case MODE_UFP_USB:
> +       case MODE_DFP_USB:
> +               tcphy_cfg_usb_pll(tcphy);
> +               if (tcphy->flip) {
> +                       tcphy_tx_usb_cfg_lane(tcphy, 3);
> +                       tcphy_rx_usb_cfg_lane(tcphy, 2);
> +               } else {
> +                       tcphy_tx_usb_cfg_lane(tcphy, 0);
> +                       tcphy_rx_usb_cfg_lane(tcphy, 1);
> +               }
> +               break;
> +       case MODE_DFP_DP:
> +               tcphy_cfg_dp_pll(tcphy);
> +               for (i = 0; i < 4; i++)
> +                       tcphy_dp_cfg_lane(tcphy, i);
> +               break;
> +       case MODE_DFP_USB | MODE_DFP_DP:
> +               tcphy_cfg_usb_pll(tcphy);
> +               tcphy_cfg_dp_pll(tcphy);
> +               if (tcphy->flip) {
> +                       tcphy_tx_usb_cfg_lane(tcphy, 3);
> +                       tcphy_rx_usb_cfg_lane(tcphy, 2);
> +                       tcphy_dp_cfg_lane(tcphy, 0);
> +                       tcphy_dp_cfg_lane(tcphy, 1);
> +               } else {
> +                       tcphy_tx_usb_cfg_lane(tcphy, 0);
> +                       tcphy_rx_usb_cfg_lane(tcphy, 1);
> +                       tcphy_dp_cfg_lane(tcphy, 2);
> +                       tcphy_dp_cfg_lane(tcphy, 3);
> +               }
> +               break;
> +       }
> +}
> +
> +static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy)
> +{
> +       u16 rdata, rdata2, val;
> +
> +       /* disable txda_cal_latch_en for rewrite the calibration values */
> +       rdata = readl(tcphy->base + TX_ANA_CTRL_REG_1);
> +       val = rdata & 0xdfff;
> +       writel(val, tcphy->base + TX_ANA_CTRL_REG_1);
> +
> +       /*
> +        * read a resistor calibration code from CMN_TXPUCAL_CTRL[6:0] and
> +        * write it to TX_DIG_CTRL_REG_2[6:0], and delay 1ms to make sure it
> +        * works.
> +        */
> +       rdata = readl(tcphy->base + TX_DIG_CTRL_REG_2);
> +       rdata = rdata & 0xffc0;
> +
> +       rdata2 = readl(tcphy->base + CMN_TXPUCAL_CTRL);
> +       rdata2 = rdata2 & 0x3f;
> +
> +       val = rdata | rdata2;
> +       writel(val, tcphy->base + TX_DIG_CTRL_REG_2);
> +       usleep_range(1000, 1050);
> +
> +       /*
> +        * Enable signal for latch that sample and holds calibration values.
> +        * Activate this signal for 1 clock cycle to sample new calibration
> +        * values.
> +        */
> +       rdata = readl(tcphy->base + TX_ANA_CTRL_REG_1);
> +       val = rdata | 0x2000;
> +       writel(val, tcphy->base + TX_ANA_CTRL_REG_1);
> +       usleep_range(150, 200);
> +
> +       /* set TX Voltage Level and TX Deemphasis to 0 */
> +       writel(0, tcphy->base + PHY_DP_TX_CTL);
> +       /* re-enable decap */
> +       writel(0x100, tcphy->base + TX_ANA_CTRL_REG_2);
> +       writel(0x300, tcphy->base + TX_ANA_CTRL_REG_2);
> +       writel(0x2008, tcphy->base + TX_ANA_CTRL_REG_1);
> +       writel(0x2018, tcphy->base + TX_ANA_CTRL_REG_1);
> +
> +       writel(0, tcphy->base + TX_ANA_CTRL_REG_5);
> +
> +       /*
> +        * Programs txda_drv_ldo_prog[15:0], Sets driver LDO
> +        * voltage 16'h1001 for DP-AUX-TX and RX
> +        */
> +       writel(0x1001, tcphy->base + TX_ANA_CTRL_REG_4);
> +
> +       /* re-enables Bandgap reference for LDO */
> +       writel(0x2098, tcphy->base + TX_ANA_CTRL_REG_1);
> +       writel(0x2198, tcphy->base + TX_ANA_CTRL_REG_1);
> +
> +       /*
> +        * re-enables the transmitter pre-driver, driver data selection MUX,
> +        * and receiver detect circuits.
> +        */
> +       writel(0x301, tcphy->base + TX_ANA_CTRL_REG_2);
> +       writel(0x303, tcphy->base + TX_ANA_CTRL_REG_2);
> +
> +       /*
> +        * BIT 12: Controls auxda_polarity, which selects the polarity of the
> +        * xcvr:
> +        * 1, Reverses the polarity (If TYPEC, Pulls ups aux_p and pull
> +        * down aux_m)
> +        * 0, Normal polarity (if TYPE_C, pulls up aux_m and pulls down
> +        * aux_p)
> +        */
> +       val = 0xa078;
> +       if (!tcphy->flip)
> +               val |= BIT(12);
> +       writel(val, tcphy->base + TX_ANA_CTRL_REG_1);
> +
> +       writel(0, tcphy->base + TX_ANA_CTRL_REG_3);
> +       writel(0, tcphy->base + TX_ANA_CTRL_REG_4);
> +       writel(0, tcphy->base + TX_ANA_CTRL_REG_5);
> +
> +       /*
> +        * Controls low_power_swing_en, set the voltage swing of the driver
> +        * to 400mv. The values below are peak to peak (differential) values.
> +        */
> +       writel(4, tcphy->base + TXDA_COEFF_CALC_CTRL);
> +       writel(0, tcphy->base + TXDA_CYA_AUXDA_CYA);
> +
> +       /* Controls tx_high_z_tm_en */
> +       val = readl(tcphy->base + TX_DIG_CTRL_REG_2);
> +       val |= BIT(15);
> +       writel(val, tcphy->base + TX_DIG_CTRL_REG_2);
> +}
> +
> +static void tcphy_usb3_init(struct rockchip_typec_phy *tcphy)
> +{
> +       struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
> +       const struct usb3phy_reg *reg = &cfg->pipe_status;
> +       int timeout;
> +       unsigned int val;
> +
> +       /* wait TCPHY for pipe ready */
> +       for (timeout = 0; timeout < 100; timeout++) {
> +               regmap_read(tcphy->grf_regs, reg->offset, &val);
> +               if (!(val & BIT(reg->enable_bit)))
> +                       return;
> +               usleep_range(10, 20);
> +       }
> +
> +       dev_err(tcphy->dev, "wait pipe ready timeout!\n");

Does this message add any value, or have an impact ? It seems to be
odd to have a dev_err() which then is ignored.

> +}
> +
> +static int tcphy_dp_init(struct rockchip_typec_phy *tcphy)
> +{
> +       struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
> +       u16 val;
> +       int ret;
> +
> +       property_enable(tcphy, &cfg->uphy_dp_sel, 1);
> +
> +       ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
> +                                val, val & BIT(6), 1000, PHY_MODE_SET_TIMEOUT);
> +       if (ret < 0) {
> +               dev_err(tcphy->dev, "failed to wait TCPHY for DP ready\n");
> +               return ret;
> +       }
> +
> +       tcphy_dp_aux_calibration(tcphy);
> +
> +       if (tcphy->mode & MODE_DFP_USB)
> +               writel(0xc101, tcphy->base + DP_MODE_CTL);
> +       else
> +               writel(0x0101, tcphy->base + DP_MODE_CTL);
> +
> +       ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
> +                                val, val & BIT(4), 1000, PHY_MODE_SET_TIMEOUT);
> +       if (ret < 0) {
> +               dev_err(tcphy->dev, "failed to wait TCPHY enter A0\n");
> +               return -EPROBE_DEFER;

If this condition results in -EPROBE_DEFER, it is not an error, and
should not result in an error message.

> +       }
> +
> +       return 0;
> +}
> +
> +static int tcphy_phy_init(struct rockchip_typec_phy *tcphy)
> +{
> +       u32 val;
> +       int ret;
> +
> +       ret = clk_prepare_enable(tcphy->clk_core);
> +       if (ret) {
> +               dev_err(tcphy->dev, "Failed to prepare_enable core clock\n");
> +               return ret;
> +       }
> +
> +       ret = clk_prepare_enable(tcphy->clk_ref);
> +       if (ret) {
> +               dev_err(tcphy->dev, "Failed to prepare_enable ref clock\n");
> +               goto err_clk_core;
> +       }
> +
> +       reset_control_deassert(tcphy->tcphy_rst);
> +
> +       tcphy_lanes_config(tcphy);
> +
> +       tcphy_cfg_pin_assign(tcphy);
> +
> +       if (tcphy->mode & MODE_DFP_DP) {
> +               if (tcphy->mode & MODE_DFP_USB)
> +                       writel(0xc104, tcphy->base + DP_MODE_CTL);
> +               else
> +                       writel(0x0104, tcphy->base + DP_MODE_CTL);
> +       }
> +
> +       reset_control_deassert(tcphy->uphy_rst);
> +
> +       ret = readx_poll_timeout(readl, tcphy->base + PMA_CMN_CTRL1,
> +                                val, val & CMN_READY, 10,
> +                                PHY_MODE_SET_TIMEOUT);
> +       if (ret < 0) {
> +               dev_err(tcphy->dev, "wait pma ready timeout\n");
> +               goto timeout_pma_ready;
> +       }
> +
> +       reset_control_deassert(tcphy->pipe_rst);
> +
> +       return 0;
> +
> +timeout_pma_ready:
> +       reset_control_assert(tcphy->tcphy_rst);
> +       reset_control_assert(tcphy->uphy_rst);
> +       clk_disable_unprepare(tcphy->clk_ref);
> +err_clk_core:
> +       clk_disable_unprepare(tcphy->clk_core);
> +       return ret;
> +}
> +
> +static void tcphy_phy_deinit(struct rockchip_typec_phy *tcphy)
> +{
> +       reset_control_assert(tcphy->tcphy_rst);
> +       reset_control_assert(tcphy->uphy_rst);
> +       reset_control_assert(tcphy->pipe_rst);
> +       clk_disable_unprepare(tcphy->clk_core);
> +       clk_disable_unprepare(tcphy->clk_ref);
> +}
> +
> +static int rockchip_typec_phy_power_on(struct phy *phy)
> +{
> +       struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
> +       struct extcon_dev *edev = tcphy->extcon;
> +       union extcon_property_value property;
> +       unsigned int id = EXTCON_USB;
> +       bool dfp, ufp, dp;
> +       u8 mode;
> +       int ret;
> +
> +       ufp = extcon_get_state(edev, EXTCON_USB);
> +       dfp = extcon_get_state(edev, EXTCON_USB_HOST);
> +       dp = extcon_get_state(edev, EXTCON_DISP_DP);
> +
> +       mode = MODE_DFP_USB;
> +       tcphy->pin_assign = PIN_ASSIGN_D;
> +
> +       if (ufp) {
> +               mode = MODE_UFP_USB;
> +               id = EXTCON_USB;
> +       } else if (dfp && dp) {
> +               mode = MODE_DFP_USB | MODE_DFP_DP;
> +               tcphy->pin_assign = PIN_ASSIGN_D;
> +       } else if (dp) {
> +               mode = MODE_DFP_DP;
> +               tcphy->pin_assign = PIN_ASSIGN_C;
> +               id = EXTCON_DISP_DP;
> +       }
> +
> +       if (tcphy->mode == mode)
> +               return 0;
> +
> +       tcphy->mode = mode;
> +       ret = extcon_get_property(edev, id, EXTCON_PROP_USB_TYPEC_POLARITY,
> +                                 &property);
> +       if (ret) {
> +               dev_err(tcphy->dev, "get property failed\n");
> +               goto err_power_on;
> +       }
> +
> +       tcphy->flip = property.intval ? 1 : 0;
> +
> +       regmap_write(tcphy->grf_regs, GRF_SOC_CON26,
> +                    DPTX_HPD_SEL_MASK | DPTX_HPD_DEL);
> +
> +       ret = tcphy_phy_init(tcphy);
> +       if (ret)
> +               goto err_power_on;
> +
> +       if (mode & (MODE_UFP_USB | MODE_DFP_USB))
> +               tcphy_usb3_init(tcphy);
> +
> +       if (mode & MODE_DFP_DP) {
> +               ret = tcphy_dp_init(tcphy);
> +               if (ret) {
> +                       tcphy_phy_deinit(tcphy);
> +                       goto err_power_on;
> +               }
> +
> +               regmap_write(tcphy->grf_regs, GRF_SOC_CON26,
> +                            DPTX_HPD_SEL_MASK | DPTX_HPD_SEL);
> +       }
> +
> +       return 0;
> +err_power_on:
> +       tcphy->mode = MODE_DISCONNECT;
> +       return ret;
> +}
> +
> +static int rockchip_typec_phy_power_off(struct phy *phy)
> +{
> +       struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
> +
> +       tcphy_phy_deinit(tcphy);
> +       tcphy->mode = MODE_DISCONNECT;
> +
> +       return 0;
> +}
> +
> +static const struct phy_ops rockchip_tcphy_ops = {
> +       .power_on       = rockchip_typec_phy_power_on,
> +       .power_off      = rockchip_typec_phy_power_off,
> +       .owner          = THIS_MODULE,
> +};
> +
> +static int tcphy_get_param(struct device *dev,
> +                          struct usb3phy_reg *reg,
> +                          const char *name)
> +{
> +       u32 buffer[3];
> +       int ret;
> +
> +       ret = of_property_read_u32_array(dev->of_node, name, buffer, 3);
> +       if (ret) {
> +               dev_err(dev, "Can not parse %s\n", name);
> +               return ret;
> +       }
> +
> +       reg->offset = buffer[0];
> +       reg->enable_bit = buffer[1];
> +       reg->write_enable = buffer[2];
> +       return 0;
> +}
> +
> +static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
> +                         struct device *dev)
> +{
> +       struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
> +       int ret;
> +
> +       ret = tcphy_get_param(dev, &cfg->typec_conn_dir,
> +                             "rockchip,typec-conn-dir");
> +       if (ret) {
> +               dev_err(dev, "could not find rockchip,typec-conn-dir node\n");
> +               return ret;
> +       }
> +
> +       ret = tcphy_get_param(dev, &cfg->usb3tousb2_en,
> +                             "rockchip,usb3tousb2-en");
> +       if (ret) {
> +               dev_err(dev, "could not find rockchip,usb3tousb2-en node\n");
> +               return ret;
> +       }
> +
> +       ret = tcphy_get_param(dev, &cfg->external_psm,
> +                             "rockchip,external-psm");
> +       if (ret) {
> +               dev_err(dev, "could not find rockchip,external-psm node\n");
> +               return ret;
> +       }
> +
> +       ret = tcphy_get_param(dev, &cfg->pipe_status,
> +                             "rockchip,pipe-status");
> +       if (ret) {
> +               dev_err(dev, "could not find rockchip,pipe-status node\n");
> +               return ret;
> +       }
> +
> +       ret = tcphy_get_param(dev, &cfg->uphy_dp_sel,
> +                             "rockchip,uphy-dp-sel");
> +       if (ret) {
> +               dev_err(dev, "could not find rockchip,uphy-dp-sel node\n");

The called function already displays an error message. I would suggest
to drop the messages here.

> +               return ret;
> +       }
> +
> +       tcphy->grf_regs = syscon_regmap_lookup_by_phandle(dev->of_node,
> +                                                         "rockchip,grf");
> +       if (IS_ERR(tcphy->grf_regs)) {
> +               dev_err(dev, "could not find grf dt node\n");
> +               return PTR_ERR(tcphy->grf_regs);
> +       }
> +
> +       tcphy->clk_core = devm_clk_get(dev, "tcpdcore");
> +       if (IS_ERR(tcphy->clk_core)) {
> +               dev_err(dev, "could not get uphy core clock\n");
> +               return PTR_ERR(tcphy->clk_core);
> +       }
> +
> +       tcphy->clk_ref = devm_clk_get(dev, "tcpdphy-ref");
> +       if (IS_ERR(tcphy->clk_ref)) {
> +               dev_err(dev, "could not get uphy ref clock\n");
> +               return PTR_ERR(tcphy->clk_ref);
> +       }
> +
> +       tcphy->uphy_rst = devm_reset_control_get(dev, "uphy");
> +       if (IS_ERR(tcphy->uphy_rst)) {
> +               dev_err(dev, "no uphy_rst reset control found\n");
> +               return PTR_ERR(tcphy->uphy_rst);
> +       }
> +
> +       tcphy->pipe_rst = devm_reset_control_get(dev, "uphy-pipe");
> +       if (IS_ERR(tcphy->pipe_rst)) {
> +               dev_err(dev, "no pipe_rst reset control found\n");
> +               return PTR_ERR(tcphy->pipe_rst);
> +       }
> +
> +       tcphy->tcphy_rst = devm_reset_control_get(dev, "uphy-tcphy");
> +       if (IS_ERR(tcphy->tcphy_rst)) {
> +               dev_err(dev, "no tcphy_rst reset control found\n");
> +               return PTR_ERR(tcphy->tcphy_rst);
> +       }
> +
> +       return 0;
> +}
> +
> +static void typec_phy_pre_init(struct rockchip_typec_phy *tcphy)
> +{
> +       struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
> +
> +       reset_control_assert(tcphy->tcphy_rst);
> +       reset_control_assert(tcphy->uphy_rst);
> +       reset_control_assert(tcphy->pipe_rst);
> +
> +       /* select external psm clock */
> +       property_enable(tcphy, &cfg->external_psm, 1);
> +       property_enable(tcphy, &cfg->usb3tousb2_en, 0);
> +
> +       tcphy->mode = MODE_DISCONNECT;
> +}
> +
> +static int rockchip_typec_phy_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct rockchip_typec_phy *tcphy;
> +       struct phy_provider *phy_provider;
> +       struct resource *res;
> +       int ret;
> +
> +       tcphy = devm_kzalloc(dev, sizeof(*tcphy), GFP_KERNEL);
> +       if (!tcphy)
> +               return -ENOMEM;
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       tcphy->base = devm_ioremap_resource(dev, res);
> +       if (IS_ERR(tcphy->base)) {
> +               dev_err(dev, "failed to remap phy regs\n");

devm_ioremap_resource() already displays detailed error messages.

> +               return PTR_ERR(tcphy->base);
> +       }
> +
> +       ret = tcphy_parse_dt(tcphy, dev);
> +       if (ret)
> +               return ret;
> +
> +       tcphy->dev = dev;
> +       platform_set_drvdata(pdev, tcphy);
> +
> +       typec_phy_pre_init(tcphy);
> +
> +       tcphy->extcon = extcon_get_edev_by_phandle(dev, 0);
> +       if (IS_ERR(tcphy->extcon)) {
> +               if (PTR_ERR(tcphy->extcon) != -EPROBE_DEFER)
> +                       dev_err(dev, "Invalid or missing extcon\n");
> +               return PTR_ERR(tcphy->extcon);
> +       }
> +
> +       tcphy->phy = devm_phy_create(dev, NULL, &rockchip_tcphy_ops);
> +       if (IS_ERR(tcphy->phy)) {
> +               dev_err(dev, "failed to create Tepy-C phy\n");
> +               return PTR_ERR(tcphy->phy);
> +       }
> +
> +       phy_set_drvdata(tcphy->phy, tcphy);
> +
> +       phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
> +       if (IS_ERR(phy_provider)) {
> +               dev_err(dev, "Failed to register phy provider\n");
> +               return PTR_ERR(phy_provider);
> +       }
> +
> +       return 0;
> +}
> +
> +static const struct of_device_id rockchip_typec_phy_dt_ids[] = {
> +       { .compatible = "rockchip,rk3399-typec-phy" },
> +       {}
> +};
> +
> +MODULE_DEVICE_TABLE(of, rockchip_typec_phy_dt_ids);
> +
> +static struct platform_driver rockchip_typec_phy_driver = {
> +       .probe          = rockchip_typec_phy_probe,
> +       .driver         = {
> +               .name   = "rockchip-typec-phy",
> +               .of_match_table = rockchip_typec_phy_dt_ids,
> +       },
> +};
> +
> +module_platform_driver(rockchip_typec_phy_driver);
> +
> +MODULE_AUTHOR("Chris Zhong <zyw at rock-chips.com>");
> +MODULE_AUTHOR("Kever Yang <kever.yang at rock-chips.com>");
> +MODULE_DESCRIPTION("Rockchip USB TYPE-C PHY driver");
> +MODULE_LICENSE("GPL v2");
> --
> 2.6.3
>


From groeck at google.com  Wed Jul 27 14:50:31 2016
From: groeck at google.com (Guenter Roeck)
Date: Wed, 27 Jul 2016 14:50:31 -0700
Subject: [v7 PATCH 4/6] arm64: dts: rockchip: add Type-C phy for RK3399
In-Reply-To: <1469624836-22792-5-git-send-email-zyw@rock-chips.com>
References: <1469624836-22792-1-git-send-email-zyw@rock-chips.com>
 <1469624836-22792-5-git-send-email-zyw@rock-chips.com>
Message-ID: <CABXOdTcr70v9QxnAh_Btvhwo4h0ZPq18T1bSAakhT5Nco-1S7Q@mail.gmail.com>

On Wed, Jul 27, 2016 at 6:07 AM, Chris Zhong <zyw at rock-chips.com> wrote:
> There are 2 Type-C phy on RK3399, they are almost same, except the
> address of register. They support USB3.0 Type-C and DisplayPort1.3
> Alt Mode on USB Type-C. Register a phy, supply it to USB3 controller
> and DP controller.
>
> Signed-off-by: Chris Zhong <zyw at rock-chips.com>

Reviewed-by: Guenter Roeck <linux at roeck-us.net>

> ---
>
> Changes in v7: None
> Changes in v6: None
> Changes in v5: None
> Changes in v4: None
> Changes in v3: None
> Changes in v2: None
> Changes in v1: None
>
>  arch/arm64/boot/dts/rockchip/rk3399.dtsi | 44 ++++++++++++++++++++++++++++++++
>  1 file changed, 44 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> index 0af03a3..7095ddf 100644
> --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> @@ -952,6 +952,50 @@
>                 };
>         };
>
> +       tcphy0: phy at ff7c0000 {
> +               compatible = "rockchip,rk3399-typec-phy";
> +               reg = <0x0 0xff7c0000 0x0 0x40000>;
> +               rockchip,grf = <&grf>;
> +               #phy-cells = <0>;
> +               clocks = <&cru SCLK_UPHY0_TCPDCORE>,
> +                        <&cru SCLK_UPHY0_TCPDPHY_REF>;
> +               clock-names = "tcpdcore", "tcpdphy-ref";
> +               assigned-clocks = <&cru SCLK_UPHY0_TCPDCORE>;
> +               assigned-clock-rates = <50000000>;
> +               resets = <&cru SRST_UPHY0>,
> +                        <&cru SRST_UPHY0_PIPE_L00>,
> +                        <&cru SRST_P_UPHY0_TCPHY>;
> +               reset-names = "uphy", "uphy-pipe", "uphy-tcphy";
> +               rockchip,typec-conn-dir = <0xe580 0 16>;
> +               rockchip,usb3tousb2-en = <0xe580 3 19>;
> +               rockchip,external-psm = <0xe588 14 30>;
> +               rockchip,pipe-status = <0xe5c0 0 0>;
> +               rockchip,uphy-dp-sel = <0x6268 19 19>;
> +               status = "disabled";
> +       };
> +
> +       tcphy1: phy at ff800000 {
> +               compatible = "rockchip,rk3399-typec-phy";
> +               reg = <0x0 0xff800000 0x0 0x40000>;
> +               rockchip,grf = <&grf>;
> +               #phy-cells = <0>;
> +               clocks = <&cru SCLK_UPHY1_TCPDCORE>,
> +                        <&cru SCLK_UPHY1_TCPDPHY_REF>;
> +               clock-names = "tcpdcore", "tcpdphy-ref";
> +               assigned-clocks = <&cru SCLK_UPHY1_TCPDCORE>;
> +               assigned-clock-rates = <50000000>;
> +               resets = <&cru SRST_UPHY1>,
> +                        <&cru SRST_UPHY1_PIPE_L00>,
> +                        <&cru SRST_P_UPHY1_TCPHY>;
> +               reset-names = "uphy", "uphy-pipe", "uphy-tcphy";
> +               rockchip,typec-conn-dir = <0xe58c 0 16>;
> +               rockchip,usb3tousb2-en = <0xe58c 3 19>;
> +               rockchip,external-psm = <0xe594 14 30>;
> +               rockchip,pipe-status = <0xe5c0 16 16>;
> +               rockchip,uphy-dp-sel = <0x6268 3 19>;
> +               status = "disabled";
> +       };
> +
>         watchdog at ff840000 {
>                 compatible = "snps,dw-wdt";
>                 reg = <0x0 0xff840000 0x0 0x100>;
> --
> 2.6.3
>


From paul.gortmaker at windriver.com  Wed Jul 27 14:57:39 2016
From: paul.gortmaker at windriver.com (Paul Gortmaker)
Date: Wed, 27 Jul 2016 17:57:39 -0400
Subject: [PATCH v7 2/2] PCI: Rockchip: Add Rockchip PCIe controller support
In-Reply-To: <20160727200443.GA30281@google.com>
References: <1468802533-17699-1-git-send-email-shawn.lin@rock-chips.com>
 <1468802533-17699-2-git-send-email-shawn.lin@rock-chips.com>
 <20160727182233.GB8901@localhost>
 <CAP=VYLqPj=81Q_8kV1wi=FHP-CB6arrXFAC0ktJrLqR-1TSCjw@mail.gmail.com>
 <20160727200443.GA30281@google.com>
Message-ID: <20160727215739.GV11410@windriver.com>

[Re: [PATCH v7 2/2] PCI: Rockchip: Add Rockchip PCIe controller support] On 27/07/2016 (Wed 13:04) Brian Norris wrote:

> + Guenter
> 
> Hi,
> 
> On Wed, Jul 27, 2016 at 03:56:36PM -0400, Paul Gortmaker wrote:
> > On Wed, Jul 27, 2016 at 2:22 PM, Bjorn Helgaas <helgaas at kernel.org> wrote:
> > > On Mon, Jul 18, 2016 at 08:42:13AM +0800, Shawn Lin wrote:
> > >> This patch adds Rockchip PCIe controller support found
> > >> on RK3399 Soc platform.
> > >>
> > >> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
> > >>
> > >> ---
> > >>
> > >> Changes in v7:
> > >> - make it as a build-in driver
> > 
> > [...]
> > 
> 
> ...
> 
> > [...]
> > 
> > >> +static struct platform_driver rockchip_pcie_driver = {
> > >> +     .driver = {
> > >> +             .name = "rockchip-pcie",
> > >> +             .of_match_table = rockchip_pcie_of_match,
> > >> +     },
> > >> +     .probe = rockchip_pcie_probe,
> > >> +
> > >> +};
> > >> +module_platform_driver(rockchip_pcie_driver);
> > >> +
> > >> +MODULE_AUTHOR("Rockchip Inc");
> > >> +MODULE_DESCRIPTION("Rockchip AXI PCIe driver");
> > >> +MODULE_LICENSE("GPL v2");
> > >
> > > Per your Kconfig, this driver cannot be a module, so remove these MODULE_*
> > > annotations.  This is to follow Paul Gortmaker's recent "Make explicitly
> > > non-modular" work.  Also change the include of <linux/module.h> to
> > > <linux/init.h>.
> > 
> > Thanks!  You beat me to it.    I didn't see any EXPORT_SYMBOL so it doesn't
> > look like we need export.h  added here.   Also, at the risk of stating
> > the obvious,
> > the module_platform_driver becomes builtin_platform_driver.
> 
> Just for reference (not to necessarily disagree here): we've kinda
> ping-ponged on this one. I suggested it could be built as a module, so
> Shawn added it, but apparently we're missing an export:
> 
> https://lkml.org/lkml/2016/7/8/704
> 
> so he reverted back. It's probably best to just leave this built-in and
> resolve the comments Paul and Bjorn made, to avoid too much more
> flip-flopping. If we still want to patch up the __weak
> pci_remap_iospace(), then we can make this a module at a later time.

Agreed, and this was the general consensus we came to for other PCI code
as well.   It might be different if we were deleting hundreds of lines
of code, but we are just making a few simple changes to make it clear
that things aren't modular, and that ensures others don't continue to
copy the same inconsistency into their new driver(s).  It doesn't in any
way prevent a future conversion to tristate if that use case makes sense.

THanks,
Paul.
--

> 
> Regards,
> Brian


From groeck at google.com  Wed Jul 27 15:24:51 2016
From: groeck at google.com (Guenter Roeck)
Date: Wed, 27 Jul 2016 15:24:51 -0700
Subject: [v7 PATCH 6/6] drm/rockchip: cdn-dp: add cdn DP support for rk3399
In-Reply-To: <1469624836-22792-7-git-send-email-zyw@rock-chips.com>
References: <1469624836-22792-1-git-send-email-zyw@rock-chips.com>
 <1469624836-22792-7-git-send-email-zyw@rock-chips.com>
Message-ID: <CABXOdTcMiKnz8-kVB2JiFwxejVOBM0QbrQvigVyE3g58sP34ow@mail.gmail.com>

On Wed, Jul 27, 2016 at 6:07 AM, Chris Zhong <zyw at rock-chips.com> wrote:
> Add support for cdn DP controller which is embedded in the rk3399
> SoCs. The DP is compliant with DisplayPort Specification,
> Version 1.3, This IP is compatible with the rockchip type-c PHY IP.
> There is a uCPU in DP controller, it need a firmware to work,
> please put the firmware file to /lib/firmware/rockchip/dptx.bin. The
> uCPU in charge of aux communication and link training, the host use
> mailbox to communicate with the ucpu.
> The dclk pin_pol of vop must not be invert for DP.
>
> Signed-off-by: Chris Zhong <zyw at rock-chips.com>
> Reviewed-by: Sean Paul <seanpaul at chromium.org>
> Acked-by: Mark Yao <mark.yao at rock-chips.com>
>
> ---
>
> Changes in v7:
> - support firmware standby when no dptx connection
> - optimization the calculation of tu size and valid symbol
>
> Changes in v6:
> - add a port struct
> - select SND_SOC_HDMI_CODEC
> - force reset the phy when hpd detected
>
> Changes in v5:
> - alphabetical order
> - do not use long, use u32 or u64
> - return MODE_CLOCK_HIGH when requested > actual
> - Optimized Coding Style
> - add a formula to get better tu size and symbol value.
> - modify according to Sean Paul's comments
> - fixed the fw_wait always 0
>
> Changes in v4:
> - use phy framework to control DP phy
> - support 2 phys
>
> Changes in v3:
> - use EXTCON_DISP_DP and EXTCON_DISP_DP_ALT cable to get dp port state.
> - reset spdif before config it
> - modify the firmware clk to 100Mhz
> - retry load firmware if fw file is requested too early
>
> Changes in v2:
> - Alphabetic order
> - remove excess error message
> - use define clk_rate
> - check all return value
> - remove dev_set_name(dp->dev, "cdn-dp");
> - use schedule_delayed_work
> - remove never-called functions
> - remove some unnecessary ()
>
> Changes in v1:
> - use extcon API
> - use hdmi-codec for the DP Asoc
> - do not initialize the "ret"
> - printk a err log when drm_of_encoder_active_endpoint_id
> - modify the dclk pin_pol to a single line
>
>  drivers/gpu/drm/rockchip/Kconfig            |  10 +
>  drivers/gpu/drm/rockchip/Makefile           |   1 +
>  drivers/gpu/drm/rockchip/cdn-dp-core.c      | 877 ++++++++++++++++++++++++++
>  drivers/gpu/drm/rockchip/cdn-dp-core.h      | 103 ++++
>  drivers/gpu/drm/rockchip/cdn-dp-reg.c       | 916 ++++++++++++++++++++++++++++
>  drivers/gpu/drm/rockchip/cdn-dp-reg.h       | 482 +++++++++++++++
>  drivers/gpu/drm/rockchip/rockchip_drm_vop.c |  13 +-
>  drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   9 +
>  drivers/gpu/drm/rockchip/rockchip_vop_reg.c |   2 +
>  9 files changed, 2410 insertions(+), 3 deletions(-)
>  create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.c
>  create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.h
>  create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.c
>  create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.h
>
> diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
> index d30bdc3..20aaafe 100644
> --- a/drivers/gpu/drm/rockchip/Kconfig
> +++ b/drivers/gpu/drm/rockchip/Kconfig
> @@ -25,6 +25,16 @@ config ROCKCHIP_ANALOGIX_DP
>           for the Analogix Core DP driver. If you want to enable DP
>           on RK3288 based SoC, you should selet this option.
>
> +config ROCKCHIP_CDN_DP
> +        tristate "Rockchip cdn DP"
> +        depends on DRM_ROCKCHIP
> +       select SND_SOC_HDMI_CODEC if SND_SOC
> +        help
> +         This selects support for Rockchip SoC specific extensions
> +         for the cdn DP driver. If you want to enable Dp on
> +         RK3399 based SoC, you should select this
> +         option.
> +
>  config ROCKCHIP_DW_HDMI
>          tristate "Rockchip specific extensions for Synopsys DW HDMI"
>          depends on DRM_ROCKCHIP
> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
> index 05d0713..abdecd5 100644
> --- a/drivers/gpu/drm/rockchip/Makefile
> +++ b/drivers/gpu/drm/rockchip/Makefile
> @@ -7,6 +7,7 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
>  rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
>
>  obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
> +obj-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
>  obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
>  obj-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
>  obj-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
> new file mode 100644
> index 0000000..31d146c
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
> @@ -0,0 +1,877 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author: Chris Zhong <zyw at rock-chips.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * 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 <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_dp_helper.h>
> +#include <drm/drm_edid.h>
> +#include <drm/drm_of.h>
> +
> +#include <linux/clk.h>
> +#include <linux/component.h>
> +#include <linux/extcon.h>
> +#include <linux/firmware.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/phy/phy.h>
> +

Any chance for alphabetic order ?

> +#include <sound/hdmi-codec.h>
> +
> +#include "cdn-dp-core.h"
> +#include "cdn-dp-reg.h"
> +#include "rockchip_drm_vop.h"
> +
> +#define connector_to_dp(c) \
> +               container_of(c, struct cdn_dp_device, connector)
> +
> +#define encoder_to_dp(c) \
> +               container_of(c, struct cdn_dp_device, encoder)
> +
> +/* dp grf register offset */
> +#define DP_VOP_SEL             0x6224
> +#define DP_SEL_VOP_LIT         BIT(12)
> +#define MAX_FW_WAIT_SECS       64
> +#define CDN_DP_FIRMWARE                "rockchip/dptx.bin"
> +
> +struct cdn_dp_data {
> +       u8 max_phy;
> +};
> +
> +struct cdn_dp_data rk3399_cdn_dp = {
> +       .max_phy = 2,
> +};
> +
> +static const struct of_device_id cdn_dp_dt_ids[] = {
> +       { .compatible = "rockchip,rk3399-cdn-dp",
> +               .data = (void *)&rk3399_cdn_dp },
> +       {}
> +};
> +
> +MODULE_DEVICE_TABLE(of, cdn_dp_dt_ids);
> +
> +static int cdn_dp_clk_enable(struct cdn_dp_device *dp)
> +{
> +       int ret;
> +       u32 rate;
> +
> +       ret = clk_prepare_enable(dp->pclk);
> +       if (ret < 0) {
> +               dev_err(dp->dev, "cannot enable dp pclk %d\n", ret);
> +               goto err_pclk;
> +       }
> +
> +       ret = clk_prepare_enable(dp->core_clk);
> +       if (ret < 0) {
> +               dev_err(dp->dev, "cannot enable core_clk %d\n", ret);
> +               goto err_core_clk;
> +       }
> +
> +       rate = clk_get_rate(dp->core_clk);
> +       if (rate < 0) {
> +               dev_err(dp->dev, "get clk rate failed: %d\n", rate);
> +               goto err_set_rate;
> +       }
> +
> +       cdn_dp_set_fw_clk(dp, rate);
> +
> +       return 0;
> +
> +err_set_rate:
> +       clk_disable_unprepare(dp->core_clk);
> +err_core_clk:
> +       clk_disable_unprepare(dp->pclk);
> +err_pclk:
> +       return ret;
> +}
> +
> +static enum drm_connector_status
> +cdn_dp_connector_detect(struct drm_connector *connector, bool force)
> +{
> +       struct cdn_dp_device *dp = connector_to_dp(connector);
> +
> +       return dp->hpd_status;
> +}
> +
> +static void cdn_dp_connector_destroy(struct drm_connector *connector)
> +{
> +       drm_connector_unregister(connector);
> +       drm_connector_cleanup(connector);
> +}
> +
> +static struct drm_connector_funcs cdn_dp_atomic_connector_funcs = {
> +       .dpms = drm_atomic_helper_connector_dpms,
> +       .detect = cdn_dp_connector_detect,
> +       .destroy = cdn_dp_connector_destroy,
> +       .fill_modes = drm_helper_probe_single_connector_modes,
> +       .reset = drm_atomic_helper_connector_reset,
> +       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> +       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +static int cdn_dp_connector_get_modes(struct drm_connector *connector)
> +{
> +       struct cdn_dp_device *dp = connector_to_dp(connector);
> +       struct edid *edid;
> +       int ret = 0;
> +
> +       edid = drm_do_get_edid(connector, cdn_dp_get_edid_block, dp);
> +       if (edid) {
> +               dev_dbg(dp->dev, "got edid: width[%d] x height[%d]\n",
> +                       edid->width_cm, edid->height_cm);
> +
> +               dp->sink_has_audio = drm_detect_monitor_audio(edid);
> +               ret = drm_add_edid_modes(connector, edid);
> +               if (ret) {
> +                       drm_mode_connector_update_edid_property(connector,
> +                                                               edid);
> +                       drm_edid_to_eld(connector, edid);
> +               }
> +               kfree(edid);
> +       } else {
> +               dev_warn(dp->dev, "failed to get edid\n");

Does this indicate a problem, and does the message add any value to
the user ? I am seeing it a lot, but I never know if it is something
to be concerned about or not.

If the message does not add value and/or indicate a real problem,
maybe it should be removed ?

> +       }
> +
> +       return ret;
> +}
> +
> +static struct drm_encoder *
> +       cdn_dp_connector_best_encoder(struct drm_connector *connector)
> +{
> +       struct cdn_dp_device *dp = connector_to_dp(connector);
> +
> +       return &dp->encoder;
> +}
> +
> +static int cdn_dp_connector_mode_valid(struct drm_connector *connector,
> +                                      struct drm_display_mode *mode)
> +{
> +       struct cdn_dp_device *dp = connector_to_dp(connector);
> +       struct drm_display_info *display_info = &dp->connector.display_info;
> +       u32 requested = mode->clock * display_info->bpc * 3 / 1000;
> +       u32 sink_max, source_max, rate;
> +       u8 lanes, i;
> +       u32 actual;
> +
> +       /* find the running port */
> +       for (i = 0; i < dp->ports; i++) {
> +               if (dp->port[i]->phy_status == 1) {

phy_status is defined as bool, so comparisons against 1 (or 0) are unnecessary.
Also, you should use true / false for assignments to it.

> +                       source_max = dp->port[i]->cap_lanes;
> +                       break;
> +               }
> +       }

If this code doesn't find a valid port, source_max will be undefined.
Maybe initialize source_max with 0 ?

> +
> +       sink_max = drm_dp_max_lane_count(dp->dpcd);
> +       lanes = min(source_max, sink_max);
> +
> +       source_max = drm_dp_bw_code_to_link_rate(CDN_DP_MAX_LINK_RATE);
> +       sink_max = drm_dp_max_link_rate(dp->dpcd);
> +       rate = min(source_max, sink_max);
> +
> +       actual = rate * lanes / 100;
> +
> +       /* efficiency is about 0.8 */
> +       actual = actual * 8 / 10;
> +
> +       if (requested > actual) {
> +               dev_dbg(dp->dev, "requested=%d, actual=%d, clock=%d\n",
> +                       requested, actual, mode->clock);
> +               return MODE_CLOCK_HIGH;
> +       }
> +
> +       return MODE_OK;
> +}
> +
> +static struct drm_connector_helper_funcs cdn_dp_connector_helper_funcs = {
> +       .get_modes = cdn_dp_connector_get_modes,
> +       .best_encoder = cdn_dp_connector_best_encoder,
> +       .mode_valid = cdn_dp_connector_mode_valid,
> +};
> +
> +static void cdn_dp_commit(struct drm_encoder *encoder)
> +{
> +       struct cdn_dp_device *dp = encoder_to_dp(encoder);
> +       int ret;
> +
> +       ret = cdn_dp_training_start(dp);
> +       if (ret) {
> +               dev_err(dp->dev, "link training failed: %d\n", ret);

An error which is then ignored ? Does this make sense ?

> +               return;
> +       }
> +
> +       ret = cdn_dp_get_training_status(dp);
> +       if (ret) {
> +               dev_err(dp->dev, "get link training status failed: %d\n", ret);
> +               return;
> +       }
> +
> +       dev_info(dp->dev, "rate:0x%x, lanes:%d\n",
> +                dp->link.rate, dp->link.num_lanes);

dev_dbg for the final version, probably.

> +
> +       if (cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE))
> +               return;
> +
> +       if (cdn_dp_config_video(dp)) {
> +               dev_err(dp->dev, "unable to config video\n");
> +               return;
> +       }
> +
> +       if (cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID))
> +               return;
> +
> +       dp->dpms_mode = DRM_MODE_DPMS_ON;
> +}
> +
> +static void cdn_dp_encoder_mode_set(struct drm_encoder *encoder,
> +                                   struct drm_display_mode *mode,
> +                                   struct drm_display_mode *adjusted)
> +{
> +       struct cdn_dp_device *dp = encoder_to_dp(encoder);
> +       struct drm_display_info *display_info = &dp->connector.display_info;
> +       struct rockchip_crtc_state *state;
> +       struct video_info *video = &dp->video_info;
> +       int ret, val;
> +
> +       switch (display_info->bpc) {
> +       case 16:
> +       case 12:
> +       case 10:
> +               video->color_depth = 10;
> +               break;
> +       case 6:
> +               video->color_depth = 6;
> +               break;
> +       default:
> +               video->color_depth = 8;
> +               break;
> +       }
> +
> +       video->color_fmt = PXL_RGB;
> +
> +       video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC);
> +       video->h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC);
> +
> +       ret = drm_of_encoder_active_endpoint_id(dp->dev->of_node, encoder);
> +       if (ret < 0) {
> +               dev_err(dp->dev, "Could not get vop id, %d", ret);
> +               return;
> +       }
> +
> +       dev_dbg(dp->dev, "vop %s output to cdn-dp\n", (ret) ? "LIT" : "BIG");
> +       state = to_rockchip_crtc_state(encoder->crtc->state);
> +       if (ret) {
> +               val = DP_SEL_VOP_LIT | (DP_SEL_VOP_LIT << 16);
> +               state->output_mode = ROCKCHIP_OUT_MODE_P888;
> +       } else {
> +               val = DP_SEL_VOP_LIT << 16;
> +               state->output_mode = ROCKCHIP_OUT_MODE_AAAA;
> +       }
> +       ret = regmap_write(dp->grf, DP_VOP_SEL, val);
> +       if (ret != 0)
> +               dev_err(dp->dev, "Could not write to GRF: %d\n", ret);
> +
> +       memcpy(&dp->mode, adjusted, sizeof(*mode));
> +}
> +
> +static void cdn_dp_encoder_enable(struct drm_encoder *encoder)
> +{
> +       struct cdn_dp_device *dp = encoder_to_dp(encoder);
> +
> +       if (dp->dpms_mode != DRM_MODE_DPMS_ON)
> +               cdn_dp_commit(encoder);
> +}
> +
> +static void cdn_dp_encoder_disable(struct drm_encoder *encoder)
> +{
> +       struct cdn_dp_device *dp = encoder_to_dp(encoder);
> +
> +       if (dp->dpms_mode != DRM_MODE_DPMS_OFF) {
> +               cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE);
> +               dp->dpms_mode = DRM_MODE_DPMS_OFF;
> +       }
> +}
> +
> +static int
> +cdn_dp_encoder_atomic_check(struct drm_encoder *encoder,
> +                           struct drm_crtc_state *crtc_state,
> +                           struct drm_connector_state *conn_state)
> +{
> +       struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
> +
> +       s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
> +       s->output_type = DRM_MODE_CONNECTOR_DisplayPort;
> +
> +       return 0;
> +}
> +
> +static struct drm_encoder_helper_funcs cdn_dp_encoder_helper_funcs = {
> +       .mode_set = cdn_dp_encoder_mode_set,
> +       .enable = cdn_dp_encoder_enable,
> +       .disable = cdn_dp_encoder_disable,
> +       .atomic_check = cdn_dp_encoder_atomic_check,
> +};
> +
> +static struct drm_encoder_funcs cdn_dp_encoder_funcs = {
> +       .destroy = drm_encoder_cleanup,
> +};
> +
> +static int cdn_dp_firmware_init(struct cdn_dp_device *dp)
> +{
> +       int ret;
> +       const u32 *iram_data, *dram_data;
> +       const struct firmware *fw = dp->fw;
> +       const struct cdn_firmware_header *hdr;
> +
> +       hdr = (struct cdn_firmware_header *)fw->data;
> +       if (fw->size != le32_to_cpu(hdr->size_bytes)) {
> +               dev_err(dp->dev, "firmware is invalid\n");
> +               return -EINVAL;
> +       }
> +
> +       iram_data = (const u32 *)(fw->data + hdr->header_size);
> +       dram_data = (const u32 *)(fw->data + hdr->header_size + hdr->iram_size);
> +
> +       ret = cdn_dp_load_firmware(dp, iram_data, hdr->iram_size,
> +                                  dram_data, hdr->dram_size);
> +       if (ret)
> +               return ret;
> +
> +       ret = cdn_dp_active(dp, true);
> +       if (ret) {
> +               dev_err(dp->dev, "active ucpu failed: %d\n", ret);
> +               return ret;
> +       }
> +
> +       dp->fw_loaded = 1;
> +
> +       return cdn_dp_event_config(dp);
> +}
> +
> +static int cdn_dp_init(struct cdn_dp_device *dp)
> +{
> +       struct device *dev = dp->dev;
> +       struct device_node *np = dev->of_node;
> +       struct platform_device *pdev = to_platform_device(dev);
> +       struct resource *res;
> +       int ret;
> +
> +       dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
> +       if (IS_ERR(dp->grf)) {
> +               dev_err(dev, "cdn-dp needs rockchip,grf property\n");
> +               return PTR_ERR(dp->grf);
> +       }
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       dp->regs = devm_ioremap_resource(dev, res);
> +       if (IS_ERR(dp->regs)) {
> +               dev_err(dev, "ioremap reg failed\n");
> +               return PTR_ERR(dp->regs);
> +       }
> +
> +       dp->core_clk = devm_clk_get(dev, "core-clk");
> +       if (IS_ERR(dp->core_clk)) {
> +               dev_err(dev, "cannot get core_clk_dp\n");
> +               return PTR_ERR(dp->core_clk);
> +       }
> +
> +       dp->pclk = devm_clk_get(dev, "pclk");
> +       if (IS_ERR(dp->pclk)) {
> +               dev_err(dev, "cannot get pclk\n");
> +               return PTR_ERR(dp->pclk);
> +       }
> +
> +       dp->spdif_clk = devm_clk_get(dev, "spdif");
> +       if (IS_ERR(dp->spdif_clk)) {
> +               dev_err(dev, "cannot get spdif_clk\n");
> +               return PTR_ERR(dp->spdif_clk);
> +       }
> +
> +       dp->spdif_rst = devm_reset_control_get(dev, "spdif");
> +       if (IS_ERR(dp->spdif_rst)) {
> +               dev_err(dev, "no spdif reset control found\n");
> +               return PTR_ERR(dp->spdif_rst);
> +       }
> +
> +       dp->dpms_mode = DRM_MODE_DPMS_OFF;
> +
> +       ret = cdn_dp_clk_enable(dp);
> +       if (ret < 0)
> +               return ret;
> +
> +       cdn_dp_clock_reset(dp);
> +
> +       return 0;
> +}
> +
> +static int cdn_dp_audio_hw_params(struct device *dev,  void *data,
> +                                 struct hdmi_codec_daifmt *daifmt,
> +                                 struct hdmi_codec_params *params)
> +{
> +       struct cdn_dp_device *dp = dev_get_drvdata(dev);
> +       struct audio_info audio = {
> +               .sample_width = params->sample_width,
> +               .sample_rate = params->sample_rate,
> +               .channels = params->channels,
> +       };
> +       int ret;
> +
> +       if (!dp->encoder.crtc)
> +               return -ENODEV;
> +
> +       switch (daifmt->fmt) {
> +       case HDMI_I2S:
> +               audio.format = AFMT_I2S;
> +               break;
> +       case HDMI_SPDIF:
> +               audio.format = AFMT_SPDIF;
> +               break;
> +       default:
> +               dev_err(dev, "%s: Invalid format %d\n", __func__, daifmt->fmt);
> +               return -EINVAL;
> +       }
> +
> +       ret = cdn_dp_audio_config_set(dp, &audio);
> +       if (!ret)
> +               dp->audio_info = audio;
> +
> +       return ret;
> +}
> +
> +static void cdn_dp_audio_shutdown(struct device *dev, void *data)
> +{
> +       struct cdn_dp_device *dp = dev_get_drvdata(dev);
> +       int ret = cdn_dp_audio_stop(dp, &dp->audio_info);
> +
> +       if (!ret)
> +               dp->audio_info.format = AFMT_UNUSED;
> +}
> +
> +static int cdn_dp_audio_digital_mute(struct device *dev, void *data,
> +                                    bool enable)
> +{
> +       struct cdn_dp_device *dp = dev_get_drvdata(dev);
> +
> +       return cdn_dp_audio_mute(dp, enable);
> +}
> +
> +static int cdn_dp_audio_get_eld(struct device *dev, void *data,
> +                               uint8_t *buf, size_t len)

checkpatch suggests to use u8.

> +{
> +       struct cdn_dp_device *dp = dev_get_drvdata(dev);
> +       struct drm_mode_config *config = &dp->encoder.dev->mode_config;
> +       struct drm_connector *connector;
> +       int ret = -ENODEV;
> +
> +       mutex_lock(&config->mutex);
> +       list_for_each_entry(connector, &config->connector_list, head) {
> +               if (&dp->encoder == connector->encoder) {
> +                       memcpy(buf, connector->eld,
> +                              min(sizeof(connector->eld), len));
> +                       ret = 0;
> +               }
> +       }
> +       mutex_unlock(&config->mutex);
> +
> +       return ret;
> +}
> +
> +static const struct hdmi_codec_ops audio_codec_ops = {
> +       .hw_params = cdn_dp_audio_hw_params,
> +       .audio_shutdown = cdn_dp_audio_shutdown,
> +       .digital_mute = cdn_dp_audio_digital_mute,
> +       .get_eld = cdn_dp_audio_get_eld,
> +};
> +
> +static int cdn_dp_audio_codec_init(struct cdn_dp_device *dp,
> +                                  struct device *dev)
> +{
> +       struct hdmi_codec_pdata codec_data = {
> +               .i2s = 1,
> +               .spdif = 1,
> +               .ops = &audio_codec_ops,
> +               .max_i2s_channels = 8,
> +       };
> +
> +       dp->audio_pdev = platform_device_register_data(
> +                        dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO,
> +                        &codec_data, sizeof(codec_data));
> +
> +       return PTR_ERR_OR_ZERO(dp->audio_pdev);
> +}
> +
> +static int cdn_dp_get_cap_lanes(struct cdn_dp_device *dp,
> +                               struct extcon_dev *edev)
> +{
> +       bool dfp, dptx;
> +       u8 lanes;
> +
> +       dfp = extcon_get_cable_state_(edev, EXTCON_USB_HOST);
> +       dptx = extcon_get_cable_state_(edev, EXTCON_DISP_DP);
> +
> +       if (dfp && dptx)
> +               lanes = 2;
> +       else if (dptx)
> +               lanes = 4;
> +       else
> +               lanes = 0;
> +
> +       return lanes;
> +}
> +
> +static int cdn_dp_phy_power_ctrl(struct cdn_dp_device *dp,
> +                                struct cdn_dp_port *port, bool enable)
> +{
> +       struct phy *phy = port->phy;
> +       int ret;
> +       int count = 0;
> +
> +       while (phy->power_count > 0) {
> +               ret = phy_power_off(phy);
> +               if (ret)
> +                       return ret;
> +               count++;
> +       }
> +
> +       count = enable ? count + 1 : count - 1;
> +       while (count-- > 0) {
> +               ret = phy_power_on(phy);
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       port->phy_status = enable;
> +       return 0;
> +}
> +
> +static int cdn_dp_pd_event(struct notifier_block *nb,
> +                          unsigned long event, void *priv)
> +{
> +       struct cdn_dp_port *port;
> +
> +       port = container_of(nb, struct cdn_dp_port, event_nb);
> +
> +       schedule_delayed_work(&port->event_wq, 0);
> +
> +       return 0;
> +}
> +
> +static void cdn_dp_pd_event_wq(struct work_struct *work)
> +{
> +       struct cdn_dp_port *port = container_of(work, struct cdn_dp_port,
> +                                               event_wq.work);
> +       struct cdn_dp_device *dp = port->dp;
> +       u8 new_cap_lanes;
> +       int ret;
> +       u8 i;
> +
> +       new_cap_lanes = cdn_dp_get_cap_lanes(dp, port->extcon);
> +
> +       if (new_cap_lanes == port->cap_lanes) {
> +               dev_warn(dp->dev, "lanes count does not change: %d\n",
> +                        new_cap_lanes);

Why dev_warn ? Or why a message in the first place ?

> +               return;
> +       }
> +
> +       if (!new_cap_lanes) {
> +               ret = cdn_dp_phy_power_ctrl(dp, port, 0);
> +               if (ret) {
> +                       dev_err(dp->dev, "phy power off failed: %d", ret);
> +                       return;
> +               }
> +               port->phy_status = 0;
> +               port->cap_lanes = new_cap_lanes;
> +               for (i = 0; i < dp->ports; i++) {
> +                       if (dp->port[i]->phy_status == 1)
> +                               return;
> +               }
> +
> +               dp->hpd_status = connector_status_disconnected;
> +               drm_helper_hpd_irq_event(dp->drm_dev);
> +               cdn_dp_active(dp, false);
> +               return;
> +       }
> +
> +       /* if other phy is running, do not touch the hpd_status, and return */
> +       for (i = 0; i < dp->ports; i++) {
> +               if (dp->port[i]->phy_status == 1) {
> +                       dev_warn(dp->dev, "busy, phy[%d] is running",
> +                                dp->port[i]->id);
> +                       return;
> +               }
> +       }
> +
> +       if (!dp->fw_loaded) {
> +               ret = request_firmware(&dp->fw, CDN_DP_FIRMWARE, dp->dev);
> +               if (ret == -ENOENT && dp->fw_wait <= MAX_FW_WAIT_SECS) {
> +                       unsigned long time = msecs_to_jiffies(dp->fw_wait * HZ);
> +
> +                       /*
> +                        * If can not find the file, retry to load the firmware
> +                        * in 1 second, if still failed after 1 minute, give up.

Comment (1 second) is no longer valid.

> +                        */
> +                       schedule_delayed_work(&port->event_wq, time);
> +                       dev_warn(dp->dev, "retry to request firmware in %dS\n",
> +                                dp->fw_wait);

I think this message should be dropped.

> +                       dp->fw_wait *= 2;
> +                       return;
> +               } else if (ret) {
> +                       dev_err(dp->dev, "failed to request firmware: %d\n",
> +                               ret);
> +                       return;
> +               }

Might move the return statement to this location.

> +       }
> +
> +       if (dp->fw_loaded)
> +               cdn_dp_active(dp, true);
> +
> +       ret = cdn_dp_phy_power_ctrl(dp, port, 1);
> +       if (ret) {
> +               if (!dp->fw_loaded)
> +                       release_firmware(dp->fw);
> +               dev_err(dp->dev, "phy power on failed: %d\n", ret);
> +               return;
> +       }
> +
> +       if (!dp->fw_loaded) {
> +               ret = cdn_dp_firmware_init(dp);
> +               release_firmware(dp->fw);
> +               if (ret) {
> +                       dev_err(dp->dev, "firmware init failed: %d", ret);
> +                       goto err_firmware;
> +               }
> +       }
> +
> +       /* read hpd status failed, or the hpd does not exist. */
> +       ret = cdn_dp_get_hpd_status(dp);
> +       if (ret <= 0) {
> +               dev_err(dp->dev, "get hpd failed: %d", ret);
> +               goto err_firmware;
> +       }
> +
> +       ret = cdn_dp_set_host_cap(dp, new_cap_lanes);
> +       if (ret) {
> +               dev_err(dp->dev, "set host capabilities failed: %d\n", ret);
> +               goto err_firmware;
> +       }
> +
> +       /*
> +        * Native read with retry for link status and receiver capability reads
> +        * for cases where the sink may still be asleep.
> +        *
> +        * Sinks are *supposed* to come up within 1ms from an off state, but
> +        * we're also supposed to retry 3 times per the spec.
> +        */
> +       for (i = 0; i < 3; i++) {
> +               ret = cdn_dp_dpcd_read(dp, 0x000, dp->dpcd,
> +                                      DP_RECEIVER_CAP_SIZE);
> +               if (!ret) {
> +                       dev_dbg(dp->dev, "get dpcd success!\n");
> +                       port->cap_lanes = new_cap_lanes;
> +                       dp->hpd_status = connector_status_connected;
> +                       drm_helper_hpd_irq_event(dp->drm_dev);
> +                       return;
> +               }
> +               usleep_range(1000, 1050);
> +       }
> +
> +       dev_err(dp->dev, "get dpcd failed: %d", ret);
> +
> +err_firmware:
> +       ret = cdn_dp_phy_power_ctrl(dp, port, 0);

The last parameter of cdn_dp_phy_power_ctrl() is bool, so this should
pass 'false' as parameter.
Same really everywhere else - instead of 0/1, use true / false for booleans.

> +       if (ret)
> +               dev_err(dp->dev, "phy power off failed: %d", ret);
> +       if (dp->fw_loaded)
> +               cdn_dp_active(dp, false);
> +}
> +
> +static int cdn_dp_bind(struct device *dev, struct device *master,
> +                      void *data)

Unnecessary line split.

> +{
> +       struct cdn_dp_device *dp = dev_get_drvdata(dev);
> +       struct drm_encoder *encoder;
> +       struct drm_connector *connector;
> +       struct cdn_dp_port *port;
> +       struct drm_device *drm_dev = data;
> +       int ret, i;
> +
> +       ret = cdn_dp_init(dp);
> +       if (ret < 0)
> +               return ret;
> +
> +       dp->drm_dev = drm_dev;
> +       dp->hpd_status = connector_status_disconnected;
> +       dp->fw_wait = 1;
> +
> +       encoder = &dp->encoder;
> +
> +       encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
> +                                                            dev->of_node);
> +       DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
> +
> +       ret = drm_encoder_init(drm_dev, encoder, &cdn_dp_encoder_funcs,
> +                              DRM_MODE_ENCODER_TMDS, NULL);
> +       if (ret) {
> +               DRM_ERROR("failed to initialize encoder with drm\n");
> +               return ret;
> +       }
> +
> +       drm_encoder_helper_add(encoder, &cdn_dp_encoder_helper_funcs);
> +
> +       connector = &dp->connector;
> +       connector->polled = DRM_CONNECTOR_POLL_HPD;
> +       connector->dpms = DRM_MODE_DPMS_OFF;
> +
> +       ret = drm_connector_init(drm_dev, connector,
> +                                &cdn_dp_atomic_connector_funcs,
> +                                DRM_MODE_CONNECTOR_DisplayPort);
> +       if (ret) {
> +               DRM_ERROR("failed to initialize connector with drm\n");
> +               goto err_free_encoder;
> +       }
> +
> +       drm_connector_helper_add(connector, &cdn_dp_connector_helper_funcs);
> +
> +       ret = drm_mode_connector_attach_encoder(connector, encoder);
> +       if (ret) {
> +               DRM_ERROR("failed to attach connector and encoder\n");
> +               goto err_free_connector;
> +       }
> +
> +       cdn_dp_audio_codec_init(dp, dev);
> +
> +

Double empty line.


> +       for (i = 0; i < dp->ports; i++) {
> +               port = dp->port[i];
> +
> +               port->event_nb.notifier_call = cdn_dp_pd_event;
> +               INIT_DELAYED_WORK(&port->event_wq, cdn_dp_pd_event_wq);
> +               ret = extcon_register_notifier(port->extcon, EXTCON_DISP_DP,
> +                                              &port->event_nb);
> +               if (ret) {
> +                       dev_err(dev, "regitster EXTCON_DISP_DP notifier err\n");

register

> +                       return ret;
> +               }
> +
> +               if (extcon_get_cable_state_(port->extcon, EXTCON_DISP_DP))

extcon_get_state()


> +                       schedule_delayed_work(&port->event_wq, 0);
> +       }
> +
> +       return 0;
> +
> +err_free_connector:
> +       drm_connector_cleanup(connector);
> +err_free_encoder:
> +       drm_encoder_cleanup(encoder);
> +       return ret;
> +}
> +
> +static void cdn_dp_unbind(struct device *dev, struct device *master, void *data)
> +{
> +       struct cdn_dp_device *dp = dev_get_drvdata(dev);
> +       struct drm_encoder *encoder = &dp->encoder;
> +       struct drm_connector *connector = &dp->connector;
> +       struct cdn_dp_port *port;
> +       int i;
> +
> +       platform_device_unregister(dp->audio_pdev);
> +       cdn_dp_encoder_disable(encoder);
> +       encoder->funcs->destroy(encoder);
> +       connector->funcs->destroy(connector);
> +
> +       for (i = 0; i < dp->ports; i++) {
> +               port = dp->port[i];
> +               extcon_unregister_notifier(port->extcon, EXTCON_DISP_DP,
> +                                          &port->event_nb);
> +       }
> +}
> +
> +static const struct component_ops cdn_dp_component_ops = {
> +       .bind = cdn_dp_bind,
> +       .unbind = cdn_dp_unbind,
> +};
> +
> +static int cdn_dp_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       const struct of_device_id *match;
> +       struct cdn_dp_data *dp_data;
> +       struct cdn_dp_port *port;
> +       struct cdn_dp_device *dp;
> +       struct extcon_dev *extcon;
> +       struct phy *phy;
> +       int i;
> +
> +       dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
> +       if (!dp)
> +               return -ENOMEM;
> +       dp->dev = dev;
> +
> +       match = of_match_node(cdn_dp_dt_ids, pdev->dev.of_node);
> +       dp_data = (struct cdn_dp_data *)match->data;

match->data is declared as void *, thus the typecast is not necessary,

> +
> +       for (i = 0; i < dp_data->max_phy; i++) {
> +               extcon = extcon_get_edev_by_phandle(dev, i);
> +               phy = devm_of_phy_get_by_index(dev, dev->of_node, i);
> +
> +               if (PTR_ERR(extcon) == -EPROBE_DEFER ||
> +                   PTR_ERR(phy) == -EPROBE_DEFER)
> +                       return -EPROBE_DEFER;
> +
> +               if (IS_ERR(extcon) || IS_ERR(phy))
> +                       continue;
> +
> +               port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
> +               if (!dp)
> +                       return -ENOMEM;
> +
> +               port->extcon = extcon;
> +               port->phy = phy;
> +               port->dp = dp;
> +               port->id = i;
> +               dp->port[dp->ports++] = port;
> +       }
> +
> +       if (!dp->ports) {
> +               dev_err(dev, "missing extcon or phy\n");
> +               return -EINVAL;
> +       }
> +
> +       dev_set_drvdata(dev, dp);
> +
> +       return component_add(dev, &cdn_dp_component_ops);
> +}
> +
> +static int cdn_dp_remove(struct platform_device *pdev)
> +{
> +       component_del(&pdev->dev, &cdn_dp_component_ops);
> +
> +       return 0;
> +}
> +
> +static struct platform_driver cdn_dp_driver = {
> +       .probe = cdn_dp_probe,
> +       .remove = cdn_dp_remove,
> +       .driver = {
> +                  .name = "cdn-dp",
> +                  .owner = THIS_MODULE,
> +                  .of_match_table = of_match_ptr(cdn_dp_dt_ids),
> +       },
> +};
> +
> +module_platform_driver(cdn_dp_driver);
> +
> +MODULE_AUTHOR("Chris Zhong <zyw at rock-chips.com>");
> +MODULE_DESCRIPTION("cdn DP Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
> new file mode 100644
> index 0000000..78424f9
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
> @@ -0,0 +1,103 @@
> +/*
> + * Copyright (C) 2016 Chris Zhong <zyw at rock-chips.com>
> + * Copyright (C) 2016 ROCKCHIP, Inc.
> + *
> + * 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.
> + */
> +
> +#ifndef _CDN_DP_CORE_H
> +#define _CDN_DP_CORE_H
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_dp_helper.h>
> +#include <drm/drm_panel.h>
> +#include "rockchip_drm_drv.h"
> +
> +#define MAX_PHY                2
> +
> +enum audio_format {
> +       AFMT_I2S = 0,
> +       AFMT_SPDIF = 1,
> +       AFMT_UNUSED,
> +};
> +
> +struct audio_info {
> +       enum audio_format format;
> +       int sample_rate;
> +       int channels;
> +       int sample_width;
> +};
> +
> +enum vic_pxl_encoding_format {
> +       PXL_RGB = 0x1,
> +       YCBCR_4_4_4 = 0x2,
> +       YCBCR_4_2_2 = 0x4,
> +       YCBCR_4_2_0 = 0x8,
> +       Y_ONLY = 0x10,
> +};
> +
> +struct video_info {
> +       bool h_sync_polarity;
> +       bool v_sync_polarity;
> +       bool interlaced;
> +       int color_depth;
> +       enum vic_pxl_encoding_format color_fmt;
> +};
> +
> +struct cdn_firmware_header {
> +       u32 size_bytes; /* size of the entire header+image(s) in bytes */
> +       u32 header_size; /* size of just the header in bytes */
> +       u32 iram_size; /* size of iram */
> +       u32 dram_size; /* size of dram */
> +};
> +
> +struct cdn_dp_port {
> +       struct cdn_dp_device *dp;
> +       struct notifier_block event_nb;
> +       struct delayed_work event_wq;
> +       struct extcon_dev *extcon;
> +       struct phy *phy;
> +       u8 cap_lanes;
> +       bool phy_status;
> +       u8 id;
> +};
> +
> +struct cdn_dp_device {
> +       struct device *dev;
> +       struct drm_device *drm_dev;
> +       struct drm_connector connector;
> +       struct drm_encoder encoder;
> +       struct drm_display_mode mode;
> +       struct platform_device *audio_pdev;
> +
> +       const struct firmware *fw;      /* cdn dp firmware */
> +       unsigned int fw_version;        /* cdn fw version */
> +       u32 fw_wait;
> +       bool fw_loaded;
> +       bool fw_actived;
> +       void __iomem *regs;
> +       struct regmap *grf;
> +       struct clk *core_clk;
> +       struct clk *pclk;
> +       struct clk *spdif_clk;
> +       struct reset_control *spdif_rst;
> +       struct audio_info audio_info;
> +       struct video_info video_info;
> +       struct drm_dp_link link;
> +       struct cdn_dp_port *port[MAX_PHY];
> +       u8 ports;
> +
> +       u8 dpcd[DP_RECEIVER_CAP_SIZE];
> +       enum drm_connector_status hpd_status;
> +       int dpms_mode;
> +       bool sink_has_audio;
> +};
> +#endif  /* _CDN_DP_CORE_H */
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
> new file mode 100644
> index 0000000..4255dac
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
> @@ -0,0 +1,916 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author: Chris Zhong <zyw at rock-chips.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * 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/clk.h>
> +#include <linux/device.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/reset.h>
> +
> +#include "cdn-dp-core.h"
> +#include "cdn-dp-reg.h"
> +
> +#define CDN_DP_SPDIF_CLK               200000000
> +#define FW_ALIVE_TIMEOUT_US            1000000
> +#define MAILBOX_RETRY_US               1000
> +#define MAILBOX_TIMEOUT_US             5000000
> +#define LINK_TRAINING_RETRY_MS         20
> +#define LINK_TRAINING_TIMEOUT_MS       500
> +
> +void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, u32 clk)
> +{
> +       writel(clk / 1000000, dp->regs + SW_CLK_H);
> +}
> +
> +void cdn_dp_clock_reset(struct cdn_dp_device *dp)
> +{
> +       u32 val;
> +
> +       val = DPTX_FRMR_DATA_CLK_RSTN_EN |
> +             DPTX_FRMR_DATA_CLK_EN |
> +             DPTX_PHY_DATA_RSTN_EN |
> +             DPTX_PHY_DATA_CLK_EN |
> +             DPTX_PHY_CHAR_RSTN_EN |
> +             DPTX_PHY_CHAR_CLK_EN |
> +             SOURCE_AUX_SYS_CLK_RSTN_EN |
> +             SOURCE_AUX_SYS_CLK_EN |
> +             DPTX_SYS_CLK_RSTN_EN |
> +             DPTX_SYS_CLK_EN |
> +             CFG_DPTX_VIF_CLK_RSTN_EN |
> +             CFG_DPTX_VIF_CLK_EN;
> +       writel(val, dp->regs + SOURCE_DPTX_CAR);
> +
> +       val = SOURCE_PHY_RSTN_EN | SOURCE_PHY_CLK_EN;
> +       writel(val, dp->regs + SOURCE_PHY_CAR);
> +
> +       val = SOURCE_PKT_SYS_RSTN_EN |
> +             SOURCE_PKT_SYS_CLK_EN |
> +             SOURCE_PKT_DATA_RSTN_EN |
> +             SOURCE_PKT_DATA_CLK_EN;
> +       writel(val, dp->regs + SOURCE_PKT_CAR);
> +
> +       val = SPDIF_CDR_CLK_RSTN_EN |
> +             SPDIF_CDR_CLK_EN |
> +             SOURCE_AIF_SYS_RSTN_EN |
> +             SOURCE_AIF_SYS_CLK_EN |
> +             SOURCE_AIF_CLK_RSTN_EN |
> +             SOURCE_AIF_CLK_EN;
> +       writel(val, dp->regs + SOURCE_AIF_CAR);
> +
> +       val = SOURCE_CIPHER_SYSTEM_CLK_RSTN_EN |
> +             SOURCE_CIPHER_SYS_CLK_EN |
> +             SOURCE_CIPHER_CHAR_CLK_RSTN_EN |
> +             SOURCE_CIPHER_CHAR_CLK_EN;
> +       writel(val, dp->regs + SOURCE_CIPHER_CAR);
> +
> +       val = SOURCE_CRYPTO_SYS_CLK_RSTN_EN |
> +             SOURCE_CRYPTO_SYS_CLK_EN;
> +       writel(val, dp->regs + SOURCE_CRYPTO_CAR);
> +
> +       val = ~(MAILBOX_INT_MASK_BIT | PIF_INT_MASK_BIT) & ALL_INT_MASK;
> +       writel(val, dp->regs + APB_INT_MASK);
> +}
> +
> +static int cdn_dp_mailbox_read(struct cdn_dp_device *dp, bool force)
> +{
> +       int val, ret;
> +
> +       if (!dp->fw_actived && !force)
> +               return -EPERM;
> +
> +       ret = readx_poll_timeout(readl, dp->regs + MAILBOX_EMPTY_ADDR,
> +                                val, !val, MAILBOX_RETRY_US,
> +                                MAILBOX_TIMEOUT_US);
> +       if (ret < 0) {
> +               dev_err(dp->dev, "failed to read mailbox, keep alive = %x\n",
> +                       readl(dp->regs + KEEP_ALIVE));
> +               return ret;
> +       }
> +
> +       return readl(dp->regs + MAILBOX0_RD_DATA) & 0xff;
> +}
> +
> +static int cdp_dp_mailbox_write(struct cdn_dp_device *dp, u8 val, bool force)
> +{
> +       int ret, full;
> +
> +       if (!dp->fw_actived && !force)
> +               return -EPERM;
> +
> +       ret = readx_poll_timeout(readl, dp->regs + MAILBOX_FULL_ADDR,
> +                                full, !full, MAILBOX_RETRY_US,
> +                                MAILBOX_TIMEOUT_US);
> +       if (ret < 0) {
> +               dev_err(dp->dev, "mailbox is full, keep alive = %x\n",
> +                       readl(dp->regs + KEEP_ALIVE));
> +               return ret;
> +       }
> +
> +       writel(val, dp->regs + MAILBOX0_WR_DATA);
> +
> +       return 0;
> +}
> +
> +static int cdn_dp_mailbox_validate_receive(struct cdn_dp_device *dp,
> +                                          u8 module_id, u8 opcode,
> +                                          u8 req_size)
> +{
> +       u32 mbox_size, i;
> +       u8 header[4];
> +       int ret;
> +
> +       /* read the header of the message */
> +       for (i = 0; i < 4; i++)  {
> +               ret = cdn_dp_mailbox_read(dp, 0);
> +               if (ret < 0)
> +                       return ret;
> +
> +               header[i] = ret;
> +       }
> +
> +       mbox_size = (header[2] << 8) | header[3];
> +
> +       if (opcode != header[0] || module_id != header[1] ||
> +           req_size != mbox_size) {
> +               dev_err(dp->dev, "mailbox validate failed");

Does this indicate a problem ? I am seeing it a lot with some Type-C
docks. What does it really mean ?

> +
> +               /*
> +                * If the message in mailbox is not what we want, we need to
> +                * clear the mailbox by read.
> +                */
> +               for (i = 0; i < mbox_size; i++)
> +                       if (cdn_dp_mailbox_read(dp, 0) < 0)
> +                               break;
> +
> +               return -EINVAL;
> +       }
> +
> +       return 0;
> +}
> +
> +static int cdn_dp_mailbox_read_receive(struct cdn_dp_device *dp,
> +                                      u8 *buff, u8 buff_size)
> +{
> +       u32 i;
> +       int ret;
> +
> +       for (i = 0; i < buff_size; i++) {
> +               ret = cdn_dp_mailbox_read(dp, 0);
> +               if (ret < 0)
> +                       return ret;
> +
> +               buff[i] = ret;
> +       }
> +
> +       return 0;
> +}
> +
> +static int cdn_dp_mailbox_send(struct cdn_dp_device *dp, u8 module_id,
> +                              u8 opcode, u16 size, u8 *message)
> +{
> +       u8 header[4];
> +       int ret, i;
> +
> +       header[0] = opcode;
> +       header[1] = module_id;
> +       header[2] = (size >> 8) & 0xff;
> +       header[3] = size & 0xff;
> +
> +       for (i = 0; i < 4; i++) {
> +               ret = cdp_dp_mailbox_write(dp, header[i], 0);
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       for (i = 0; i < size; i++) {
> +               ret = cdp_dp_mailbox_write(dp, message[i], 0);
> +               if (ret) {
> +                       dev_err(dp->dev, "mailbox send failed: %d", ret);
> +                       return ret;
> +               }
> +       }
> +
> +       return 0;
> +}
> +
> +static int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
> +{
> +       u8 msg[6];
> +
> +       msg[0] = (addr >> 8) & 0xff;
> +       msg[1] = addr & 0xff;
> +       msg[2] = (val >> 24) & 0xff;
> +       msg[3] = (val >> 16) & 0xff;
> +       msg[4] = (val >> 8) & 0xff;
> +       msg[5] = val & 0xff;
> +       return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_REGISTER,
> +                                  sizeof(msg), msg);
> +}
> +
> +int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
> +{
> +       u8 msg[5], reg[5];
> +       int ret;
> +
> +       msg[0] = (len >> 8) & 0xff;
> +       msg[1] = len & 0xff;
> +       msg[2] = (addr >> 16) & 0xff;
> +       msg[3] = (addr >> 8) & 0xff;
> +       msg[4] = addr & 0xff;
> +       cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_READ_DPCD,
> +                           sizeof(msg), msg);
> +
> +       ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
> +                                             DPTX_READ_DPCD,
> +                                             sizeof(reg) + len);
> +       if (ret)
> +               return ret;
> +
> +       ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg));
> +       if (ret)
> +               return ret;
> +
> +       return cdn_dp_mailbox_read_receive(dp, data, len);
> +}
> +
> +int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
> +{
> +       u8 msg[6], reg[5];
> +       int ret;
> +
> +       msg[0] = 0;
> +       msg[1] = 1;
> +       msg[2] = (addr >> 16) & 0xff;
> +       msg[3] = (addr >> 8) & 0xff;
> +       msg[4] = addr & 0xff;
> +       msg[5] = value;
> +       cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_DPCD,
> +                           sizeof(msg), msg);
> +
> +       ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
> +                                             DPTX_WRITE_DPCD, sizeof(reg));
> +       if (ret)
> +               return ret;
> +
> +       ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg));
> +       if (ret)
> +               return ret;
> +
> +       if (addr != (reg[2] << 16 | reg[3] << 8 | reg[4])) {
> +               dev_err(dp->dev, "write dpcd failed\n");
> +               return -EINVAL;
> +       }
> +
> +       return 0;
> +}
> +
> +int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
> +                        u32 i_size, const u32 *d_mem, u32 d_size)
> +{
> +       int i, ret;
> +       u32 reg;
> +
> +       /* reset ucpu before load firmware*/
> +       writel(APB_IRAM_PATH | APB_DRAM_PATH | APB_XT_RESET,
> +              dp->regs + APB_CTRL);
> +
> +       for (i = 0; i < i_size; i += 4)
> +               writel(*i_mem++, dp->regs + ADDR_IMEM + i);
> +
> +       for (i = 0; i < d_size; i += 4)
> +               writel(*d_mem++, dp->regs + ADDR_DMEM + i);
> +
> +       /* un-reset ucpu */
> +       writel(0, dp->regs + APB_CTRL);
> +
> +       /* check the keep alive register to make sure fw working */
> +       ret = readx_poll_timeout(readl, dp->regs + KEEP_ALIVE,
> +                                reg, reg, 2000, FW_ALIVE_TIMEOUT_US);
> +       if (ret < 0) {
> +               dev_err(dp->dev, "failed to loaded the FW reg = %x\n", reg);
> +               return -EINVAL;
> +       }
> +
> +       reg = readl(dp->regs + VER_L) & 0xff;
> +       dp->fw_version = reg;
> +       reg = readl(dp->regs + VER_H) & 0xff;
> +       dp->fw_version |= reg << 8;
> +       reg = readl(dp->regs + VER_LIB_L_ADDR) & 0xff;
> +       dp->fw_version |= reg << 16;
> +       reg = readl(dp->regs + VER_LIB_H_ADDR) & 0xff;
> +       dp->fw_version |= reg << 24;
> +
> +       dev_dbg(dp->dev, "firmware version: %x\n", dp->fw_version);
> +
> +       return 0;
> +}
> +
> +int cdn_dp_active(struct cdn_dp_device *dp, bool enable)
> +{
> +       u8 msg[5];
> +       int ret, i;
> +
> +       msg[0] = GENERAL_MAIN_CONTROL;
> +       msg[1] = MB_MODULE_ID_GENERAL;
> +       msg[2] = 0;
> +       msg[3] = 1;
> +       msg[4] = enable ? FW_ACTIVE : FW_STANDBY;
> +
> +       for (i = 0; i < sizeof(msg); i++) {
> +               ret = cdp_dp_mailbox_write(dp, msg[i], 1);
> +               if (ret)
> +                       return ret;
> +       }
> +
> +       /* read the firmware state */
> +       for (i = 0; i < sizeof(msg); i++)  {
> +               ret = cdn_dp_mailbox_read(dp, 1);
> +               if (ret < 0)
> +                       return ret;
> +
> +               msg[i] = ret;
> +       }
> +
> +       dp->fw_actived = (msg[4] == FW_ACTIVE);
> +       return 0;
> +}
> +
> +int cdn_dp_reg_write_bit(struct cdn_dp_device *dp, u16 addr, u8 start_bit,
> +                        u8 bits_no, u32 val)
> +{
> +       u8 field[8];
> +
> +       field[0] = (addr >> 8) & 0xff;
> +       field[1] = addr & 0xff;
> +       field[2] = start_bit;
> +       field[3] = bits_no;
> +       field[4] = (val >> 24) & 0xff;
> +       field[5] = (val >> 16) & 0xff;
> +       field[6] = (val >> 8) & 0xff;
> +       field[7] = val & 0xff;
> +
> +       return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_FIELD,
> +                                  sizeof(field), field);
> +}
> +
> +int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes)
> +{
> +       u8 msg[8];
> +       int ret;
> +
> +       msg[0] = CDN_DP_MAX_LINK_RATE;
> +       msg[1] = lanes;
> +       msg[2] = VOLTAGE_LEVEL_2;
> +       msg[3] = PRE_EMPHASIS_LEVEL_3;
> +       msg[4] = PTS1 | PTS2 | PTS3 | PTS4;
> +       msg[5] = FAST_LT_NOT_SUPPORT;
> +       msg[6] = LANE_MAPPING_NORMAL;
> +       msg[7] = ENHANCED;
> +
> +       ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
> +                                 DPTX_SET_HOST_CAPABILITIES,
> +                                 sizeof(msg), msg);
> +       if (ret)
> +               return ret;
> +
> +       ret = cdn_dp_reg_write(dp, DP_AUX_SWAP_INVERSION_CONTROL,
> +                              AUX_HOST_INVERT);
> +       if (ret)
> +               return ret;
> +
> +       return 0;
> +}
> +
> +int cdn_dp_event_config(struct cdn_dp_device *dp)
> +{
> +       u8 msg[5];
> +
> +       memset(msg, 0, sizeof(msg));
> +
> +       msg[0] = DPTX_EVENT_ENABLE_HPD | DPTX_EVENT_ENABLE_TRAINING;
> +
> +       return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_ENABLE_EVENT,
> +                                  sizeof(msg), msg);
> +}
> +
> +u32 cdn_dp_get_event(struct cdn_dp_device *dp)
> +{
> +       return readl(dp->regs + SW_EVENTS0);
> +}
> +
> +int cdn_dp_get_hpd_status(struct cdn_dp_device *dp)
> +{
> +       u8 status;
> +       int ret;
> +
> +       ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_HPD_STATE,
> +                                 0, NULL);
> +       if (ret)
> +               return ret;
> +
> +       ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
> +                                             DPTX_HPD_STATE, sizeof(status));
> +       if (ret)
> +               return ret;
> +
> +       ret = cdn_dp_mailbox_read_receive(dp, &status, sizeof(status));
> +       if (ret)
> +               return ret;
> +
> +       return status;
> +}
> +
> +int cdn_dp_get_edid_block(void *data, u8 *edid,
> +                         unsigned int block, size_t length)
> +{
> +       struct cdn_dp_device *dp = data;
> +       u8 msg[2], reg[2];
> +       int ret;
> +
> +       msg[0] = block / 2;
> +       msg[1] = block % 2;
> +
> +       ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_GET_EDID,
> +                                 sizeof(msg), msg);
> +       if (ret)
> +               return ret;
> +
> +       ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
> +                                             DPTX_GET_EDID,
> +                                             sizeof(reg) + length);
> +       if (ret)
> +               return ret;
> +
> +       ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg));
> +       if (ret)
> +               return ret;
> +
> +       ret = cdn_dp_mailbox_read_receive(dp, edid, length);
> +       if (ret)
> +               return ret;
> +
> +       if (reg[0] != length || reg[1] != block / 2) {
> +               dev_err(dp->dev, "edid block size err\n");
> +               return -EINVAL;
> +       }
> +
> +       return 0;
> +}
> +
> +int cdn_dp_training_start(struct cdn_dp_device *dp)
> +{
> +       u8 msg, event[2];
> +       unsigned long timeout;
> +       int ret;
> +
> +       msg = LINK_TRAINING_RUN;
> +
> +       /* start training */
> +       ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_TRAINING_CONTROL,
> +                                 sizeof(msg), &msg);
> +       if (ret)
> +               return ret;
> +
> +       timeout = jiffies + msecs_to_jiffies(LINK_TRAINING_TIMEOUT_MS);
> +       while (time_before(jiffies, timeout)) {
> +               msleep(LINK_TRAINING_RETRY_MS);
> +               ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
> +                                         DPTX_READ_EVENT, 0, NULL);
> +               if (ret)
> +                       return ret;
> +
> +               ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
> +                                                     DPTX_READ_EVENT,
> +                                                     sizeof(event));
> +               if (ret)
> +                       return ret;
> +
> +               ret = cdn_dp_mailbox_read_receive(dp, event, sizeof(event));
> +               if (ret)
> +                       return ret;
> +
> +               if (event[1] & EQ_PHASE_FINISHED)
> +                       return 0;
> +       }
> +
> +       return -ETIMEDOUT;
> +}
> +
> +int cdn_dp_get_training_status(struct cdn_dp_device *dp)
> +{
> +       u8 status[10];
> +       int ret;
> +
> +       ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_READ_LINK_STAT,
> +                                 0, NULL);
> +       if (ret)
> +               return ret;
> +
> +       ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
> +                                             DPTX_READ_LINK_STAT,
> +                                             sizeof(status));
> +       if (ret)
> +               return ret;
> +
> +       ret = cdn_dp_mailbox_read_receive(dp, status, sizeof(status));
> +       if (ret)
> +               return ret;
> +
> +       dp->link.rate = status[0];
> +       dp->link.num_lanes = status[1];
> +
> +       return 0;
> +}
> +
> +int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active)
> +{
> +       u8 msg;
> +
> +       msg = !!active;
> +
> +       return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_SET_VIDEO,
> +                                  sizeof(msg), &msg);
> +}
> +
> +static int cdn_dp_get_msa_misc(struct video_info *video,
> +                              struct drm_display_mode *mode)
> +{
> +       u8 val[2];
> +       u32 msa_misc;
> +
> +       switch (video->color_fmt) {
> +       case PXL_RGB:
> +       case Y_ONLY:
> +               val[0] = 0;
> +               break;
> +       /* set YUV default color space conversion to BT601 */
> +       case YCBCR_4_4_4:
> +               val[0] = 6 + BT_601 * 8;
> +               break;
> +       case YCBCR_4_2_2:
> +               val[0] = 5 + BT_601 * 8;
> +               break;
> +       case YCBCR_4_2_0:
> +               val[0] = 5;
> +               break;
> +       };
> +
> +       switch (video->color_depth) {
> +       case 6:
> +               val[1] = 0;
> +               break;
> +       case 8:
> +               val[1] = 1;
> +               break;
> +       case 10:
> +               val[1] = 2;
> +               break;
> +       case 12:
> +               val[1] = 3;
> +               break;
> +       case 16:
> +               val[1] = 4;
> +               break;
> +       };
> +
> +       msa_misc = 2 * val[0] + 32 * val[1] +
> +                  ((video->color_fmt == Y_ONLY) ? (1 << 14) : 0);
> +
> +       return msa_misc;
> +}
> +
> +int cdn_dp_config_video(struct cdn_dp_device *dp)
> +{
> +       struct video_info *video = &dp->video_info;
> +       struct drm_display_mode *mode = &dp->mode;
> +       u64 symbol, tmp;
> +       u32 val, link_rate;
> +       u8 bit_per_pix;
> +       u8 tu_size_reg = TU_SIZE;
> +       int ret;
> +
> +       bit_per_pix = (video->color_fmt == YCBCR_4_2_2) ?
> +                     (video->color_depth * 2) : (video->color_depth * 3);
> +
> +       link_rate = drm_dp_bw_code_to_link_rate(dp->link.rate) / 1000;
> +
> +       val = VIF_BYPASS_INTERLACE;
> +       ret = cdn_dp_reg_write(dp, BND_HSYNC2VSYNC, val);
> +       if (ret)
> +               return ret;
> +
> +       ret = cdn_dp_reg_write(dp, HSYNC2VSYNC_POL_CTRL, 0);
> +       if (ret)
> +               return ret;
> +
> +       /*
> +        * get a best tu_size and valid symbol:
> +        * 1. chose Lclk freq(162Mhz, 270Mhz, 540Mhz), set TU to 32
> +        * 2. calculate VS(valid symbol) = TU * Pclk * Bpp / (Lclk * Lanes)
> +        * 3. if VS > *.85 or VS < *.1 or VS < 2 or TU < VS + 4, then set
> +        *    TU += 2 and repeat 2nd step.
> +        */
> +       do {
> +               tu_size_reg += 2;
> +               tmp = tu_size_reg * mode->clock * bit_per_pix;
> +               tmp /= dp->link.num_lanes * link_rate * 8;
> +               symbol = tmp / 1000;
> +       } while ((symbol <= 1) || (tu_size_reg - symbol < 4) ||
> +                (tmp % 1000 > 850) || (tmp % 1000 < 100));
> +
> +       val = symbol + (tu_size_reg << 8);
> +       ret = cdn_dp_reg_write(dp, DP_FRAMER_TU, val);
> +       if (ret)
> +               return ret;
> +
> +       /* set the FIFO Buffer size */
> +       val = ((mode->clock * (symbol + 1) / 1000) + link_rate);
> +       val /= (dp->link.num_lanes * link_rate);
> +       val = 8 * (symbol + 1) / bit_per_pix - val;
> +       val += 2;
> +       ret = cdn_dp_reg_write(dp, DP_VC_TABLE(15), val);
> +
> +       switch (video->color_depth) {
> +       case 6:
> +               val = BCS_6;
> +               break;
> +       case 8:
> +               val = BCS_8;
> +               break;
> +       case 10:
> +               val = BCS_10;
> +               break;
> +       case 12:
> +               val = BCS_12;
> +               break;
> +       case 16:
> +               val = BCS_16;
> +               break;
> +       };
> +
> +       val += video->color_fmt << 8;
> +       ret = cdn_dp_reg_write(dp, DP_FRAMER_PXL_REPR, val);
> +       if (ret)
> +               return ret;
> +
> +       val = video->h_sync_polarity ? DP_FRAMER_SP_HSP : 0;
> +       val |= video->v_sync_polarity ? DP_FRAMER_SP_VSP : 0;
> +       ret = cdn_dp_reg_write(dp, DP_FRAMER_SP, val);
> +       if (ret)
> +               return ret;
> +
> +       val = (mode->hsync_start - mode->hdisplay) << 16;
> +       val |= mode->htotal - mode->hsync_end;
> +       ret = cdn_dp_reg_write(dp, DP_FRONT_BACK_PORCH, val);
> +       if (ret)
> +               return ret;
> +
> +       val = mode->hdisplay * bit_per_pix / 8;
> +       ret = cdn_dp_reg_write(dp, DP_BYTE_COUNT, val);
> +       if (ret)
> +               return ret;
> +
> +       val = mode->htotal | ((mode->htotal - mode->hsync_start) << 16);
> +       ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_0, val);
> +       if (ret)
> +               return ret;
> +
> +       val = mode->hsync_end - mode->hsync_start;
> +       val |= (mode->hdisplay << 16) | (video->h_sync_polarity << 15);
> +       ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_1, val);
> +       if (ret)
> +               return ret;
> +
> +       val = mode->vtotal;
> +       val |= ((mode->vtotal - mode->vsync_start) << 16);
> +       ret = cdn_dp_reg_write(dp, MSA_VERTICAL_0, val);
> +       if (ret)
> +               return ret;
> +
> +       val = mode->vsync_end - mode->vsync_start;
> +       val |= mode->vdisplay << 16 | (video->v_sync_polarity << 15);
> +       ret = cdn_dp_reg_write(dp, MSA_VERTICAL_1, val);
> +       if (ret)
> +               return ret;
> +
> +       val = cdn_dp_get_msa_misc(video, mode);
> +       ret = cdn_dp_reg_write(dp, MSA_MISC, val);
> +       if (ret)
> +               return ret;
> +
> +       ret = cdn_dp_reg_write(dp, STREAM_CONFIG, 1);
> +       if (ret)
> +               return ret;
> +
> +       val = mode->hsync_end - mode->hsync_start;
> +       val |= (mode->hdisplay << 16);
> +       ret = cdn_dp_reg_write(dp, DP_HORIZONTAL, val);
> +       if (ret)
> +               return ret;
> +
> +       val = mode->vtotal;
> +       val -= (mode->vtotal - mode->vdisplay);
> +       val |= (mode->vtotal - mode->vsync_start) << 16;
> +       ret = cdn_dp_reg_write(dp, DP_VERTICAL_0, val);
> +       if (ret)
> +               return ret;
> +
> +       val = mode->vtotal;
> +       ret = cdn_dp_reg_write(dp, DP_VERTICAL_1, val);
> +       if (ret)
> +               return ret;
> +
> +       val =  0;
> +       return cdn_dp_reg_write_bit(dp, DP_VB_ID, 2, 1, val);
> +}
> +
> +int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio)
> +{
> +       u32 val;
> +       int ret;
> +
> +       ret = cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, 0);
> +       if (ret)
> +               return ret;
> +
> +       val = SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS;
> +       val |= SPDIF_FIFO_MID_RANGE(0xe0);
> +       val |= SPDIF_JITTER_THRSH(0xe0);
> +       val |= SPDIF_JITTER_AVG_WIN(7);
> +       writel(val, dp->regs + SPDIF_CTRL_ADDR);
> +
> +       /* clearn the audio config and reset */
> +       writel(0, dp->regs + AUDIO_SRC_CNTL);
> +       writel(0, dp->regs + AUDIO_SRC_CNFG);
> +       writel(AUDIO_SW_RST, dp->regs + AUDIO_SRC_CNTL);
> +       writel(0, dp->regs + AUDIO_SRC_CNTL);
> +
> +       /* reset smpl2pckt component  */
> +       writel(0, dp->regs + SMPL2PKT_CNTL);
> +       writel(AUDIO_SW_RST, dp->regs + SMPL2PKT_CNTL);
> +       writel(0, dp->regs + SMPL2PKT_CNTL);
> +
> +       /* reset FIFO */
> +       writel(AUDIO_SW_RST, dp->regs + FIFO_CNTL);
> +       writel(0, dp->regs + FIFO_CNTL);
> +
> +       if (audio->format == AFMT_SPDIF)
> +               clk_disable_unprepare(dp->spdif_clk);
> +
> +       return 0;
> +}
> +
> +int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable)
> +{
> +       return cdn_dp_reg_write_bit(dp, DP_VB_ID, 4, 1, enable);
> +}
> +
> +static void cdn_dp_audio_config_i2s(struct cdn_dp_device *dp,
> +                                   struct audio_info *audio)
> +{
> +       int sub_pckt_num = 1, i2s_port_en_val = 0xf, i;
> +       u32 val;
> +
> +       if (audio->channels == 2) {
> +               if (dp->link.num_lanes == 1)
> +                       sub_pckt_num = 2;
> +               else
> +                       sub_pckt_num = 4;
> +
> +               i2s_port_en_val = 1;
> +       } else if (audio->channels == 4) {
> +               i2s_port_en_val = 3;
> +       }
> +
> +       writel(0x0, dp->regs + SPDIF_CTRL_ADDR);
> +
> +       writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL);
> +
> +       val = MAX_NUM_CH(audio->channels);
> +       val |= NUM_OF_I2S_PORTS(audio->channels);
> +       val |= AUDIO_TYPE_LPCM;
> +       val |= CFG_SUB_PCKT_NUM(sub_pckt_num);
> +       writel(val, dp->regs + SMPL2PKT_CNFG);
> +
> +       if (audio->sample_width == 16)
> +               val = 0;
> +       else if (audio->sample_width == 24)
> +               val = 1 << 9;
> +       else
> +               val = 2 << 9;
> +
> +       val |= AUDIO_CH_NUM(audio->channels);
> +       val |= I2S_DEC_PORT_EN(i2s_port_en_val);
> +       val |= TRANS_SMPL_WIDTH_32;
> +       writel(val, dp->regs + AUDIO_SRC_CNFG);
> +
> +       for (i = 0; i < (audio->channels + 1) / 2; i++) {
> +               if (audio->sample_width == 16)
> +                       val = (0x08 << 8) | (0x08 << 20);
> +               else if (audio->sample_width == 24)
> +                       val = (0x0b << 8) | (0x0b << 20);
> +
> +               val |= ((2 * i) << 4) | ((2 * i + 1) << 16);
> +               writel(val, dp->regs + STTS_BIT_CH(i));
> +       }
> +
> +       switch (audio->sample_rate) {
> +       case 32000:
> +               val = SAMPLING_FREQ(3) |
> +                     ORIGINAL_SAMP_FREQ(0xc);
> +               break;
> +       case 44100:
> +               val = SAMPLING_FREQ(0) |
> +                     ORIGINAL_SAMP_FREQ(0xf);
> +               break;
> +       case 48000:
> +               val = SAMPLING_FREQ(2) |
> +                     ORIGINAL_SAMP_FREQ(0xd);
> +               break;
> +       case 88200:
> +               val = SAMPLING_FREQ(8) |
> +                     ORIGINAL_SAMP_FREQ(0x7);
> +               break;
> +       case 96000:
> +               val = SAMPLING_FREQ(0xa) |
> +                     ORIGINAL_SAMP_FREQ(5);
> +               break;
> +       case 176400:
> +               val = SAMPLING_FREQ(0xc) |
> +                     ORIGINAL_SAMP_FREQ(3);
> +               break;
> +       case 192000:
> +               val = SAMPLING_FREQ(0xe) |
> +                     ORIGINAL_SAMP_FREQ(1);
> +               break;
> +       }
> +       val |= 4;
> +       writel(val, dp->regs + COM_CH_STTS_BITS);
> +
> +       writel(SMPL2PKT_EN, dp->regs + SMPL2PKT_CNTL);
> +       writel(I2S_DEC_START, dp->regs + AUDIO_SRC_CNTL);
> +}
> +
> +static void cdn_dp_audio_config_spdif(struct cdn_dp_device *dp)
> +{
> +       u32 val;
> +
> +       val = SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS;
> +       val |= SPDIF_FIFO_MID_RANGE(0xe0);
> +       val |= SPDIF_JITTER_THRSH(0xe0);
> +       val |= SPDIF_JITTER_AVG_WIN(7);
> +       writel(val, dp->regs + SPDIF_CTRL_ADDR);
> +
> +       writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL);
> +
> +       val = MAX_NUM_CH(2) | AUDIO_TYPE_LPCM | CFG_SUB_PCKT_NUM(4);
> +       writel(val, dp->regs + SMPL2PKT_CNFG);
> +       writel(SMPL2PKT_EN, dp->regs + SMPL2PKT_CNTL);
> +
> +       val = SPDIF_ENABLE | SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS;
> +       val |= SPDIF_FIFO_MID_RANGE(0xe0);
> +       val |= SPDIF_JITTER_THRSH(0xe0);
> +       val |= SPDIF_JITTER_AVG_WIN(7);
> +       writel(val, dp->regs + SPDIF_CTRL_ADDR);
> +
> +       clk_prepare_enable(dp->spdif_clk);
> +       clk_set_rate(dp->spdif_clk, CDN_DP_SPDIF_CLK);
> +}
> +
> +int cdn_dp_audio_config_set(struct cdn_dp_device *dp, struct audio_info *audio)
> +{
> +       int ret;
> +
> +       /* reset the spdif clk before config */
> +       if (audio->format == AFMT_SPDIF) {
> +               reset_control_assert(dp->spdif_rst);
> +               reset_control_deassert(dp->spdif_rst);
> +       }
> +
> +       ret = cdn_dp_reg_write(dp, CM_LANE_CTRL, LANE_REF_CYC);
> +       if (ret)
> +               return ret;
> +
> +       ret = cdn_dp_reg_write(dp, CM_CTRL, 0);
> +       if (ret)
> +               return ret;
> +
> +       if (audio->format == AFMT_I2S)
> +               cdn_dp_audio_config_i2s(dp, audio);
> +       else
> +               cdn_dp_audio_config_spdif(dp);
> +
> +       return cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, AUDIO_PACK_EN);
> +}
> diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
> new file mode 100644
> index 0000000..e83aebd
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
> @@ -0,0 +1,482 @@
> +/*
> + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
> + * Author: Chris Zhong <zyw at rock-chips.com>
> + *
> + * This software is licensed under the terms of the GNU General Public
> + * License version 2, as published by the Free Software Foundation, and
> + * may be copied, distributed, and modified under those terms.
> + *
> + * 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.
> + */
> +
> +#ifndef _CDN_DP_REG_H
> +#define _CDN_DP_REG_H
> +
> +#include <linux/bitops.h>
> +
> +#define ADDR_IMEM              0x10000
> +#define ADDR_DMEM              0x20000
> +
> +/* APB CFG addr */
> +#define APB_CTRL                       0
> +#define XT_INT_CTRL                    0x04
> +#define MAILBOX_FULL_ADDR              0x08
> +#define MAILBOX_EMPTY_ADDR             0x0c
> +#define MAILBOX0_WR_DATA               0x10
> +#define MAILBOX0_RD_DATA               0x14
> +#define KEEP_ALIVE                     0x18
> +#define VER_L                          0x1c
> +#define VER_H                          0x20
> +#define VER_LIB_L_ADDR                 0x24
> +#define VER_LIB_H_ADDR                 0x28
> +#define SW_DEBUG_L                     0x2c
> +#define SW_DEBUG_H                     0x30
> +#define MAILBOX_INT_MASK               0x34
> +#define MAILBOX_INT_STATUS             0x38
> +#define SW_CLK_L                       0x3c
> +#define SW_CLK_H                       0x40
> +#define SW_EVENTS0                     0x44
> +#define SW_EVENTS1                     0x48
> +#define SW_EVENTS2                     0x4c
> +#define SW_EVENTS3                     0x50
> +#define XT_OCD_CTRL                    0x60
> +#define APB_INT_MASK                   0x6c
> +#define APB_STATUS_MASK                        0x70
> +
> +/* audio decoder addr */
> +#define AUDIO_SRC_CNTL                 0x30000
> +#define AUDIO_SRC_CNFG                 0x30004
> +#define COM_CH_STTS_BITS               0x30008
> +#define STTS_BIT_CH(x)                 (0x3000c + ((x) << 2))
> +#define SPDIF_CTRL_ADDR                        0x3004c
> +#define SPDIF_CH1_CS_3100_ADDR         0x30050
> +#define SPDIF_CH1_CS_6332_ADDR         0x30054
> +#define SPDIF_CH1_CS_9564_ADDR         0x30058
> +#define SPDIF_CH1_CS_12796_ADDR                0x3005c
> +#define SPDIF_CH1_CS_159128_ADDR       0x30060
> +#define SPDIF_CH1_CS_191160_ADDR       0x30064
> +#define SPDIF_CH2_CS_3100_ADDR         0x30068
> +#define SPDIF_CH2_CS_6332_ADDR         0x3006c
> +#define SPDIF_CH2_CS_9564_ADDR         0x30070
> +#define SPDIF_CH2_CS_12796_ADDR                0x30074
> +#define SPDIF_CH2_CS_159128_ADDR       0x30078
> +#define SPDIF_CH2_CS_191160_ADDR       0x3007c
> +#define SMPL2PKT_CNTL                  0x30080
> +#define SMPL2PKT_CNFG                  0x30084
> +#define FIFO_CNTL                      0x30088
> +#define FIFO_STTS                      0x3008c
> +
> +/* source pif addr */
> +#define SOURCE_PIF_WR_ADDR             0x30800
> +#define SOURCE_PIF_WR_REQ              0x30804
> +#define SOURCE_PIF_RD_ADDR             0x30808
> +#define SOURCE_PIF_RD_REQ              0x3080c
> +#define SOURCE_PIF_DATA_WR             0x30810
> +#define SOURCE_PIF_DATA_RD             0x30814
> +#define SOURCE_PIF_FIFO1_FLUSH         0x30818
> +#define SOURCE_PIF_FIFO2_FLUSH         0x3081c
> +#define SOURCE_PIF_STATUS              0x30820
> +#define SOURCE_PIF_INTERRUPT_SOURCE    0x30824
> +#define SOURCE_PIF_INTERRUPT_MASK      0x30828
> +#define SOURCE_PIF_PKT_ALLOC_REG       0x3082c
> +#define SOURCE_PIF_PKT_ALLOC_WR_EN     0x30830
> +#define SOURCE_PIF_SW_RESET            0x30834
> +
> +/* bellow registers need access by mailbox */
> +/* source car addr */
> +#define SOURCE_HDTX_CAR                        0x0900
> +#define SOURCE_DPTX_CAR                        0x0904
> +#define SOURCE_PHY_CAR                 0x0908
> +#define SOURCE_CEC_CAR                 0x090c
> +#define SOURCE_CBUS_CAR                        0x0910
> +#define SOURCE_PKT_CAR                 0x0918
> +#define SOURCE_AIF_CAR                 0x091c
> +#define SOURCE_CIPHER_CAR              0x0920
> +#define SOURCE_CRYPTO_CAR              0x0924
> +
> +/* clock meters addr */
> +#define CM_CTRL                                0x0a00
> +#define CM_I2S_CTRL                    0x0a04
> +#define CM_SPDIF_CTRL                  0x0a08
> +#define CM_VID_CTRL                    0x0a0c
> +#define CM_LANE_CTRL                   0x0a10
> +#define I2S_NM_STABLE                  0x0a14
> +#define I2S_NCTS_STABLE                        0x0a18
> +#define SPDIF_NM_STABLE                        0x0a1c
> +#define SPDIF_NCTS_STABLE              0x0a20
> +#define NMVID_MEAS_STABLE              0x0a24
> +#define I2S_MEAS                       0x0a40
> +#define SPDIF_MEAS                     0x0a80
> +#define NMVID_MEAS                     0x0ac0
> +
> +/* source vif addr */
> +#define BND_HSYNC2VSYNC                        0x0b00
> +#define HSYNC2VSYNC_F1_L1              0x0b04
> +#define HSYNC2VSYNC_F2_L1              0x0b08
> +#define HSYNC2VSYNC_STATUS             0x0b0c
> +#define HSYNC2VSYNC_POL_CTRL           0x0b10
> +
> +/* dptx phy addr */
> +#define DP_TX_PHY_CONFIG_REG           0x2000
> +#define DP_TX_PHY_STATUS_REG           0x2004
> +#define DP_TX_PHY_SW_RESET             0x2008
> +#define DP_TX_PHY_SCRAMBLER_SEED       0x200c
> +#define DP_TX_PHY_TRAINING_01_04       0x2010
> +#define DP_TX_PHY_TRAINING_05_08       0x2014
> +#define DP_TX_PHY_TRAINING_09_10       0x2018
> +#define TEST_COR                       0x23fc
> +
> +/* dptx hpd addr */
> +#define HPD_IRQ_DET_MIN_TIMER          0x2100
> +#define HPD_IRQ_DET_MAX_TIMER          0x2104
> +#define HPD_UNPLGED_DET_MIN_TIMER      0x2108
> +#define HPD_STABLE_TIMER               0x210c
> +#define HPD_FILTER_TIMER               0x2110
> +#define HPD_EVENT_MASK                 0x211c
> +#define HPD_EVENT_DET                  0x2120
> +
> +/* dpyx framer addr */
> +#define DP_FRAMER_GLOBAL_CONFIG                0x2200
> +#define DP_SW_RESET                    0x2204
> +#define DP_FRAMER_TU                   0x2208
> +#define DP_FRAMER_PXL_REPR             0x220c
> +#define DP_FRAMER_SP                   0x2210
> +#define AUDIO_PACK_CONTROL             0x2214
> +#define DP_VC_TABLE(x)                 (0x2218 + ((x) << 2))
> +#define DP_VB_ID                       0x2258
> +#define DP_MTPH_LVP_CONTROL            0x225c
> +#define DP_MTPH_SYMBOL_VALUES          0x2260
> +#define DP_MTPH_ECF_CONTROL            0x2264
> +#define DP_MTPH_ACT_CONTROL            0x2268
> +#define DP_MTPH_STATUS                 0x226c
> +#define DP_INTERRUPT_SOURCE            0x2270
> +#define DP_INTERRUPT_MASK              0x2274
> +#define DP_FRONT_BACK_PORCH            0x2278
> +#define DP_BYTE_COUNT                  0x227c
> +
> +/* dptx stream addr */
> +#define MSA_HORIZONTAL_0               0x2280
> +#define MSA_HORIZONTAL_1               0x2284
> +#define MSA_VERTICAL_0                 0x2288
> +#define MSA_VERTICAL_1                 0x228c
> +#define MSA_MISC                       0x2290
> +#define STREAM_CONFIG                  0x2294
> +#define AUDIO_PACK_STATUS              0x2298
> +#define VIF_STATUS                     0x229c
> +#define PCK_STUFF_STATUS_0             0x22a0
> +#define PCK_STUFF_STATUS_1             0x22a4
> +#define INFO_PACK_STATUS               0x22a8
> +#define RATE_GOVERNOR_STATUS           0x22ac
> +#define DP_HORIZONTAL                  0x22b0
> +#define DP_VERTICAL_0                  0x22b4
> +#define DP_VERTICAL_1                  0x22b8
> +#define DP_BLOCK_SDP                   0x22bc
> +
> +/* dptx glbl addr */
> +#define DPTX_LANE_EN                   0x2300
> +#define DPTX_ENHNCD                    0x2304
> +#define DPTX_INT_MASK                  0x2308
> +#define DPTX_INT_STATUS                        0x230c
> +
> +/* dp aux addr */
> +#define DP_AUX_HOST_CONTROL            0x2800
> +#define DP_AUX_INTERRUPT_SOURCE                0x2804
> +#define DP_AUX_INTERRUPT_MASK          0x2808
> +#define DP_AUX_SWAP_INVERSION_CONTROL  0x280c
> +#define DP_AUX_SEND_NACK_TRANSACTION   0x2810
> +#define DP_AUX_CLEAR_RX                        0x2814
> +#define DP_AUX_CLEAR_TX                        0x2818
> +#define DP_AUX_TIMER_STOP              0x281c
> +#define DP_AUX_TIMER_CLEAR             0x2820
> +#define DP_AUX_RESET_SW                        0x2824
> +#define DP_AUX_DIVIDE_2M               0x2828
> +#define DP_AUX_TX_PREACHARGE_LENGTH    0x282c
> +#define DP_AUX_FREQUENCY_1M_MAX                0x2830
> +#define DP_AUX_FREQUENCY_1M_MIN                0x2834
> +#define DP_AUX_RX_PRE_MIN              0x2838
> +#define DP_AUX_RX_PRE_MAX              0x283c
> +#define DP_AUX_TIMER_PRESET            0x2840
> +#define DP_AUX_NACK_FORMAT             0x2844
> +#define DP_AUX_TX_DATA                 0x2848
> +#define DP_AUX_RX_DATA                 0x284c
> +#define DP_AUX_TX_STATUS               0x2850
> +#define DP_AUX_RX_STATUS               0x2854
> +#define DP_AUX_RX_CYCLE_COUNTER                0x2858
> +#define DP_AUX_MAIN_STATES             0x285c
> +#define DP_AUX_MAIN_TIMER              0x2860
> +#define DP_AUX_AFE_OUT                 0x2864
> +
> +/* crypto addr */
> +#define CRYPTO_HDCP_REVISION           0x5800
> +#define HDCP_CRYPTO_CONFIG             0x5804
> +#define CRYPTO_INTERRUPT_SOURCE                0x5808
> +#define CRYPTO_INTERRUPT_MASK          0x580c
> +#define CRYPTO22_CONFIG                        0x5818
> +#define CRYPTO22_STATUS                        0x581c
> +#define SHA_256_DATA_IN                        0x583c
> +#define SHA_256_DATA_OUT_(x)           (0x5850 + ((x) << 2))
> +#define AES_32_KEY_(x)                 (0x5870 + ((x) << 2))
> +#define AES_32_DATA_IN                 0x5880
> +#define AES_32_DATA_OUT_(x)            (0x5884 + ((x) << 2))
> +#define CRYPTO14_CONFIG                        0x58a0
> +#define CRYPTO14_STATUS                        0x58a4
> +#define CRYPTO14_PRNM_OUT              0x58a8
> +#define CRYPTO14_KM_0                  0x58ac
> +#define CRYPTO14_KM_1                  0x58b0
> +#define CRYPTO14_AN_0                  0x58b4
> +#define CRYPTO14_AN_1                  0x58b8
> +#define CRYPTO14_YOUR_KSV_0            0x58bc
> +#define CRYPTO14_YOUR_KSV_1            0x58c0
> +#define CRYPTO14_MI_0                  0x58c4
> +#define CRYPTO14_MI_1                  0x58c8
> +#define CRYPTO14_TI_0                  0x58cc
> +#define CRYPTO14_KI_0                  0x58d0
> +#define CRYPTO14_KI_1                  0x58d4
> +#define CRYPTO14_BLOCKS_NUM            0x58d8
> +#define CRYPTO14_KEY_MEM_DATA_0                0x58dc
> +#define CRYPTO14_KEY_MEM_DATA_1                0x58e0
> +#define CRYPTO14_SHA1_MSG_DATA         0x58e4
> +#define CRYPTO14_SHA1_V_VALUE_(x)      (0x58e8 + ((x) << 2))
> +#define TRNG_CTRL                      0x58fc
> +#define TRNG_DATA_RDY                  0x5900
> +#define TRNG_DATA                      0x5904
> +
> +/* cipher addr */
> +#define HDCP_REVISION                  0x60000
> +#define INTERRUPT_SOURCE               0x60004
> +#define INTERRUPT_MASK                 0x60008
> +#define HDCP_CIPHER_CONFIG             0x6000c
> +#define AES_128_KEY_0                  0x60010
> +#define AES_128_KEY_1                  0x60014
> +#define AES_128_KEY_2                  0x60018
> +#define AES_128_KEY_3                  0x6001c
> +#define AES_128_RANDOM_0               0x60020
> +#define AES_128_RANDOM_1               0x60024
> +#define CIPHER14_KM_0                  0x60028
> +#define CIPHER14_KM_1                  0x6002c
> +#define CIPHER14_STATUS                        0x60030
> +#define CIPHER14_RI_PJ_STATUS          0x60034
> +#define CIPHER_MODE                    0x60038
> +#define CIPHER14_AN_0                  0x6003c
> +#define CIPHER14_AN_1                  0x60040
> +#define CIPHER22_AUTH                  0x60044
> +#define CIPHER14_R0_DP_STATUS          0x60048
> +#define CIPHER14_BOOTSTRAP             0x6004c
> +
> +#define DPTX_FRMR_DATA_CLK_RSTN_EN     BIT(11)
> +#define DPTX_FRMR_DATA_CLK_EN          BIT(10)
> +#define DPTX_PHY_DATA_RSTN_EN          BIT(9)
> +#define DPTX_PHY_DATA_CLK_EN           BIT(8)
> +#define DPTX_PHY_CHAR_RSTN_EN          BIT(7)
> +#define DPTX_PHY_CHAR_CLK_EN           BIT(6)
> +#define SOURCE_AUX_SYS_CLK_RSTN_EN     BIT(5)
> +#define SOURCE_AUX_SYS_CLK_EN          BIT(4)
> +#define DPTX_SYS_CLK_RSTN_EN           BIT(3)
> +#define DPTX_SYS_CLK_EN                        BIT(2)
> +#define CFG_DPTX_VIF_CLK_RSTN_EN       BIT(1)
> +#define CFG_DPTX_VIF_CLK_EN            BIT(0)
> +
> +#define SOURCE_PHY_RSTN_EN             BIT(1)
> +#define SOURCE_PHY_CLK_EN              BIT(0)
> +
> +#define SOURCE_PKT_SYS_RSTN_EN         BIT(3)
> +#define SOURCE_PKT_SYS_CLK_EN          BIT(2)
> +#define SOURCE_PKT_DATA_RSTN_EN                BIT(1)
> +#define SOURCE_PKT_DATA_CLK_EN         BIT(0)
> +
> +#define SPDIF_CDR_CLK_RSTN_EN          BIT(5)
> +#define SPDIF_CDR_CLK_EN               BIT(4)
> +#define SOURCE_AIF_SYS_RSTN_EN         BIT(3)
> +#define SOURCE_AIF_SYS_CLK_EN          BIT(2)
> +#define SOURCE_AIF_CLK_RSTN_EN         BIT(1)
> +#define SOURCE_AIF_CLK_EN              BIT(0)
> +
> +#define SOURCE_CIPHER_SYSTEM_CLK_RSTN_EN       BIT(3)
> +#define SOURCE_CIPHER_SYS_CLK_EN               BIT(2)
> +#define SOURCE_CIPHER_CHAR_CLK_RSTN_EN         BIT(1)
> +#define SOURCE_CIPHER_CHAR_CLK_EN              BIT(0)
> +
> +#define SOURCE_CRYPTO_SYS_CLK_RSTN_EN  BIT(1)
> +#define SOURCE_CRYPTO_SYS_CLK_EN       BIT(0)
> +
> +#define APB_IRAM_PATH                  BIT(2)
> +#define APB_DRAM_PATH                  BIT(1)
> +#define APB_XT_RESET                   BIT(0)
> +
> +#define MAILBOX_INT_MASK_BIT           BIT(1)
> +#define PIF_INT_MASK_BIT               BIT(0)
> +#define ALL_INT_MASK                   3
> +
> +/* mailbox */
> +#define MB_OPCODE_ID                   0
> +#define MB_MODULE_ID                   1
> +#define MB_SIZE_MSB_ID                 2
> +#define MB_SIZE_LSB_ID                 3
> +#define MB_DATA_ID                     4
> +
> +#define MB_MODULE_ID_DP_TX             0x01
> +#define MB_MODULE_ID_HDCP_TX           0x07
> +#define MB_MODULE_ID_HDCP_RX           0x08
> +#define MB_MODULE_ID_HDCP_GENERAL      0x09
> +#define MB_MODULE_ID_GENERAL           0x0a
> +
> +/* general opcode */
> +#define GENERAL_MAIN_CONTROL            0x01
> +#define GENERAL_TEST_ECHO               0x02
> +#define GENERAL_BUS_SETTINGS            0x03
> +#define GENERAL_TEST_ACCESS             0x04
> +
> +#define DPTX_SET_POWER_MNG                     0x00
> +#define DPTX_SET_HOST_CAPABILITIES             0x01
> +#define DPTX_GET_EDID                          0x02
> +#define DPTX_READ_DPCD                         0x03
> +#define DPTX_WRITE_DPCD                                0x04
> +#define DPTX_ENABLE_EVENT                      0x05
> +#define DPTX_WRITE_REGISTER                    0x06
> +#define DPTX_READ_REGISTER                     0x07
> +#define DPTX_WRITE_FIELD                       0x08
> +#define DPTX_TRAINING_CONTROL                  0x09
> +#define DPTX_READ_EVENT                                0x0a
> +#define DPTX_READ_LINK_STAT                    0x0b
> +#define DPTX_SET_VIDEO                         0x0c
> +#define DPTX_SET_AUDIO                         0x0d
> +#define DPTX_GET_LAST_AUX_STAUS                        0x0e
> +#define DPTX_SET_LINK_BREAK_POINT              0x0f
> +#define DPTX_FORCE_LANES                       0x10
> +#define DPTX_HPD_STATE                         0x11
> +
> +#define FW_STANDBY                             0
> +#define FW_ACTIVE                              1
> +
> +#define DPTX_EVENT_ENABLE_HPD                  BIT(0)
> +#define DPTX_EVENT_ENABLE_TRAINING             BIT(1)
> +
> +#define LINK_TRAINING_NOT_ACTIVE               0
> +#define LINK_TRAINING_RUN                      1
> +#define LINK_TRAINING_RESTART                  2
> +
> +#define CONTROL_VIDEO_IDLE                     0
> +#define CONTROL_VIDEO_VALID                    1
> +
> +#define VIF_BYPASS_INTERLACE                   BIT(13)
> +#define INTERLACE_FMT_DET                      BIT(12)
> +#define INTERLACE_DTCT_WIN                     0x20
> +
> +#define DP_FRAMER_SP_INTERLACE_EN              BIT(2)
> +#define DP_FRAMER_SP_HSP                       BIT(1)
> +#define DP_FRAMER_SP_VSP                       BIT(0)
> +
> +/* capability */
> +#define AUX_HOST_INVERT                                3
> +#define        FAST_LT_SUPPORT                         1
> +#define FAST_LT_NOT_SUPPORT                    0
> +#define LANE_MAPPING_NORMAL                    0xe4
> +#define LANE_MAPPING_FLIPPED                   0x1b
> +#define ENHANCED                               1
> +
> +#define        FULL_LT_STARTED                         BIT(0)
> +#define FASE_LT_STARTED                                BIT(1)
> +#define CLK_RECOVERY_FINISHED                  BIT(2)
> +#define EQ_PHASE_FINISHED                      BIT(3)
> +#define FASE_LT_START_FINISHED                 BIT(4)
> +#define CLK_RECOVERY_FAILED                    BIT(5)
> +#define EQ_PHASE_FAILED                                BIT(6)
> +#define FASE_LT_FAILED                         BIT(7)
> +
> +#define DPTX_HPD_EVENT                         BIT(0)
> +#define DPTX_TRAINING_EVENT                    BIT(1)
> +#define HDCP_TX_STATUS_EVENT                   BIT(4)
> +#define HDCP2_TX_IS_KM_STORED_EVENT            BIT(5)
> +#define HDCP2_TX_STORE_KM_EVENT                        BIT(6)
> +#define HDCP_TX_IS_RECEIVER_ID_VALID_EVENT     BIT(7)
> +
> +#define TU_SIZE                                        30
> +#define CDN_DP_MAX_LINK_RATE                   DP_LINK_BW_5_4
> +
> +/* audio */
> +#define AUDIO_PACK_EN                          BIT(8)
> +#define SAMPLING_FREQ(x)                       (((x) & 0xf) << 16)
> +#define ORIGINAL_SAMP_FREQ(x)                  (((x) & 0xf) << 24)
> +#define SYNC_WR_TO_CH_ZERO                     BIT(1)
> +#define I2S_DEC_START                          BIT(1)
> +#define AUDIO_SW_RST                           BIT(0)
> +#define SMPL2PKT_EN                            BIT(1)
> +#define MAX_NUM_CH(x)                          (((x) & 0x1f) - 1)
> +#define NUM_OF_I2S_PORTS(x)                    ((((x) / 2 - 1) & 0x3) << 5)
> +#define AUDIO_TYPE_LPCM                                (2 << 7)
> +#define CFG_SUB_PCKT_NUM(x)                    ((((x) - 1) & 0x7) << 11)
> +#define AUDIO_CH_NUM(x)                                ((((x) - 1) & 0x1f) << 2)
> +#define TRANS_SMPL_WIDTH_16                    0
> +#define TRANS_SMPL_WIDTH_24                    BIT(11)
> +#define TRANS_SMPL_WIDTH_32                    (2 << 11)
> +#define I2S_DEC_PORT_EN(x)                     (((x) & 0xf) << 17)
> +#define SPDIF_ENABLE                           BIT(21)
> +#define SPDIF_AVG_SEL                          BIT(20)
> +#define SPDIF_JITTER_BYPASS                    BIT(19)
> +#define SPDIF_FIFO_MID_RANGE(x)                        (((x) & 0xff) << 11)
> +#define SPDIF_JITTER_THRSH(x)                  (((x) & 0xff) << 3)
> +#define SPDIF_JITTER_AVG_WIN(x)                        ((x) & 0x7)
> +
> +/* Refernce cycles when using lane clock as refernce */
> +#define LANE_REF_CYC                           0x8000
> +
> +enum voltage_swing_level {
> +       VOLTAGE_LEVEL_0,
> +       VOLTAGE_LEVEL_1,
> +       VOLTAGE_LEVEL_2,
> +       VOLTAGE_LEVEL_3,
> +};
> +
> +enum pre_emphasis_level {
> +       PRE_EMPHASIS_LEVEL_0,
> +       PRE_EMPHASIS_LEVEL_1,
> +       PRE_EMPHASIS_LEVEL_2,
> +       PRE_EMPHASIS_LEVEL_3,
> +};
> +
> +enum pattern_set {
> +       PTS1            = BIT(0),
> +       PTS2            = BIT(1),
> +       PTS3            = BIT(2),
> +       PTS4            = BIT(3),
> +       DP_NONE         = BIT(4)
> +};
> +
> +enum vic_color_depth {
> +       BCS_6 = 0x1,
> +       BCS_8 = 0x2,
> +       BCS_10 = 0x4,
> +       BCS_12 = 0x8,
> +       BCS_16 = 0x10,
> +};
> +
> +enum vic_bt_type {
> +       BT_601 = 0x0,
> +       BT_709 = 0x1,
> +};
> +
> +void cdn_dp_clock_reset(struct cdn_dp_device *dp);
> +
> +void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, u32 clk);
> +int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
> +                        u32 i_size, const u32 *d_mem, u32 d_size);
> +int cdn_dp_active(struct cdn_dp_device *dp, bool enable);
> +int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes);
> +int cdn_dp_event_config(struct cdn_dp_device *dp);
> +u32 cdn_dp_get_event(struct cdn_dp_device *dp);
> +int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
> +int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value);
> +int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len);
> +int cdn_dp_get_edid_block(void *dp, u8 *edid,
> +                         unsigned int block, size_t length);
> +int cdn_dp_training_start(struct cdn_dp_device *dp);
> +int cdn_dp_get_training_status(struct cdn_dp_device *dp);
> +int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active);
> +int cdn_dp_config_video(struct cdn_dp_device *dp);
> +int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio);
> +int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable);
> +int cdn_dp_audio_config_set(struct cdn_dp_device *dp, struct audio_info *audio);
> +#endif /* _CDN_DP_REG_H */
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> index edd7ec2..d723d8e 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
> @@ -969,9 +969,11 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
>                 vop_dsp_hold_valid_irq_disable(vop);
>         }
>
> -       pin_pol = 0x8;
> -       pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1;
> -       pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : (1 << 1);
> +       pin_pol = BIT(DCLK_INVERT);
> +       pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ?
> +                  0 : BIT(HSYNC_POSITIVE);
> +       pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ?
> +                  0 : BIT(VSYNC_POSITIVE);
>         VOP_CTRL_SET(vop, pin_pol, pin_pol);
>
>         switch (s->output_type) {
> @@ -991,6 +993,11 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
>                 VOP_CTRL_SET(vop, mipi_pin_pol, pin_pol);
>                 VOP_CTRL_SET(vop, mipi_en, 1);
>                 break;
> +       case DRM_MODE_CONNECTOR_DisplayPort:
> +               pin_pol &= ~BIT(DCLK_INVERT);
> +               VOP_CTRL_SET(vop, dp_pin_pol, pin_pol);
> +               VOP_CTRL_SET(vop, dp_en, 1);
> +               break;
>         default:
>                 DRM_ERROR("unsupport connector_type[%d]\n", s->output_type);
>         }
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> index ff4f52e..4820a8b 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
> @@ -45,6 +45,7 @@ struct vop_ctrl {
>         struct vop_reg edp_en;
>         struct vop_reg hdmi_en;
>         struct vop_reg mipi_en;
> +       struct vop_reg dp_en;
>         struct vop_reg out_mode;
>         struct vop_reg dither_down;
>         struct vop_reg dither_up;
> @@ -53,6 +54,7 @@ struct vop_ctrl {
>         struct vop_reg hdmi_pin_pol;
>         struct vop_reg edp_pin_pol;
>         struct vop_reg mipi_pin_pol;
> +       struct vop_reg dp_pin_pol;
>
>         struct vop_reg htotal_pw;
>         struct vop_reg hact_st_end;
> @@ -242,6 +244,13 @@ enum scale_down_mode {
>         SCALE_DOWN_AVG = 0x1
>  };
>
> +enum vop_pol {
> +       HSYNC_POSITIVE = 0,
> +       VSYNC_POSITIVE = 1,
> +       DEN_NEGATIVE   = 2,
> +       DCLK_INVERT    = 3
> +};
> +
>  #define FRAC_16_16(mult, div)    (((mult) << 16) / (div))
>  #define SCL_FT_DEFAULT_FIXPOINT_SHIFT  12
>  #define SCL_MAX_VSKIPLINES             4
> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> index 5b1ae1f..dcf172e 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
> @@ -281,6 +281,7 @@ static const struct vop_data rk3288_vop = {
>  static const struct vop_ctrl rk3399_ctrl_data = {
>         .standby = VOP_REG(RK3399_SYS_CTRL, 0x1, 22),
>         .gate_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 23),
> +       .dp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 11),
>         .rgb_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 12),
>         .hdmi_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 13),
>         .edp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 14),
> @@ -290,6 +291,7 @@ static const struct vop_ctrl rk3399_ctrl_data = {
>         .data_blank = VOP_REG(RK3399_DSP_CTRL0, 0x1, 19),
>         .out_mode = VOP_REG(RK3399_DSP_CTRL0, 0xf, 0),
>         .rgb_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16),
> +       .dp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16),
>         .hdmi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 20),
>         .edp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 24),
>         .mipi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 28),
> --
> 2.6.3
>


From kyle at infradead.org  Wed Jul 27 16:41:34 2016
From: kyle at infradead.org (Kyle McMartin)
Date: Wed, 27 Jul 2016 19:41:34 -0400
Subject: [PATCH] linux-firmware: rockchip: update dptx firmware to v2.6
In-Reply-To: <1469618694-10413-1-git-send-email-zyw@rock-chips.com>
References: <1469618694-10413-1-git-send-email-zyw@rock-chips.com>
Message-ID: <20160727234134.GM8680@merlin.infradead.org>

On Wed, Jul 27, 2016 at 07:24:54PM +0800, Chris Zhong wrote:
> Update firmware to v2.6, it support standby, controlled by mailbox
> commend. And update the licence.
> 
> Signed-off-by: Chris Zhong <zyw at rock-chips.com>

applied, thanks Chris.

regards, --Kyle


From mark.yao at rock-chips.com  Wed Jul 27 20:01:04 2016
From: mark.yao at rock-chips.com (Mark yao)
Date: Thu, 28 Jul 2016 11:01:04 +0800
Subject: [PATCH 1/3] drm: introduce share plane
In-Reply-To: <579732B2.80600@rock-chips.com>
References: <1469519194-23133-1-git-send-email-mark.yao@rock-chips.com>
 <20160726082635.GA31475@phenom.ffwll.local> <579732B2.80600@rock-chips.com>
Message-ID: <57997570.5070806@rock-chips.com>

Any ideas for the share planes?

This function is important for our series of vop full design.
     The series of vop is:
     IP version    chipname
     3.1           rk3288
     3.2           rk3368
     3.4           rk3366
     3.5           rk3399 big
     3.6           rk3399 lit
     3.7           rk322x

example on rk3288:  if not support share plane, each vop only support 
four planes, but if support this function, each vop can support ten planes.

On 2016?07?26? 17:51, Mark yao wrote:
> On 2016?07?26? 16:26, Daniel Vetter wrote:
>> On Tue, Jul 26, 2016 at 03:46:32PM +0800, Mark Yao wrote:
>>> >What is share plane:
>>> >Plane hardware only be used when the display scanout run into plane 
>>> active
>>> >scanout, that means we can reuse the plane hardware resources on plane
>>> >non-active scanout.
>>> >
>>> >      --------------------------------------------------
>>> >     |  scanout                                       |
>>> >     |         ------------------                     |
>>> >     |         | parent plane   |                     |
>>> >     |         | active scanout |                     |
>>> >     |         |                |   ----------------- |
>>> >     |         ------------------   | share plane 1 | |
>>> >     |  -----------------           |active scanout | |
>>> >     |  | share plane 0 |           |               | |
>>> >     |  |active scanout |           ----------------- |
>>> >     |  |               |                             |
>>> >     |  -----------------                             |
>>> >     --------------------------------------------------
>>> >One plane hardware can be reuse for multi-planes, we assume the first
>>> >plane is parent plane, other planes share the resource with first one.
>>> >     parent plane
>>> >         |---share plane 0
>>> >         |---share plane 1
>>> >         ...
>>> >
>>> >Because resource share, There are some limit on share plane: one group
>>> >of share planes need use same zpos, can not overlap, etc.
>>> >
>>> >We assume share plane is a universal plane with some limit flags.
>>> >people who use the share plane need know the limit, should call the 
>>> ioctl
>>> >DRM_CLIENT_CAP_SHARE_PLANES, and judge the planes limit before use it.
>>> >
>>> >A group of share planes would has same shard id, so userspace can
>>> >group them, judge share plane's limit.
>>> >
>>> >Signed-off-by: Mark Yao<mark.yao at rock-chips.com>
>> This seems extremely hw specific, why exactly do we need to add a new
>> relationship on planes? What does this buy on_other_  drivers?
> Yes, now it's plane hardware specific, maybe others have same design, 
> because this design
> would save hardware resource to support multi-planes.
>
>> Imo this should be solved by virtualizing planes in the driver. Start 
>> out
>> by assigning planes, and if you can reuse one for sharing then do that,
>> otherwise allocate a new one. If there's not enough real planes, fail 
>> the
>> atomic_check.
> I think that is too complex, trying with atomic_check I think it's not 
> a good idea, userspace try planes every commit would be a heavy work.
>
> Userspace need  know all planes relationship, group them, some display 
> windows can put together, some can't,
> too many permutation and combination, I think can't just commit with try.
>
> example:
> userspace:
> windows 1: pos(0, 0)  size(1024, 100)
> windows 2: pos(0, 50) size(400, 500)
> windows 3: pos(0, 200) size(800, 300)
>
> drm plane resources:
> plane 0 and plane 1 is a group of share planes
> plane 2 is common plane.
>
> if userspace know the relationship, then they can assign windows 1 and 
> window 3 to plane0 and plane 1. that would be success.
> but if they don't know, assign window 1/2 to plane 0/1, failed, assign 
> window 2/3 to plane 0/1, failed. mostly would get failed.
>
>>
>> This seems way to hw specific to be useful as a generic concept.
>
> We want to change the drm_mode_getplane_res behavior, if userspace 
> call DRM_CLIENT_CAP_SHARE_PLANES, that means userspace know hardware 
> limit,
> then we return full planes support to userspace, if don't, just make a 
> group of share planes as one plane.
> this work is on generic place.
>
>> -Daniel
>>
>>
>


-- 
?ark Yao




From ykk at rock-chips.com  Wed Jul 27 20:28:45 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Thu, 28 Jul 2016 11:28:45 +0800
Subject: [PATCH v1 2/2] drm/bridge: analogix_dp: turn off the panel when
 eDP need to disable
In-Reply-To: <CAOw6vbLXDuEW1egN3N8XxXdW8hdrY8W9T-v_d6hWDnRcRqZe_A@mail.gmail.com>
References: <1469106874-4912-1-git-send-email-ykk@rock-chips.com>
 <1469106886-4979-1-git-send-email-ykk@rock-chips.com>
 <CAOw6vbK7CnFwH_Tyrq6ab4DBxVLa8-JCYEM-Tu+mOYsKmNULtg@mail.gmail.com>
 <57917011.6000108@rock-chips.com>
 <CAOw6vbLXDuEW1egN3N8XxXdW8hdrY8W9T-v_d6hWDnRcRqZe_A@mail.gmail.com>
Message-ID: <57997BED.4050600@rock-chips.com>

Sean,

Oops, sorry about miss your suggest :(

On 07/22/2016 11:03 PM, Sean Paul wrote:
> On Thu, Jul 21, 2016 at 9:00 PM, Yakir Yang <ykk at rock-chips.com> wrote:
>> Sean,
>>
>> Thanks for your fast respond :-)
>>
>> But this patch is not the latest one, I have upgraded this to "v1.1" version
>> to fix the eDP can't be disabled problem:
>>      [PATCH v1.1 2/2] drm/bridge: analogix_dp: turn off the panel when eDP
>> need to disable
>>          Changes in v1.1: - unprepare the panel at the end of
>> bridge->disable() function
>>
>> In spite of this, I would take your comments with my "v1.1" patch.
>>
>> On 07/21/2016 10:28 PM, Sean Paul wrote:
>>
>> On Thu, Jul 21, 2016 at 9:14 AM, Yakir Yang <ykk at rock-chips.com> wrote:
>>
>> Some panels (like Sharp LQ123P1JX31) need to be turn off when eDP
>> controller stop to send valid video signal, otherwhise panel would
>> go burn in, and keep flicker and flicker.
>>
>> So it's better to turn off the panel when eDP need to disable, and
>> we need to turn on the panel in connector->detect() callback, so
>> that driver would detect panel status rightly.
>>
>> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
>> ---
>>   drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 18 ++++++++++--------
>>   1 file changed, 10 insertions(+), 8 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>> b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>> index 32715da..ea059b3 100644
>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>> @@ -961,6 +961,14 @@ analogix_dp_detect(struct drm_connector *connector,
>> bool force)
>>   {
>>          struct analogix_dp_device *dp = to_dp(connector);
>>
>> +       /*
>> +        * Panle would prepare for several times here, but don't worry it
>>
>> s/Panle/Panel/
>>
>>
>> Done
>>
>> +        * would only enable the hardware at the first prepare time.
>>
>> Errr, this shouldn't go in detect. How about putting this in
>> bridge_enable instead?
>>
>>
>> Nope, if we put this in bridge_enable, then eDP would never be enabled.
>> Here're the calling flow.
>>
>> --> analogix_dp_probe
>>    --> analogix_dp_bind  # we still haven't prepared the panel here, that
>> means panel have been powered up
>>      --> analogix_dp_detect  # Oops, losing panel valid hotplug signal, cause
>> panel have been powered up
>>      --> ** Keep detecting **
>
> Yeah, after playing around with the patch yesterday I discovered this
> for myself. I still don't think detect() should be changing hardware
> state. Perhaps you could add a matching unprepare() after you attempt
> to detect the panel?

Hmm, I don't understand the meaning of "add a matching unprepare()".

I didn't see there is an unprepare() callback in drm_crtc_helper.h, would
you like to share some simple code :-D

Thanks,
- Yakir

> Sean
>
>> +        */
>> +       if (dp->plat_data->panel)
>> +               if (drm_panel_prepare(dp->plat_data->panel))
>>
>> Personally, I don't like doing work in a conditional since you're
>> throwing the return code away. Could you break this out into:
>>
>> ret = drm_panel_prepare(dp->plat_data->panel);
>> if (ret)
>>    DRM_ERROR("failed to setup the panel ret=%d\n", ret);
>>
>>
>> Okay
>>
>>
>> +                       DRM_ERROR("failed to setup the panel\n");
>> +
>>          if (analogix_dp_detect_hpd(dp))
>>                  return connector_status_disconnected;
>>
>> @@ -1063,7 +1071,8 @@ static void analogix_dp_bridge_disable(struct
>> drm_bridge *bridge)
>>                  return;
>>
>>          if (dp->plat_data->panel) {
>> -               if (drm_panel_disable(dp->plat_data->panel)) {
>> +               if (drm_panel_disable(dp->plat_data->panel) ||
>> +                   drm_panel_unprepare(dp->plat_data->panel)) {
>>
>> Same comment here, please break this out into separate statements for
>> better readability/logging.
>>
>>
>> Okay,
>>
>>
>> Thanks,
>> - Yakir
>>
>>
>>                          DRM_ERROR("failed to disable the panel\n");
>>                          return;
>>                  }
>> @@ -1333,13 +1342,6 @@ int analogix_dp_bind(struct device *dev, struct
>> drm_device *drm_dev,
>>
>>          phy_power_on(dp->phy);
>>
>> -       if (dp->plat_data->panel) {
>> -               if (drm_panel_prepare(dp->plat_data->panel)) {
>> -                       DRM_ERROR("failed to setup the panel\n");
>> -                       return -EBUSY;
>> -               }
>> -       }
>> -
>>          analogix_dp_init_dp(dp);
>>
>>          ret = devm_request_threaded_irq(&pdev->dev, dp->irq,
>> --
>> 1.9.1
>>
>>
>>
>>
>>
>
>




From daniel at ffwll.ch  Thu Jul 28 01:03:02 2016
From: daniel at ffwll.ch (Daniel Vetter)
Date: Thu, 28 Jul 2016 10:03:02 +0200
Subject: [PATCH 1/3] drm: introduce share plane
In-Reply-To: <57997570.5070806@rock-chips.com>
References: <1469519194-23133-1-git-send-email-mark.yao@rock-chips.com>
 <20160726082635.GA31475@phenom.ffwll.local>
 <579732B2.80600@rock-chips.com> <57997570.5070806@rock-chips.com>
Message-ID: <20160728080302.GG31475@phenom.ffwll.local>

On Thu, Jul 28, 2016 at 11:01:04AM +0800, Mark yao wrote:
> Any ideas for the share planes?
> 
> This function is important for our series of vop full design.
>     The series of vop is:
>     IP version    chipname
>     3.1           rk3288
>     3.2           rk3368
>     3.4           rk3366
>     3.5           rk3399 big
>     3.6           rk3399 lit
>     3.7           rk322x
> 
> example on rk3288:  if not support share plane, each vop only support four
> planes, but if support this function, each vop can support ten planes.

Like I said, register 10 planes in the kernel driver, figure out a good
way to actually allocate them to hw resources. We have a similar issue on
skl/bxt in the i915 driver where there's only a limited amount of
scalers, and we need to dynamically allocate them to drm_plane. Here you
have fancy amount of scanout engines which you need to dynamically
allocate.

> On 2016?07?26? 17:51, Mark yao wrote:
> > On 2016?07?26? 16:26, Daniel Vetter wrote:
> > > On Tue, Jul 26, 2016 at 03:46:32PM +0800, Mark Yao wrote:
> > > > >What is share plane:
> > > > >Plane hardware only be used when the display scanout run into
> > > > plane active
> > > > >scanout, that means we can reuse the plane hardware resources on plane
> > > > >non-active scanout.
> > > > >
> > > > >      --------------------------------------------------
> > > > >     |  scanout                                       |
> > > > >     |         ------------------                     |
> > > > >     |         | parent plane   |                     |
> > > > >     |         | active scanout |                     |
> > > > >     |         |                |   ----------------- |
> > > > >     |         ------------------   | share plane 1 | |
> > > > >     |  -----------------           |active scanout | |
> > > > >     |  | share plane 0 |           |               | |
> > > > >     |  |active scanout |           ----------------- |
> > > > >     |  |               |                             |
> > > > >     |  -----------------                             |
> > > > >     --------------------------------------------------
> > > > >One plane hardware can be reuse for multi-planes, we assume the first
> > > > >plane is parent plane, other planes share the resource with first one.
> > > > >     parent plane
> > > > >         |---share plane 0
> > > > >         |---share plane 1
> > > > >         ...
> > > > >
> > > > >Because resource share, There are some limit on share plane: one group
> > > > >of share planes need use same zpos, can not overlap, etc.
> > > > >
> > > > >We assume share plane is a universal plane with some limit flags.
> > > > >people who use the share plane need know the limit, should call
> > > > the ioctl
> > > > >DRM_CLIENT_CAP_SHARE_PLANES, and judge the planes limit before use it.
> > > > >
> > > > >A group of share planes would has same shard id, so userspace can
> > > > >group them, judge share plane's limit.
> > > > >
> > > > >Signed-off-by: Mark Yao<mark.yao at rock-chips.com>
> > > This seems extremely hw specific, why exactly do we need to add a new
> > > relationship on planes? What does this buy on_other_  drivers?
> > Yes, now it's plane hardware specific, maybe others have same design,
> > because this design
> > would save hardware resource to support multi-planes.
> > 
> > > Imo this should be solved by virtualizing planes in the driver.
> > > Start out
> > > by assigning planes, and if you can reuse one for sharing then do that,
> > > otherwise allocate a new one. If there's not enough real planes,
> > > fail the
> > > atomic_check.
> > I think that is too complex, trying with atomic_check I think it's not a
> > good idea, userspace try planes every commit would be a heavy work.
> > 
> > Userspace need  know all planes relationship, group them, some display
> > windows can put together, some can't,
> > too many permutation and combination, I think can't just commit with try.
> > 
> > example:
> > userspace:
> > windows 1: pos(0, 0)  size(1024, 100)
> > windows 2: pos(0, 50) size(400, 500)
> > windows 3: pos(0, 200) size(800, 300)
> > 
> > drm plane resources:
> > plane 0 and plane 1 is a group of share planes
> > plane 2 is common plane.
> > 
> > if userspace know the relationship, then they can assign windows 1 and
> > window 3 to plane0 and plane 1. that would be success.
> > but if they don't know, assign window 1/2 to plane 0/1, failed, assign
> > window 2/3 to plane 0/1, failed. mostly would get failed.

You can still do this with the design I describe. The only difference is
that you allow generic userspace to make optimal use of your planes, too.

> > > This seems way to hw specific to be useful as a generic concept.
> > 
> > We want to change the drm_mode_getplane_res behavior, if userspace call
> > DRM_CLIENT_CAP_SHARE_PLANES, that means userspace know hardware limit,
> > then we return full planes support to userspace, if don't, just make a
> > group of share planes as one plane.
> > this work is on generic place.

So ... do you have patches for all the generic kms userspace that's out
there? Are those reviewed and ready for merging?

Adding new userspace abi is much, much, much harder than sovling this in
the vop driver. I'm working on some documentation to make this all clear
(since many arm folks seem unaware of the uapi rules we have in the drm
subsystem). But really, you're trying the much harder route with this
patch.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


From mark.yao at rock-chips.com  Thu Jul 28 02:28:24 2016
From: mark.yao at rock-chips.com (Mark yao)
Date: Thu, 28 Jul 2016 17:28:24 +0800
Subject: [PATCH 1/3] drm: introduce share plane
In-Reply-To: <20160728080302.GG31475@phenom.ffwll.local>
References: <1469519194-23133-1-git-send-email-mark.yao@rock-chips.com>
 <20160726082635.GA31475@phenom.ffwll.local> <579732B2.80600@rock-chips.com>
 <57997570.5070806@rock-chips.com> <20160728080302.GG31475@phenom.ffwll.local>
Message-ID: <5799D038.5000906@rock-chips.com>

On 2016?07?28? 16:03, Daniel Vetter wrote:
> On Thu, Jul 28, 2016 at 11:01:04AM +0800, Mark yao wrote:
>> Any ideas for the share planes?
>>
>> This function is important for our series of vop full design.
>>      The series of vop is:
>>      IP version    chipname
>>      3.1           rk3288
>>      3.2           rk3368
>>      3.4           rk3366
>>      3.5           rk3399 big
>>      3.6           rk3399 lit
>>      3.7           rk322x
>>
>> example on rk3288:  if not support share plane, each vop only support four
>> planes, but if support this function, each vop can support ten planes.
> Like I said, register 10 planes in the kernel driver, figure out a good
> way to actually allocate them to hw resources. We have a similar issue on
> skl/bxt in the i915 driver where there's only a limited amount of
> scalers, and we need to dynamically allocate them to drm_plane. Here you
> have fancy amount of scanout engines which you need to dynamically
> allocate.
>
>> On 2016?07?26? 17:51, Mark yao wrote:
>>> On 2016?07?26? 16:26, Daniel Vetter wrote:
>>>> On Tue, Jul 26, 2016 at 03:46:32PM +0800, Mark Yao wrote:
>>>>>> What is share plane:
>>>>>> Plane hardware only be used when the display scanout run into
>>>>> plane active
>>>>>> scanout, that means we can reuse the plane hardware resources on plane
>>>>>> non-active scanout.
>>>>>>
>>>>>>       --------------------------------------------------
>>>>>>      |  scanout                                       |
>>>>>>      |         ------------------                     |
>>>>>>      |         | parent plane   |                     |
>>>>>>      |         | active scanout |                     |
>>>>>>      |         |                |   ----------------- |
>>>>>>      |         ------------------   | share plane 1 | |
>>>>>>      |  -----------------           |active scanout | |
>>>>>>      |  | share plane 0 |           |               | |
>>>>>>      |  |active scanout |           ----------------- |
>>>>>>      |  |               |                             |
>>>>>>      |  -----------------                             |
>>>>>>      --------------------------------------------------
>>>>>> One plane hardware can be reuse for multi-planes, we assume the first
>>>>>> plane is parent plane, other planes share the resource with first one.
>>>>>>      parent plane
>>>>>>          |---share plane 0
>>>>>>          |---share plane 1
>>>>>>          ...
>>>>>>
>>>>>> Because resource share, There are some limit on share plane: one group
>>>>>> of share planes need use same zpos, can not overlap, etc.
>>>>>>
>>>>>> We assume share plane is a universal plane with some limit flags.
>>>>>> people who use the share plane need know the limit, should call
>>>>> the ioctl
>>>>>> DRM_CLIENT_CAP_SHARE_PLANES, and judge the planes limit before use it.
>>>>>>
>>>>>> A group of share planes would has same shard id, so userspace can
>>>>>> group them, judge share plane's limit.
>>>>>>
>>>>>> Signed-off-by: Mark Yao<mark.yao at rock-chips.com>
>>>> This seems extremely hw specific, why exactly do we need to add a new
>>>> relationship on planes? What does this buy on_other_  drivers?
>>> Yes, now it's plane hardware specific, maybe others have same design,
>>> because this design
>>> would save hardware resource to support multi-planes.
>>>
>>>> Imo this should be solved by virtualizing planes in the driver.
>>>> Start out
>>>> by assigning planes, and if you can reuse one for sharing then do that,
>>>> otherwise allocate a new one. If there's not enough real planes,
>>>> fail the
>>>> atomic_check.
>>> I think that is too complex, trying with atomic_check I think it's not a
>>> good idea, userspace try planes every commit would be a heavy work.
>>>
>>> Userspace need  know all planes relationship, group them, some display
>>> windows can put together, some can't,
>>> too many permutation and combination, I think can't just commit with try.
>>>
>>> example:
>>> userspace:
>>> windows 1: pos(0, 0)  size(1024, 100)
>>> windows 2: pos(0, 50) size(400, 500)
>>> windows 3: pos(0, 200) size(800, 300)
>>>
>>> drm plane resources:
>>> plane 0 and plane 1 is a group of share planes
>>> plane 2 is common plane.
>>>
>>> if userspace know the relationship, then they can assign windows 1 and
>>> window 3 to plane0 and plane 1. that would be success.
>>> but if they don't know, assign window 1/2 to plane 0/1, failed, assign
>>> window 2/3 to plane 0/1, failed. mostly would get failed.
> You can still do this with the design I describe. The only difference is
> that you allow generic userspace to make optimal use of your planes, too.
>
>>>> This seems way to hw specific to be useful as a generic concept.
>>> We want to change the drm_mode_getplane_res behavior, if userspace call
>>> DRM_CLIENT_CAP_SHARE_PLANES, that means userspace know hardware limit,
>>> then we return full planes support to userspace, if don't, just make a
>>> group of share planes as one plane.
>>> this work is on generic place.
> So ... do you have patches for all the generic kms userspace that's out
> there? Are those reviewed and ready for merging?
No, we have no other patches send to generic kms userspace upstream.

We use it on our internal userspace application now.

on our userspace application:
1, directly call drmSetClientCap(fd(), DRM_CLIENT_CAP_SHARE_PLANES, 1);
2, get planes with drmModeGetResources(fd());
3, get plane share id with drmModeObjectGetProperties,
4, group the planes with share id, if two plane use same "share id", 
that means these plane is on the same group
5, judge the plane's limit when doing plane commit.

on userspace only add a new ioctl macro DRM_CLIENT_CAP_SHARE_PLANES.

> Adding new userspace abi is much, much, much harder than sovling this in
> the vop driver. I'm working on some documentation to make this all clear
> (since many arm folks seem unaware of the uapi rules we have in the drm
> subsystem). But really, you're trying the much harder route with this
> patch.
Hmmm, So sad, you are right, change the generic api is a harder work.

Ok, I will try your advice.

Thanks.

> -Daniel

-- 
?ark Yao




From seanpaul at chromium.org  Thu Jul 28 07:06:15 2016
From: seanpaul at chromium.org (Sean Paul)
Date: Thu, 28 Jul 2016 10:06:15 -0400
Subject: [PATCH v1 2/2] drm/bridge: analogix_dp: turn off the panel when
 eDP need to disable
In-Reply-To: <57997BED.4050600@rock-chips.com>
References: <1469106874-4912-1-git-send-email-ykk@rock-chips.com>
 <1469106886-4979-1-git-send-email-ykk@rock-chips.com>
 <CAOw6vbK7CnFwH_Tyrq6ab4DBxVLa8-JCYEM-Tu+mOYsKmNULtg@mail.gmail.com>
 <57917011.6000108@rock-chips.com>
 <CAOw6vbLXDuEW1egN3N8XxXdW8hdrY8W9T-v_d6hWDnRcRqZe_A@mail.gmail.com>
 <57997BED.4050600@rock-chips.com>
Message-ID: <CAOw6vbKbLzm4mPtp2LsG=7=KJkS_4Oe7rA4Of0uT5vM_Xoiphw@mail.gmail.com>

On Wed, Jul 27, 2016 at 11:28 PM, Yakir Yang <ykk at rock-chips.com> wrote:
> Sean,
>
> Oops, sorry about miss your suggest :(
>
>
> On 07/22/2016 11:03 PM, Sean Paul wrote:
>>
>> On Thu, Jul 21, 2016 at 9:00 PM, Yakir Yang <ykk at rock-chips.com> wrote:
>>>
>>> Sean,
>>>
>>> Thanks for your fast respond :-)
>>>
>>> But this patch is not the latest one, I have upgraded this to "v1.1"
>>> version
>>> to fix the eDP can't be disabled problem:
>>>      [PATCH v1.1 2/2] drm/bridge: analogix_dp: turn off the panel when
>>> eDP
>>> need to disable
>>>          Changes in v1.1: - unprepare the panel at the end of
>>> bridge->disable() function
>>>
>>> In spite of this, I would take your comments with my "v1.1" patch.
>>>
>>> On 07/21/2016 10:28 PM, Sean Paul wrote:
>>>
>>> On Thu, Jul 21, 2016 at 9:14 AM, Yakir Yang <ykk at rock-chips.com> wrote:
>>>
>>> Some panels (like Sharp LQ123P1JX31) need to be turn off when eDP
>>> controller stop to send valid video signal, otherwhise panel would
>>> go burn in, and keep flicker and flicker.
>>>
>>> So it's better to turn off the panel when eDP need to disable, and
>>> we need to turn on the panel in connector->detect() callback, so
>>> that driver would detect panel status rightly.
>>>
>>> Signed-off-by: Yakir Yang <ykk at rock-chips.com>
>>> ---
>>>   drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 18
>>> ++++++++++--------
>>>   1 file changed, 10 insertions(+), 8 deletions(-)
>>>
>>> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>>> b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>>> index 32715da..ea059b3 100644
>>> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>>> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>>> @@ -961,6 +961,14 @@ analogix_dp_detect(struct drm_connector *connector,
>>> bool force)
>>>   {
>>>          struct analogix_dp_device *dp = to_dp(connector);
>>>
>>> +       /*
>>> +        * Panle would prepare for several times here, but don't worry it
>>>
>>> s/Panle/Panel/
>>>
>>>
>>> Done
>>>
>>> +        * would only enable the hardware at the first prepare time.
>>>
>>> Errr, this shouldn't go in detect. How about putting this in
>>> bridge_enable instead?
>>>
>>>
>>> Nope, if we put this in bridge_enable, then eDP would never be enabled.
>>> Here're the calling flow.
>>>
>>> --> analogix_dp_probe
>>>    --> analogix_dp_bind  # we still haven't prepared the panel here, that
>>> means panel have been powered up
>>>      --> analogix_dp_detect  # Oops, losing panel valid hotplug signal,
>>> cause
>>> panel have been powered up
>>>      --> ** Keep detecting **
>>
>>
>> Yeah, after playing around with the patch yesterday I discovered this
>> for myself. I still don't think detect() should be changing hardware
>> state. Perhaps you could add a matching unprepare() after you attempt
>> to detect the panel?
>
>
> Hmm, I don't understand the meaning of "add a matching unprepare()".
>
> I didn't see there is an unprepare() callback in drm_crtc_helper.h, would
> you like to share some simple code :-D
>

In other words, make sure the hardware is in the same state at the
beginning and end of detect(). You shouldn't have the panel in a
different state when you exit detect().

Sean



> Thanks,
> - Yakir
>
>
>> Sean
>>
>>> +        */
>>> +       if (dp->plat_data->panel)
>>> +               if (drm_panel_prepare(dp->plat_data->panel))
>>>
>>> Personally, I don't like doing work in a conditional since you're
>>> throwing the return code away. Could you break this out into:
>>>
>>> ret = drm_panel_prepare(dp->plat_data->panel);
>>> if (ret)
>>>    DRM_ERROR("failed to setup the panel ret=%d\n", ret);
>>>
>>>
>>> Okay
>>>
>>>
>>> +                       DRM_ERROR("failed to setup the panel\n");
>>> +
>>>          if (analogix_dp_detect_hpd(dp))
>>>                  return connector_status_disconnected;
>>>
>>> @@ -1063,7 +1071,8 @@ static void analogix_dp_bridge_disable(struct
>>> drm_bridge *bridge)
>>>                  return;
>>>
>>>          if (dp->plat_data->panel) {
>>> -               if (drm_panel_disable(dp->plat_data->panel)) {
>>> +               if (drm_panel_disable(dp->plat_data->panel) ||
>>> +                   drm_panel_unprepare(dp->plat_data->panel)) {
>>>
>>> Same comment here, please break this out into separate statements for
>>> better readability/logging.
>>>
>>>
>>> Okay,
>>>
>>>
>>> Thanks,
>>> - Yakir
>>>
>>>
>>>                          DRM_ERROR("failed to disable the panel\n");
>>>                          return;
>>>                  }
>>> @@ -1333,13 +1342,6 @@ int analogix_dp_bind(struct device *dev, struct
>>> drm_device *drm_dev,
>>>
>>>          phy_power_on(dp->phy);
>>>
>>> -       if (dp->plat_data->panel) {
>>> -               if (drm_panel_prepare(dp->plat_data->panel)) {
>>> -                       DRM_ERROR("failed to setup the panel\n");
>>> -                       return -EBUSY;
>>> -               }
>>> -       }
>>> -
>>>          analogix_dp_init_dp(dp);
>>>
>>>          ret = devm_request_threaded_irq(&pdev->dev, dp->irq,
>>> --
>>> 1.9.1
>>>
>>>
>>>
>>>
>>>
>>
>>
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc"
> in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


From thierry.reding at gmail.com  Thu Jul 28 07:13:25 2016
From: thierry.reding at gmail.com (Thierry Reding)
Date: Thu, 28 Jul 2016 16:13:25 +0200
Subject: [PATCH 2/2] dt-bindings: add simple-panel-dsi and simple-panel
In-Reply-To: <57982469.6030201@rock-chips.com>
References: <1468984730-23186-1-git-send-email-mark.yao@rock-chips.com>
 <1468984730-23186-2-git-send-email-mark.yao@rock-chips.com>
 <20160725152125.GS21170@ulmo.ba.sec>
 <5796C47C.5040208@rock-chips.com>
 <20160726090226.GB2433@ulmo.ba.sec>
 <57982469.6030201@rock-chips.com>
Message-ID: <20160728141325.GB28342@ulmo.ba.sec>

On Wed, Jul 27, 2016 at 11:03:05AM +0800, Mark yao wrote:
> On 2016?07?26? 17:02, Thierry Reding wrote:
> > On Tue, Jul 26, 2016 at 10:01:32AM +0800, Mark yao wrote:
> > > On 2016?07?25? 23:21, Thierry Reding wrote:
> > > 
> > >      On Wed, Jul 20, 2016 at 11:18:50AM +0800, Mark Yao wrote:
> > > 
> > >          Allow user add display timing on device tree with simple-panel-dsi
> > >          or simple-panel.
> > > 
> > >          Cc: Thierry Reding <thierry.reding at gmail.com>
> > >          Cc: Rob Herring <robh+dt at kernel.org>
> > >          Cc: Mark Rutland <mark.rutland at arm.com>
> > > 
> > >          Signed-off-by: Mark Yao <mark.yao at rock-chips.com>
> > >          ---
> > >           .../bindings/display/panel/simple-panel.txt        | 48 ++++++++++++++++++++++
> > >           1 file changed, 48 insertions(+)
> > > 
> > >      Sorry, not going to happen. Read this for an explanation of why not:
> > > 
> > >              https://sietch-tagr.blogspot.de/2016/04/display-panels-are-not-special.html
> > > 
> > >      Thierry
> > > 
> > > 
> > > Hi Thierry
> > > 
> > > The blog actually not persuade me why can't use display timing on
> > > device tree.
> > Okay, perhaps read it again, it addresses most of your points below.
> > 
> > > 1, Binding panel as a simple string on device tree seems simple on device tree,
> > > but it's complex on kernel code, and kernel code would became bigger and
> > > bigger.
> > I don't think the video timings in the simple-panel driver are very
> > complex. They also don't use very much space. And if you're really
> > concerned about space you can always use conditional compilation and
> > Kconfig symbols to remove timings for panels that you don't use.
> > 
> > Also, panels are characterized by much more than just video timings.
> > There were attempts, way back, to fully describe panels in device tree
> > and that failed. What you propose here is a partial solution to a much
> > more complex problem.
> > 
> > This is all explained in the blog post.
> > 
> > > 2, Our customer always ask me, where is the display timing? They only find a
> > > simple panel string on device tree,  need search the kernel code to find the
> > > actually timing. They are used to find all device info on device tree, but
> > > panel timing info is not, this would confuse them. They don't want to know how
> > > code work, just want a easier interface.
> > That's not a very good argument. There's plenty of data that's not in
> > device tree for other devices, why should panels be different? Also, I
> > would hope that any customer of yours knows their way around kernel
> > code and can therefore easily add video timings for new panels. It's
> > quite trivial to do, and there are many examples on how to do it.
> > 
> > > 3, I think device tree not only can use for kernel, other module also can use
> > > it. on our project, we use uboot + kernel, the uboot support fdt, that function
> > > can parsing device tree. So if describe the display timing on device tree, both
> > > uboot and kernel can share same display timing, not need to describe twice, it
> > > would save work and not easy to make mistake.
> > That's a bit of a stretch. Video timings is fairly straightforward data
> > and can be easily added to any other piece of code that you want to run.
> > Yes you will have to duplicate the data, but how is that different from
> > duplicating all the driver code?
> > 
> > > 4, For differentiation product, we face many different panel, every once in a
> > > while, need to add a new panel, we can't convert all the panel , code the panel
> > > on kernel seems too bad, and the kernel image became bigger and bigger.
> > Why can't you convert all the panels? We already support a bunch of them
> > and haven't yet run into any problems. If you do encounter any issues
> > trying to port panels to the DRM panel infrastructure, please let me
> > know and I can help sort them out.
> > 
> > The kernel image size isn't a problem either. In any modern kernel the
> > video timing data in the panel driver is tiny compared to the rest.
> > 
> > > Generally, Our customer don't want to do any modify on kernel, they just modify
> > > device tree to bring up their device. Describe the panel timing on device tree,
> > > would make customer easy to use and reuse it.
> > Yes, that would perhaps make it easier for them to bring up the device.
> > But soon after they'll notice that there are glitches when turning the
> > panel on and off, and then they'll realize that they can't fix that
> > using their simple device tree.
> About the panel on and off, I don't think the panel-simple do the good
> enough.
> 
> panel-simple only have one gpio and one regulator, and their sequence is
> hard code, Why not a panel have two gpio or two regulator? On our project,
> we find many customer don't use the RC to do panel reset, they directly use
> gpio reset, so they need a gpio to do panel reset.

The driver is called panel-simple for a reason. It's not meant to cover
all possible use-cases, only the simple ones.

> the device tree panel's on and off function is what the next step I want to
> upstream, on our downstream kernel, we do like that:
> 
> panel {
>     power_ctrl {
>           power0 {
>                   gpios = <xxx>;
>                   delay,ms = <3>;
>           }
>           power1 {
>                   regulator = <xxx>;
>                   delay,ms = <3>;
>           }
>           power2 {
>                   backlight = <xxx>;
>                   delay,ms = <3>;
>           }
>     }
> }
> and on driver, power on sequence with power0->power1->power2, power down
> with power2->power1->power0.
> if user want to swap the power, can easy do that by  adjust dts power
> sequence.
> 
> this method can easy order the gpio, regulator, backlight sequence, judge
> the delay time and add new regulator or gpio.
> I think this panel power on and off method is better than panel-simple
> driver currently using.

That's almost exactly like the power sequences that Alex Courbot had
proposed about three years ago. The concept of such a thing was rejected
by device tree binding maintainers, which is why we ended up with what
we have now. I'm sure you can find a link to the discussion if you want
the details about why it got rejected.

All of that's described in the blog, by the way.

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160728/1e412141/attachment.sig>

From robh+dt at kernel.org  Thu Jul 28 12:00:21 2016
From: robh+dt at kernel.org (Rob Herring)
Date: Thu, 28 Jul 2016 14:00:21 -0500
Subject: [PATCH 2/2] dt-bindings: add simple-panel-dsi and simple-panel
In-Reply-To: <20160726090226.GB2433@ulmo.ba.sec>
References: <1468984730-23186-1-git-send-email-mark.yao@rock-chips.com>
 <1468984730-23186-2-git-send-email-mark.yao@rock-chips.com>
 <20160725152125.GS21170@ulmo.ba.sec> <5796C47C.5040208@rock-chips.com>
 <20160726090226.GB2433@ulmo.ba.sec>
Message-ID: <CAL_JsqL8D3OK_JQcY+dLTcqcvmgB2Vuc0Tknf1zsR=8Pa3F4jw@mail.gmail.com>

On Tue, Jul 26, 2016 at 4:02 AM, Thierry Reding
<thierry.reding at gmail.com> wrote:
> On Tue, Jul 26, 2016 at 10:01:32AM +0800, Mark yao wrote:
>> On 2016?07?25? 23:21, Thierry Reding wrote:
>>
>>     On Wed, Jul 20, 2016 at 11:18:50AM +0800, Mark Yao wrote:
>>
>>         Allow user add display timing on device tree with simple-panel-dsi
>>         or simple-panel.
>>
>>         Cc: Thierry Reding <thierry.reding at gmail.com>
>>         Cc: Rob Herring <robh+dt at kernel.org>
>>         Cc: Mark Rutland <mark.rutland at arm.com>
>>
>>         Signed-off-by: Mark Yao <mark.yao at rock-chips.com>
>>         ---
>>          .../bindings/display/panel/simple-panel.txt        | 48 ++++++++++++++++++++++
>>          1 file changed, 48 insertions(+)
>>
>>     Sorry, not going to happen. Read this for an explanation of why not:
>>
>>             https://sietch-tagr.blogspot.de/2016/04/display-panels-are-not-special.html
>>
>>     Thierry
>>
>>
>> Hi Thierry
>>
>> The blog actually not persuade me why can't use display timing on
>> device tree.
>
> Okay, perhaps read it again, it addresses most of your points below.
>
>> 1, Binding panel as a simple string on device tree seems simple on device tree,
>> but it's complex on kernel code, and kernel code would became bigger and
>> bigger.
>
> I don't think the video timings in the simple-panel driver are very
> complex. They also don't use very much space. And if you're really
> concerned about space you can always use conditional compilation and
> Kconfig symbols to remove timings for panels that you don't use.
>
> Also, panels are characterized by much more than just video timings.
> There were attempts, way back, to fully describe panels in device tree
> and that failed. What you propose here is a partial solution to a much
> more complex problem.
>
> This is all explained in the blog post.

While I agree with everything Thierry has said in this thread, there
is an argument to be made for timing information in DT.

Maybe most vendors have now learned that flexible clock frequencies
are needed to generate the correct timings, but I have worked on parts
without fine resolution (i.e. a dedicated pll). Even HiKey with HDMI
suffers from this. In that case you can be tuning the timings
(increasing the h and/or v blanks) to get the desired refresh rates.
So in that case, the panel compatible would not determine the display
timings and you could have the same panel with different timings on
different boards. That's maybe rare enough that putting timing info in
DT still wouldn't make sense.

But that is not the problem here, and everything else about this is wrong.

Rob


From smbarber at chromium.org  Thu Jul 28 14:53:02 2016
From: smbarber at chromium.org (Stephen Barber)
Date: Thu, 28 Jul 2016 14:53:02 -0700
Subject: [PATCH 1/2] thermal: rockchip: enhance the tsadc's bandgap
 feature for rk3399
In-Reply-To: <1469628643-29661-1-git-send-email-wxt@rock-chips.com>
References: <1469628643-29661-1-git-send-email-wxt@rock-chips.com>
Message-ID: <CADR9n49WW9e+YLVmpjBtMU_r7Y+zDpuz48J_RtGBpdnuYNvDXg@mail.gmail.com>

On Wed, Jul 27, 2016 at 7:10 AM, Caesar Wang <wxt at rock-chips.com> wrote:
> From: Rocky Hao <rocky.hao at rock-chips.com>
>
> Due to the voltage ripple, the sensing data of the tsadc is not accurate.
> And in this patch, the bandgap feature is enhanced to remove the voltage
> ripple, and then the tsadc can sense the temperature more precisely.
>
> Obsolete codes are removed as well.
>
> Signed-off-by: Rocky Hao <rocky.hao at rock-chips.com>
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
> Cc: Eduardo Valentin <edubezval at gmail.com>
> Cc: Zhang Rui <rui.zhang at intel.com>
> Cc: Heiko Stuebner <heiko at sntech.de>
> Cc: linux-pm at vger.kernel.org
> ---
>
>  drivers/thermal/rockchip_thermal.c | 11 ++++++-----
>  1 file changed, 6 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c
> index db5ecc5..a90423d 100644
> --- a/drivers/thermal/rockchip_thermal.c
> +++ b/drivers/thermal/rockchip_thermal.c
> @@ -222,10 +222,10 @@ struct rockchip_thermal_data {
>  #define GRF_TSADC_TESTBIT_L                    0x0e648
>  #define GRF_TSADC_TESTBIT_H                    0x0e64c
>
> -#define GRF_TSADC_TSEN_PD_ON                   (0x30003 << 0)
> -#define GRF_TSADC_TSEN_PD_OFF                  (0x30000 << 0)
>  #define GRF_SARADC_TESTBIT_ON                  (0x10001 << 2)
>  #define GRF_TSADC_TESTBIT_H_ON                 (0x10001 << 2)
> +#define GRF_TSADC_VCM_EN_L                     (0x10001 << 7)
> +#define GRF_TSADC_VCM_EN_H                     (0x10001 << 7)
>
>  /**
>   * struct tsadc_table - code to temperature conversion table
> @@ -567,9 +567,10 @@ static void rk_tsadcv3_initialize(struct regmap *grf, void __iomem *regs,
>                                regs + TSADCV2_HIGHT_TSHUT_DEBOUNCE);
>
>         } else {
> -               regmap_write(grf, GRF_TSADC_TESTBIT_L, GRF_TSADC_TSEN_PD_ON);
> -               mdelay(10);
> -               regmap_write(grf, GRF_TSADC_TESTBIT_L, GRF_TSADC_TSEN_PD_OFF);
> +               /* Enable the voltage common mode feature */
> +               regmap_write(grf, GRF_TSADC_TESTBIT_L, GRF_TSADC_VCM_EN_L);
> +               regmap_write(grf, GRF_TSADC_TESTBIT_H, GRF_TSADC_VCM_EN_H);
> +
>                 usleep_range(15, 100); /* The spec note says at least 15 us */
>                 regmap_write(grf, GRF_SARADC_TESTBIT, GRF_SARADC_TESTBIT_ON);
>                 regmap_write(grf, GRF_TSADC_TESTBIT_H, GRF_TSADC_TESTBIT_H_ON);
> --
> 1.9.1
>

Tested-by: Stephen Barber <smbarber at chromium.org>

Steve


From smbarber at chromium.org  Thu Jul 28 14:53:28 2016
From: smbarber at chromium.org (Stephen Barber)
Date: Thu, 28 Jul 2016 14:53:28 -0700
Subject: [PATCH 2/2] thermal: rockchip: optimize sensor auto accessing
 period
In-Reply-To: <1469628643-29661-2-git-send-email-wxt@rock-chips.com>
References: <1469628643-29661-1-git-send-email-wxt@rock-chips.com>
 <1469628643-29661-2-git-send-email-wxt@rock-chips.com>
Message-ID: <CADR9n4__hcQosSOM5w0PMw8ck_gPi4Sc5ixbN6CiKKGia2gd7A@mail.gmail.com>

On Wed, Jul 27, 2016 at 7:10 AM, Caesar Wang <wxt at rock-chips.com> wrote:
> From: Rocky Hao <rocky.hao at rock-chips.com>
>
> In less than 10 ms, the temperature of soc will arise 10 degree. 250 ms
> is too big for soc tempeture control. Setting 2.5 ms will speed up
> temperature accessing speed but introduce no more cpu's computing overhead.
> We set AUTO_PERIOD_TIME and TSADCV3_AUTO_PERIOD_HT_TIME the same value,
> because normal temperature update speed is also our consern in IPA.
>
> Signed-off-by: Rocky Hao <rocky.hao at rock-chips.com>
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
> Cc: Zhang Rui <rui.zhang at intel.com>
> Cc: Eduardo Valentin <edubezval at gmail.com>
> Cc: Heiko Stuebner <heiko at sntech.de>
> Cc: linux-pm at vger.kernel.org
>
> ---
>
>  drivers/thermal/rockchip_thermal.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c
> index a90423d..1f165c9 100644
> --- a/drivers/thermal/rockchip_thermal.c
> +++ b/drivers/thermal/rockchip_thermal.c
> @@ -213,8 +213,8 @@ struct rockchip_thermal_data {
>  #define TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT     4
>  #define TSADCV2_AUTO_PERIOD_TIME               250 /* 250ms */
>  #define TSADCV2_AUTO_PERIOD_HT_TIME            50  /* 50ms */
> -#define TSADCV3_AUTO_PERIOD_TIME               187500 /* 250ms */
> -#define TSADCV3_AUTO_PERIOD_HT_TIME            37500  /* 50ms */
> +#define TSADCV3_AUTO_PERIOD_TIME               1875 /* 2.5ms */
> +#define TSADCV3_AUTO_PERIOD_HT_TIME            1875 /* 2.5ms */
>
>  #define TSADCV2_USER_INTER_PD_SOC              0x340 /* 13 clocks */
>
> --
> 1.9.1
>


Tested-by: Stephen Barber <smbarber at chromium.org>

Steve


From shawn.lin at rock-chips.com  Thu Jul 28 18:22:05 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Fri, 29 Jul 2016 09:22:05 +0800
Subject: [PATCH v4 1/2] Documentation: bindings: add dt documentation for
 Rockchip PCIe PHY
Message-ID: <1469755326-10263-1-git-send-email-shawn.lin@rock-chips.com>

This patch adds a binding that describes the Rockchip PCIe PHY
found on Rockchip SoCs PCIe interface.

Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>

---

Changes in v4: None
Changes in v3:
- rename the node to pcie_phy: pcie-phy suggested by Doug

Changes in v2:
- add clk and reset description
- remove unit-address

 .../devicetree/bindings/phy/rockchip-pcie-phy.txt  | 32 ++++++++++++++++++++++
 1 file changed, 32 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/rockchip-pcie-phy.txt

diff --git a/Documentation/devicetree/bindings/phy/rockchip-pcie-phy.txt b/Documentation/devicetree/bindings/phy/rockchip-pcie-phy.txt
new file mode 100644
index 0000000..aedca29
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/rockchip-pcie-phy.txt
@@ -0,0 +1,32 @@
+Rockchip PCIE PHY
+-----------------------
+
+Required properties:
+ - compatible: rockchip,rk3399-pcie-phy
+ - #phy-cells: must be 0
+ - clocks: Must contain an entry in clock-names.
+	See ../clocks/clock-bindings.txt for details.
+ - clock-names: Must be "refclk"
+ - resets: Must contain an entry in reset-names.
+	See ../reset/reset.txt for details.
+ - reset-names: Must be "phy"
+
+Example:
+
+grf: syscon at ff770000 {
+	compatible = "rockchip,rk3399-grf", "syscon", "simple-mfd";
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	...
+
+	pcie_phy: pcie-phy {
+		compatible = "rockchip,rk3399-pcie-phy";
+		#phy-cells = <0>;
+		clocks = <&cru SCLK_PCIEPHY_REF>;
+		clock-names = "refclk";
+		resets = <&cru SRST_PCIEPHY>;
+		reset-names = "phy";
+	};
+};
+
-- 
2.3.7




From shawn.lin at rock-chips.com  Thu Jul 28 18:22:06 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Fri, 29 Jul 2016 09:22:06 +0800
Subject: [PATCH v4 2/2] phy: add a driver for the Rockchip SoC internal PCIe
 PHY
In-Reply-To: <1469755326-10263-1-git-send-email-shawn.lin@rock-chips.com>
References: <1469755326-10263-1-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <1469755326-10263-2-git-send-email-shawn.lin@rock-chips.com>

This patch to add a generic PHY driver for rockchip PCIe PHY.
Access the PHY via registers provided by GRF (general register
files) module.

Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>

---

Changes in v4:
- remove laneoff symbol as we still fail to get a workable solution
  except for exporting symbol. But I will be back with some other possible
  routine once finished.

Changes in v3: None
Changes in v2: None

 drivers/phy/Kconfig             |   7 +
 drivers/phy/Makefile            |   1 +
 drivers/phy/phy-rockchip-pcie.c | 357 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 365 insertions(+)
 create mode 100644 drivers/phy/phy-rockchip-pcie.c

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 02afc624..824b568 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -371,6 +371,13 @@ config PHY_ROCKCHIP_DP
 	help
 	  Enable this to support the Rockchip Display Port PHY.
 
+config PHY_ROCKCHIP_PCIE
+	tristate "Rockchip PCIe PHY Driver"
+	depends on ARCH_ROCKCHIP && OF
+	select GENERIC_PHY
+	help
+	  Enable this to support the Rockchip PCIe PHY.
+
 config PHY_ST_SPEAR1310_MIPHY
 	tristate "ST SPEAR1310-MIPHY driver"
 	select GENERIC_PHY
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index fa8480e..0a9dcd8 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_PHY_EXYNOS5_USBDRD)	+= phy-exynos5-usbdrd.o
 obj-$(CONFIG_PHY_QCOM_APQ8064_SATA)	+= phy-qcom-apq8064-sata.o
 obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o
 obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o
+obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o
 obj-$(CONFIG_PHY_ROCKCHIP_DP)		+= phy-rockchip-dp.o
 obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA)	+= phy-qcom-ipq806x-sata.o
 obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY)	+= phy-spear1310-miphy.o
diff --git a/drivers/phy/phy-rockchip-pcie.c b/drivers/phy/phy-rockchip-pcie.c
new file mode 100644
index 0000000..63cd200
--- /dev/null
+++ b/drivers/phy/phy-rockchip-pcie.c
@@ -0,0 +1,357 @@
+/*
+ * Rockchip PCIe PHY driver
+ *
+ * Copyright (C) 2016 Shawn Lin <shawn.lin at rock-chips.com>
+ * Copyright (C) 2016 ROCKCHIP, Inc.
+ *
+ * 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/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+/*
+ * The higher 16-bit of this register is used for write protection
+ * only if BIT(x + 16) set to 1 the BIT(x) can be written.
+ */
+#define HIWORD_UPDATE(val, mask, shift) \
+		((val) << (shift) | (mask) << ((shift) + 16))
+
+#define PHY_MAX_LANE_NUM      4
+#define PHY_CFG_DATA_SHIFT    7
+#define PHY_CFG_ADDR_SHIFT    1
+#define PHY_CFG_DATA_MASK     0xf
+#define PHY_CFG_ADDR_MASK     0x3f
+#define PHY_CFG_RD_MASK       0x3ff
+#define PHY_CFG_WR_ENABLE     1
+#define PHY_CFG_WR_DISABLE    1
+#define PHY_CFG_WR_SHIFT      0
+#define PHY_CFG_WR_MASK       1
+#define PHY_CFG_PLL_LOCK      0x10
+#define PHY_CFG_CLK_TEST      0x10
+#define PHY_CFG_CLK_SCC       0x12
+#define PHY_CFG_SEPE_RATE     BIT(3)
+#define PHY_CFG_PLL_100M      BIT(3)
+#define PHY_PLL_LOCKED        BIT(9)
+#define PHY_PLL_OUTPUT        BIT(10)
+#define PHY_LANE_A_STATUS     0x30
+#define PHY_LANE_B_STATUS     0x31
+#define PHY_LANE_C_STATUS     0x32
+#define PHY_LANE_D_STATUS     0x33
+#define PHY_LANE_RX_DET_SHIFT 11
+#define PHY_LANE_RX_DET_TH    0x1
+#define PHY_LANE_IDLE_OFF     0x1
+#define PHY_LANE_IDLE_MASK    0x1
+#define PHY_LANE_IDLE_A_SHIFT 3
+#define PHY_LANE_IDLE_B_SHIFT 4
+#define PHY_LANE_IDLE_C_SHIFT 5
+#define PHY_LANE_IDLE_D_SHIFT 6
+
+struct rockchip_pcie_data {
+	unsigned int pcie_conf;
+	unsigned int pcie_status;
+	unsigned int pcie_laneoff;
+};
+
+struct rockchip_pcie_phy {
+	struct rockchip_pcie_data *phy_data;
+	struct regmap *reg_base;
+	struct reset_control *phy_rst;
+	struct clk *clk_pciephy_ref;
+};
+
+static inline void phy_wr_cfg(struct rockchip_pcie_phy *rk_phy,
+			      u32 addr, u32 data)
+{
+	regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
+		     HIWORD_UPDATE(data,
+				   PHY_CFG_DATA_MASK,
+				   PHY_CFG_DATA_SHIFT) |
+		     HIWORD_UPDATE(addr,
+				   PHY_CFG_ADDR_MASK,
+				   PHY_CFG_ADDR_SHIFT));
+	udelay(1);
+	regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
+		     HIWORD_UPDATE(PHY_CFG_WR_ENABLE,
+				   PHY_CFG_WR_MASK,
+				   PHY_CFG_WR_SHIFT));
+	udelay(1);
+	regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
+		     HIWORD_UPDATE(PHY_CFG_WR_DISABLE,
+				   PHY_CFG_WR_MASK,
+				   PHY_CFG_WR_SHIFT));
+}
+
+static inline u32 phy_rd_cfg(struct rockchip_pcie_phy *rk_phy,
+			     u32 addr)
+{
+	u32 val;
+
+	regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
+		     HIWORD_UPDATE(addr,
+				   PHY_CFG_RD_MASK,
+				   PHY_CFG_ADDR_SHIFT));
+	regmap_read(rk_phy->reg_base,
+		    rk_phy->phy_data->pcie_status,
+		    &val);
+	return val;
+}
+
+static int rockchip_pcie_phy_power_off(struct phy *phy)
+{
+	struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy);
+	int err = 0;
+
+	err = reset_control_assert(rk_phy->phy_rst);
+	if (err) {
+		pr_err("assert phy_rst err %d\n", err);
+		return err;
+	}
+
+	return 0;
+}
+
+static int rockchip_pcie_phy_power_on(struct phy *phy)
+{
+	struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy);
+	int err = 0;
+	u32 status;
+	unsigned long timeout;
+
+	err = reset_control_deassert(rk_phy->phy_rst);
+	if (err) {
+		pr_err("deassert phy_rst err %d\n", err);
+		return err;
+	}
+
+	regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
+		     HIWORD_UPDATE(PHY_CFG_PLL_LOCK,
+				   PHY_CFG_ADDR_MASK,
+				   PHY_CFG_ADDR_SHIFT));
+
+	/*
+	 * No documented timeout value for phy operation below,
+	 * so we make it large enough here. And we use loop-break
+	 * method which should not be harmful.
+	 */
+	timeout = jiffies + msecs_to_jiffies(1000);
+
+	err = -EINVAL;
+	while (time_before(jiffies, timeout)) {
+		regmap_read(rk_phy->reg_base,
+			    rk_phy->phy_data->pcie_status,
+			    &status);
+		if (status & PHY_PLL_LOCKED) {
+			pr_debug("pll locked!\n");
+			err = 0;
+			break;
+		}
+		msleep(20);
+	}
+
+	if (err) {
+		pr_err("pll lock timeout!\n");
+		goto err_pll_lock;
+	}
+
+	phy_wr_cfg(rk_phy, PHY_CFG_CLK_TEST, PHY_CFG_SEPE_RATE);
+	phy_wr_cfg(rk_phy, PHY_CFG_CLK_SCC, PHY_CFG_PLL_100M);
+
+	err = -ETIMEDOUT;
+	while (time_before(jiffies, timeout)) {
+		regmap_read(rk_phy->reg_base,
+			    rk_phy->phy_data->pcie_status,
+			    &status);
+		if (!(status & PHY_PLL_OUTPUT)) {
+			pr_debug("pll output enable done!\n");
+			err = 0;
+			break;
+		}
+		msleep(20);
+	}
+
+	if (err) {
+		pr_err("pll output enable timeout!\n");
+		goto err_pll_lock;
+	}
+
+	regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
+		     HIWORD_UPDATE(PHY_CFG_PLL_LOCK,
+				   PHY_CFG_ADDR_MASK,
+				   PHY_CFG_ADDR_SHIFT));
+	err = -EINVAL;
+	while (time_before(jiffies, timeout)) {
+		regmap_read(rk_phy->reg_base,
+			    rk_phy->phy_data->pcie_status,
+			    &status);
+		if (status & PHY_PLL_LOCKED) {
+			pr_debug("pll relocked!\n");
+			err = 0;
+			break;
+		}
+		msleep(20);
+	}
+
+	if (err) {
+		pr_err("pll relock timeout!\n");
+		goto err_pll_lock;
+	}
+
+	return 0;
+
+err_pll_lock:
+	reset_control_assert(rk_phy->phy_rst);
+	return err;
+}
+
+static int rockchip_pcie_phy_init(struct phy *phy)
+{
+	struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy);
+	int err = 0;
+
+	err = clk_prepare_enable(rk_phy->clk_pciephy_ref);
+	if (err) {
+		pr_err("Fail to enable pcie ref clock.\n");
+		goto err_refclk;
+	}
+
+	err = reset_control_assert(rk_phy->phy_rst);
+	if (err) {
+		pr_err("assert phy_rst err %d\n", err);
+		goto err_reset;
+	}
+
+	return err;
+
+err_reset:
+	clk_disable_unprepare(rk_phy->clk_pciephy_ref);
+err_refclk:
+	return err;
+}
+
+static int rockchip_pcie_phy_exit(struct phy *phy)
+{
+	struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy);
+	int err = 0;
+
+	clk_disable_unprepare(rk_phy->clk_pciephy_ref);
+
+	err = reset_control_deassert(rk_phy->phy_rst);
+	if (err) {
+		pr_err("deassert phy_rst err %d\n", err);
+		goto err_reset;
+	}
+
+	return err;
+
+err_reset:
+	clk_prepare_enable(rk_phy->clk_pciephy_ref);
+	return err;
+}
+
+static const struct phy_ops ops = {
+	.init		= rockchip_pcie_phy_init,
+	.exit		= rockchip_pcie_phy_exit,
+	.power_on	= rockchip_pcie_phy_power_on,
+	.power_off	= rockchip_pcie_phy_power_off,
+	.owner		= THIS_MODULE,
+};
+
+static const struct rockchip_pcie_data rk3399_pcie_data = {
+	.pcie_conf = 0xe220,
+	.pcie_status = 0xe2a4,
+	.pcie_laneoff = 0xe214,
+};
+
+static const struct of_device_id rockchip_pcie_phy_dt_ids[] = {
+	{
+		.compatible = "rockchip,rk3399-pcie-phy",
+		.data = &rk3399_pcie_data,
+	},
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, rockchip_pcie_phy_dt_ids);
+
+static int rockchip_pcie_phy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct rockchip_pcie_phy *rk_phy;
+	struct phy *generic_phy;
+	struct phy_provider *phy_provider;
+	struct regmap *grf;
+	const struct of_device_id *of_id;
+
+	grf = syscon_node_to_regmap(dev->parent->of_node);
+	if (IS_ERR(grf)) {
+		dev_err(dev, "Missing rockchip,grf property\n");
+		return PTR_ERR(grf);
+	}
+
+	rk_phy = devm_kzalloc(dev, sizeof(*rk_phy), GFP_KERNEL);
+	if (!rk_phy)
+		return -ENOMEM;
+
+	of_id = of_match_device(rockchip_pcie_phy_dt_ids, &pdev->dev);
+	if (!of_id)
+		return -EINVAL;
+
+	rk_phy->phy_data = (struct rockchip_pcie_data *)of_id->data;
+	rk_phy->reg_base = grf;
+
+	rk_phy->phy_rst = devm_reset_control_get(dev, "phy");
+	if (IS_ERR(rk_phy->phy_rst)) {
+		if (PTR_ERR(rk_phy->phy_rst) != -EPROBE_DEFER)
+			dev_err(dev,
+				"missing phy property for reset controller\n");
+		return PTR_ERR(rk_phy->phy_rst);
+	}
+
+	rk_phy->clk_pciephy_ref = devm_clk_get(dev, "refclk");
+	if (IS_ERR(rk_phy->clk_pciephy_ref)) {
+		dev_err(dev, "refclk not found.\n");
+		return PTR_ERR(rk_phy->clk_pciephy_ref);
+	}
+
+	generic_phy = devm_phy_create(dev, dev->of_node, &ops);
+	if (IS_ERR(generic_phy)) {
+		dev_err(dev, "failed to create PHY\n");
+		return PTR_ERR(generic_phy);
+	}
+
+	phy_set_drvdata(generic_phy, rk_phy);
+	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+	return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static struct platform_driver rockchip_pcie_driver = {
+	.probe		= rockchip_pcie_phy_probe,
+	.driver		= {
+		.name	= "rockchip-pcie-phy",
+		.of_match_table = rockchip_pcie_phy_dt_ids,
+	},
+};
+
+module_platform_driver(rockchip_pcie_driver);
+
+MODULE_AUTHOR("Shawn Lin <shawn.lin at rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip PCIe PHY driver");
+MODULE_LICENSE("GPL v2");
-- 
2.3.7




From shawn.lin at rock-chips.com  Thu Jul 28 19:48:12 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Fri, 29 Jul 2016 10:48:12 +0800
Subject: [PATCH v8 1/2] Documentation: bindings: add dt doc for Rockchip PCIe
 controller
Message-ID: <1469760493-25821-1-git-send-email-shawn.lin@rock-chips.com>

This patch adds a binding that describes the Rockchip PCIe controller
found on Rockchip SoCs PCIe interface.

Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>

Acked-by: Rob Herring <robh at kernel.org>
Reviewed-by: Brian Norris <briannorris at chromium.org>
---

Changes in v8:
- add Brian's review-tag

Changes in v7:
- move optional properties ahead of interrupt subnode
- use 0-base index for INTx

Changes in v6:
- add ack tag from Rob

Changes in v5:
- fix wrong example reported by Marc
- add seperate section to describe the interrupt controller child
  node

Changes in v4:
- fix example of adding intermediate interrupt controller for pcie
  legacy interrrupt

Changes in v3:
- fix example dts code suggested by Rob and Marc
- remove driver's behaviour of regulator

Changes in v2:
- fix lots clk/reset stuff suggested by Heiko
- remove msi-parent and add msi-map suggested by Marc
- drop phy related stuff
- some others minor fixes

 .../devicetree/bindings/pci/rockchip-pcie.txt      | 106 +++++++++++++++++++++
 1 file changed, 106 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/rockchip-pcie.txt

diff --git a/Documentation/devicetree/bindings/pci/rockchip-pcie.txt b/Documentation/devicetree/bindings/pci/rockchip-pcie.txt
new file mode 100644
index 0000000..3f74c87
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/rockchip-pcie.txt
@@ -0,0 +1,106 @@
+* Rockchip AXI PCIe Root Port Bridge DT description
+
+Required properties:
+- #address-cells: Address representation for root ports, set to <3>
+- #size-cells: Size representation for root ports, set to <2>
+- #interrupt-cells: specifies the number of cells needed to encode an
+		interrupt source. The value must be 1.
+- compatible: Should contain "rockchip,rk3399-pcie"
+- reg: Two register ranges as listed in the reg-names property
+- reg-names: Must include the following names
+	- "axi-base"
+	- "apb-base"
+- clocks: Must contain an entry for each entry in clock-names.
+		See ../clocks/clock-bindings.txt for details.
+- clock-names: Must include the following entries:
+	- "aclk"
+	- "aclk-perf"
+	- "hclk"
+	- "pm"
+- msi-map: Maps a Requester ID to an MSI controller and associated.
+		See ./pci-msi.txt
+- phys: From PHY bindings: Phandle for the Generic PHY for PCIe.
+- phy-names:  MUST be "pcie-phy".
+- interrupts: Three interrupt entries must be specified.
+- interrupt-names: Must include the following names
+	- "sys"
+	- "legacy"
+	- "client"
+- resets: Must contain five entries for each entry in reset-names.
+	   See ../reset/reset.txt for details.
+- reset-names: Must include the following names
+	- "core"
+	- "mgmt"
+	- "mgmt-sticky"
+	- "pipe"
+- pinctrl-names : The pin control state names
+- pinctrl-0: The "default" pinctrl state
+- #interrupt-cells: specifies the number of cells needed to encode an
+	interrupt source. The value must be 1.
+- interrupt-map-mask and interrupt-map: standard PCI properties
+
+Optional Property:
+- ep-gpios: contain the entry for pre-reset gpio
+- num-lanes: number of lanes to use
+- vpcie3v3-supply: The phandle to the 3.3v regulator to use for pcie.
+- vpcie1v8-supply: The phandle to the 1.8v regulator to use for pcie.
+- vpcie0v9-supply: The phandle to the 0.9v regulator to use for pcie.
+
+*Interrupt controller child node*
+The core controller provides a single interrupt for legacy INTx. The pcie node
+should contain an interrupt controller node as a target for the PCI
+'interrupt-map' property. This node represents the domain at which the four
+INTx interrupts are decoded and routed.
+
+
+Required properties for Interrupt controller child node:
+- interrupt-controller: identifies the node as an interrupt controller
+- #address-cells: specifies the number of cells needed to encode an
+	address. The value must be 0.
+- #interrupt-cells: specifies the number of cells needed to encode an
+	interrupt source. The value must be 1.
+
+Example:
+
+pcie0: pcie at f8000000 {
+	compatible = "rockchip,rk3399-pcie";
+	#address-cells = <3>;
+	#size-cells = <2>;
+	clocks = <&cru ACLK_PCIE>, <&cru ACLK_PERF_PCIE>,
+		 <&cru PCLK_PCIE>, <&cru SCLK_PCIE_PM>;
+	clock-names = "aclk", "aclk-perf",
+		      "hclk", "pm";
+	bus-range = <0x0 0x1>;
+	interrupts = <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH 0>,
+		     <GIC_SPI 50 IRQ_TYPE_LEVEL_HIGH 0>,
+		     <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH 0>;
+	interrupt-names = "sys", "legacy", "client";
+	assigned-clocks = <&cru SCLK_PCIEPHY_REF>;
+	assigned-clock-parents = <&cru SCLK_PCIEPHY_REF100M>;
+	assigned-clock-rates = <100000000>;
+	ep-gpios = <&gpio3 13 GPIO_ACTIVE_HIGH>;
+	ranges = <0x83000000 0x0 0xfa000000 0x0 0xfa000000 0x0 0x600000
+		  0x81000000 0x0 0xfa600000 0x0 0xfa600000 0x0 0x100000>;
+	num-lanes = <4>;
+	msi-map = <0x0 &its 0x0 0x1000>;
+	reg = <0x0 0xf8000000 0x0 0x2000000>, <0x0 0xfd000000 0x0 0x1000000>;
+	reg-names = "axi-base", "apb-base";
+	resets = <&cru SRST_PCIE_CORE>, <&cru SRST_PCIE_MGMT>,
+		 <&cru SRST_PCIE_MGMT_STICKY>, <&cru SRST_PCIE_PIPE>;
+	reset-names = "core", "mgmt", "mgmt-sticky", "pipe";
+	phys = <&pcie_phy>;
+	phy-names = "pcie-phy";
+	pinctrl-names = "default";
+	pinctrl-0 = <&pcie_clkreq>;
+	#interrupt-cells = <1>;
+	interrupt-map-mask = <0 0 0 7>;
+	interrupt-map = <0 0 0 1 &pcie0_intc 0>,
+			<0 0 0 2 &pcie0_intc 1>,
+			<0 0 0 3 &pcie0_intc 2>,
+			<0 0 0 4 &pcie0_intc 3>;
+	pcie0_intc: interrupt-controller {
+		interrupt-controller;
+		#address-cells = <0>;
+		#interrupt-cells = <1>;
+	};
+};
-- 
2.3.7




From shawn.lin at rock-chips.com  Thu Jul 28 19:48:13 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Fri, 29 Jul 2016 10:48:13 +0800
Subject: [PATCH v8 2/2] PCI: Rockchip: Add Rockchip PCIe controller support
In-Reply-To: <1469760493-25821-1-git-send-email-shawn.lin@rock-chips.com>
References: <1469760493-25821-1-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <1469760493-25821-2-git-send-email-shawn.lin@rock-chips.com>

This patch adds Rockchip PCIe controller support found
on RK3399 Soc platform.

Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>

Reviewed-by: Brian Norris <briannorris at chromium.org>
---

Changes in v8:
- fix all the comments suggested by Bjorn Helgaas in v7[1]
  [1] https://patchwork.kernel.org/patch/9233887/
- enable bandwith interrupt for debug

Changes in v7:
- make it as a build-in driver
- improve gen1/2 training timeout checking
- only clear known interrupt
- fix INTx for 0-base index

Changes in v6:
- use "depends on PCI_MSI_IRQ_DOMAIN" suggested by Arnd

Changes in v5:
- handle multiple pending INTx at the same time
  suggested by Marc

Changes in v4:
- address the comments from Brain

Changes in v3:
- remove header file
- remove struct msi_controller and move most of variables
  of rockchip_pcie_port to become the local ones.
- allow legacy int even if enabling MSI
- drop regulator set voltage operation suggested by Doug

Changes in v2:
- remove phy related stuff and call phy API
- add new head file and define lots of macro to make
  the code more readable
- remove lots msi related code suggested by Marc
- add IO window address translation
- init_port and parse_dt reconstruction suggested by Bharat
- improve wr_own_conf suggested by Arnd
- make pcie as an interrupt controller and fix wrong int handler
  suggested by Marc
- remove PCI_PROBE_ONLY suggested by Lorenzo

 drivers/pci/host/Kconfig         |   11 +
 drivers/pci/host/Makefile        |    1 +
 drivers/pci/host/pcie-rockchip.c | 1257 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 1269 insertions(+)
 create mode 100644 drivers/pci/host/pcie-rockchip.c

diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index c917e2f..4d714d2 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -264,4 +264,15 @@ config PCIE_ARTPEC6
 	  Say Y here to enable PCIe controller support on Axis ARTPEC-6
 	  SoCs.  This PCIe controller uses the DesignWare core.
 
+config PCIE_ROCKCHIP
+	bool "Rockchip PCIe controller"
+	depends on ARM64 && ARCH_ROCKCHIP
+	depends on OF
+	depends on PCI_MSI_IRQ_DOMAIN
+	select MFD_SYSCON
+	help
+	  Say Y here if you want internal PCI support on Rockchip SoC.
+	  There is 1 internal PCIe port available to support GEN2 with
+	  4 slots.
+
 endmenu
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index 5bc0af2..695f716 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -30,3 +30,4 @@ obj-$(CONFIG_PCI_HOST_THUNDER_ECAM) += pci-thunder-ecam.o
 obj-$(CONFIG_PCI_HOST_THUNDER_PEM) += pci-thunder-pem.o
 obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o
 obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o
+obj-$(CONFIG_PCIE_ROCKCHIP) += pcie-rockchip.o
diff --git a/drivers/pci/host/pcie-rockchip.c b/drivers/pci/host/pcie-rockchip.c
new file mode 100644
index 0000000..13ac199
--- /dev/null
+++ b/drivers/pci/host/pcie-rockchip.c
@@ -0,0 +1,1257 @@
+/*
+ * Rockchip AXI PCIe host controller driver
+ *
+ * Copyright (c) 2016 Rockchip, Inc.
+ *
+ * Author: Shawn Lin <shawn.lin at rock-chips.com>
+ *         Wenrui Li <wenrui.li at rock-chips.com>
+ *
+ * Bits taken from Synopsys Designware Host controller driver and
+ * ARM PCI Host generic driver.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_pci.h>
+#include <linux/of_platform.h>
+#include <linux/of_irq.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/regmap.h>
+
+#define PCIE_CLIENT_BASE                   0x0
+#define PCIE_RC_CONFIG_NORMAL_BASE         0x800000
+#define PCIE_RC_CONFIG_BASE                0xa00000
+#define PCIE_RC_CONFIG_LCSR_OFFSET         0xd0
+#define PCIE_RC_CONFIG_LCSR_BW_ENA         0x1
+#define PCIE_RC_CONFIG_LCSR_LBMIE          BIT(10)
+#define PCIE_RC_CONFIG_LCSR_LABIE          BIT(11)
+#define PCIE_RC_CONFIG_LCSR_LBMS           BIT(30)
+#define PCIE_RC_CONFIG_LCSR_LAMS           BIT(31)
+#define PCIE_CORE_LINK_CTRL_STATUS         0x8000d0
+#define PCIE_CORE_CTRL_MGMT_BASE           0x900000
+#define PCIE_CORE_AXI_CONF_BASE            0xc00000
+#define PCIE_CORE_AXI_INBOUND_BASE         0xc00800
+#define PCIE_CLIENT_BASIC_STATUS0          0x44
+#define PCIE_CLIENT_BASIC_STATUS1          0x48
+#define PCIE_CLIENT_INT_MASK               0x4c
+#define PCIE_CLIENT_INT_STATUS             0x50
+#define PCIE_CLIENT_INT_LEGACY_DONE        BIT(15)
+#define PCIE_CLIENT_INT_MSG                BIT(14)
+#define PCIE_CLIENT_INT_HOT_RST            BIT(13)
+#define PCIE_CLIENT_INT_DPA                BIT(12)
+#define PCIE_CLIENT_INT_FATAL_ERR          BIT(11)
+#define PCIE_CLIENT_INT_NFATAL_ERR         BIT(10)
+#define PCIE_CLIENT_INT_CORR_ERR           BIT(9)
+#define PCIE_CLIENT_INT_INTD               BIT(8)
+#define PCIE_CLIENT_INT_INTC               BIT(7)
+#define PCIE_CLIENT_INT_INTB               BIT(6)
+#define PCIE_CLIENT_INT_INTA               BIT(5)
+#define PCIE_CLIENT_INT_LOCAL              BIT(4)
+#define PCIE_CLIENT_INT_UDMA               BIT(3)
+#define PCIE_CLIENT_INT_PHY                BIT(2)
+#define PCIE_CLIENT_INT_HOT_PLUG           BIT(1)
+#define PCIE_CLIENT_INT_PWR_STCG           BIT(0)
+#define PCIE_RC_CONFIG_RID_CCR             0x8
+#define PCIE_RC_CONFIG_LCS                 0xd0
+#define PCIE_RC_BAR_CONF                   0x300
+#define PCIE_CORE_OB_REGION_ADDR1          0x4
+#define PCIE_CORE_OB_REGION_DESC0          0x8
+#define PCIE_CORE_OB_REGION_DESC1          0xc
+#define PCIE_CORE_OB_REGION_ADDR0_NUM_BITS 0x3fU
+#define PCIE_CORE_OB_REGION_ADDR0_LO_ADDR  0xffffff00U
+#define PCIE_CORE_IB_REGION_ADDR0_NUM_BITS 0x3fU
+#define PCIE_CORE_IB_REGION_ADDR0_LO_ADDR  0xffffff00U
+#define PCIE_RP_IB_ADDR_TRANS              0x4
+#define PCIE_CORE_INT_MASK                 0x900210
+#define PCIE_CORE_INT_STATUS               0x90020c
+#define PCIE_CORE_INT_PRFPE                BIT(0)
+#define PCIE_CORE_INT_CRFPE                BIT(1)
+#define PCIE_CORE_INT_RRPE                 BIT(2)
+#define PCIE_CORE_INT_PRFO                 BIT(3)
+#define PCIE_CORE_INT_CRFO                 BIT(4)
+#define PCIE_CORE_INT_RT                   BIT(5)
+#define PCIE_CORE_INT_RTR                  BIT(6)
+#define PCIE_CORE_INT_PE                   BIT(7)
+#define PCIE_CORE_INT_MTR                  BIT(8)
+#define PCIE_CORE_INT_UCR                  BIT(9)
+#define PCIE_CORE_INT_FCE                  BIT(10)
+#define PCIE_CORE_INT_CT                   BIT(11)
+#define PCIE_CORE_INT_UTC                  BIT(18)
+#define PCIE_CORE_INT_MMVC                 BIT(19)
+
+/* Size of one AXI Region (not Region 0) */
+#define AXI_REGION_SIZE                    BIT(20)
+/* Size of Region 0, equal to sum of sizes of other regions */
+#define AXI_REGION_0_SIZE                 (32 * (0x1 << 20))
+#define OB_REG_SIZE_SHIFT                  5
+#define IB_ROOT_PORT_REG_SIZE_SHIFT        3
+#define AXI_WRAPPER_IO_WRITE               0x6
+#define AXI_WRAPPER_MEM_WRITE              0x2
+#define MAX_AXI_IB_ROOTPORT_REGION_NUM     3
+#define MIN_AXI_ADDR_BITS_PASSED           8
+#define ROCKCHIP_VENDOR_ID                 0x1d87
+#define PCIE_ECAM_BUS(x)		  (((x) & 0xff) << 20)
+#define PCIE_ECAM_DEV(x)		  (((x) & 0x1f) << 15)
+#define PCIE_ECAM_FUNC(x)		  (((x) & 0x7) << 12)
+#define PCIE_ECAM_REG(x)		  (((x) & 0xfff) << 0)
+#define PCIE_ECAM_ADDR(bus, dev, func, reg) \
+	  (PCIE_ECAM_BUS(bus) | PCIE_ECAM_DEV(dev) | \
+	   PCIE_ECAM_FUNC(func) | PCIE_ECAM_REG(reg))
+
+/*
+  * The higher 16-bit of this register is used for write protection
+  * only if BIT(x + 16) set to 1 the BIT(x) can be written.
+  */
+#define HIWORD_UPDATE(val, mask, shift) \
+	((val) << (shift) | (mask) << ((shift) + 16))
+
+#define RC_REGION_0_ADDR_TRANS_H          0x00000000
+#define RC_REGION_0_ADDR_TRANS_L          0x00000000
+#define RC_REGION_0_PASS_BITS             (25 - 1)
+#define RC_REGION_1_ADDR_TRANS_H          0x00000000
+#define RC_REGION_1_ADDR_TRANS_L          0x00400000
+#define RC_REGION_1_PASS_BITS             (20 - 1)
+#define MAX_AXI_WRAPPER_REGION_NUM        33
+#define PCIE_CORE_LCSR_RETRAIN_LINK       BIT(5)
+#define PCIE_CLIENT_CONF_ENABLE           1
+#define PCIE_CLIENT_CONF_ENABLE_SHIFT     0
+#define PCIE_CLIENT_CONF_ENABLE_MASK      0x1
+#define PCIE_CLIENT_LINK_TRAIN_ENABLE     1
+#define PCIE_CLIENT_LINK_TRAIN_SHIFT      1
+#define PCIE_CLIENT_LINK_TRAIN_MASK       0x1
+#define PCIE_CLIENT_ARI_ENABLE            1
+#define PCIE_CLIENT_ARI_ENABLE_SHIFT      3
+#define PCIE_CLIENT_ARI_ENABLE_MASK       0x1
+#define PCIE_CLIENT_CONF_LANE_NUM(x)      (x / 2)
+#define PCIE_CLIENT_CONF_LANE_NUM_SHIFT   4
+#define PCIE_CLIENT_CONF_LANE_NUM_MASK    0x3
+#define PCIE_CLIENT_MODE_RC               1
+#define PCIE_CLIENT_MODE_SHIFT            6
+#define PCIE_CLIENT_MODE_MASK             0x1
+#define PCIE_CLIENT_GEN_SEL_2             1
+#define PCIE_CLIENT_GEN_SEL_1             0
+#define PCIE_CLIENT_GEN_SEL_SHIFT         7
+#define PCIE_CLIENT_GEN_SEL_MASK          0x1
+#define PCIE_CLIENT_LINK_STATUS_UP        0x3
+#define PCIE_CLIENT_LINK_STATUS_SHIFT     20
+#define PCIE_CLIENT_LINK_STATUS_MASK      0x3
+#define PCIE_CORE_PL_CONF_SPEED_2_5G      0x0
+#define PCIE_CORE_PL_CONF_SPEED_5G        0x1
+#define PCIE_CORE_PL_CONF_SPEED_8G        0x2
+#define PCIE_CORE_PL_CONF_SPEED_SHIFT     3
+#define PCIE_CORE_PL_CONF_SPEED_MASK      0x3
+#define PCIE_CORE_PL_CONF_LANE_SHIFT      1
+#define PCIE_CORE_PL_CONF_LANE_MASK       0x3
+#define PCIE_CORE_RC_CONF_SCC_SHIFT       16
+
+#define ROCKCHIP_PCIE_RPIFR1_INTR_MASK    GENMASK(8, 5)
+#define ROCKCHIP_PCIE_RPIFR1_INTR_SHIFT   5
+
+#define PCIE_CORE_INT \
+		(PCIE_CORE_INT_PRFPE | PCIE_CORE_INT_CRFPE | \
+		 PCIE_CORE_INT_RRPE | PCIE_CORE_INT_CRFO | \
+		 PCIE_CORE_INT_RT | PCIE_CORE_INT_RTR | \
+		 PCIE_CORE_INT_PE | PCIE_CORE_INT_MTR | \
+		 PCIE_CORE_INT_UCR | PCIE_CORE_INT_FCE | \
+		 PCIE_CORE_INT_CT | PCIE_CORE_INT_UTC | \
+		 PCIE_CORE_INT_MMVC)
+
+#define PCIE_CLIENT_INT_SUBSYSTEM \
+	(PCIE_CLIENT_INT_PWR_STCG | PCIE_CLIENT_INT_HOT_PLUG | \
+	PCIE_CLIENT_INT_PHY | PCIE_CLIENT_INT_UDMA | \
+	PCIE_CLIENT_INT_LOCAL)
+
+#define PCIE_CLIENT_INT_LEGACY \
+	(PCIE_CLIENT_INT_INTA | PCIE_CLIENT_INT_INTB | \
+	PCIE_CLIENT_INT_INTC | PCIE_CLIENT_INT_INTD)
+
+#define PCIE_CLIENT_INT_CLI \
+	(PCIE_CLIENT_INT_CORR_ERR | PCIE_CLIENT_INT_NFATAL_ERR | \
+	PCIE_CLIENT_INT_FATAL_ERR | PCIE_CLIENT_INT_DPA | \
+	PCIE_CLIENT_INT_HOT_RST | PCIE_CLIENT_INT_MSG | \
+	PCIE_CLIENT_INT_LEGACY_DONE | PCIE_CLIENT_INT_LEGACY | \
+	PCIE_CLIENT_INT_PHY)
+
+struct rockchip_pcie_port {
+	void	__iomem *reg_base;
+	void	__iomem *apb_base;
+	struct	phy *phy;
+	struct	reset_control *core_rst;
+	struct	reset_control *mgmt_rst;
+	struct	reset_control *mgmt_sticky_rst;
+	struct	reset_control *pipe_rst;
+	struct	clk *aclk_pcie;
+	struct	clk *aclk_perf_pcie;
+	struct	clk *hclk_pcie;
+	struct	clk *clk_pcie_pm;
+	struct	regulator *vpcie3v3; /* 3.3V power supply */
+	struct	regulator *vpcie1v8; /* 1.8V power supply */
+	struct	regulator *vpcie0v9; /* 0.9V power supply */
+	struct	gpio_desc *ep_gpio;
+	u32	lanes;
+	u8	root_bus_nr;
+	struct	device *dev;
+	struct	irq_domain *irq_domain;
+};
+
+static inline u32 pcie_read(struct rockchip_pcie_port *port, u32 reg)
+{
+	return readl(port->apb_base + reg);
+}
+
+static inline void pcie_write(struct rockchip_pcie_port *port,
+			      u32 val, u32 reg)
+{
+	writel(val, port->apb_base + reg);
+}
+
+static void rockchip_pcie_enable_bw_int(struct rockchip_pcie_port *port)
+{
+	u32 status;
+
+	status = pcie_read(port, PCIE_RC_CONFIG_BASE +
+				 PCIE_RC_CONFIG_LCSR_OFFSET);
+	status |= (PCIE_RC_CONFIG_LCSR_LBMIE | PCIE_RC_CONFIG_LCSR_LABIE);
+	pcie_write(port, status, PCIE_RC_CONFIG_BASE +
+				 PCIE_RC_CONFIG_LCSR_OFFSET);
+}
+
+static void rockchip_pcie_clr_bw_int(struct rockchip_pcie_port *port)
+{
+	u32 status;
+
+	status = pcie_read(port, PCIE_RC_CONFIG_BASE +
+				 PCIE_RC_CONFIG_LCSR_OFFSET);
+	status |= (PCIE_RC_CONFIG_LCSR_LBMS | PCIE_RC_CONFIG_LCSR_LAMS);
+	pcie_write(port, status, PCIE_RC_CONFIG_BASE +
+				 PCIE_RC_CONFIG_LCSR_OFFSET);
+}
+
+static int rockchip_pcie_valid_config(struct rockchip_pcie_port *pp,
+				      struct pci_bus *bus, int dev)
+{
+	/* access only one slot on each root port */
+	if (bus->number == pp->root_bus_nr && dev > 0)
+		return 0;
+
+	/*
+	 * do not read more than one device on the bus directly attached
+	 * to RC's downstream side.
+	 */
+	if (bus->primary == pp->root_bus_nr && dev > 0)
+		return 0;
+
+	return 1;
+}
+
+static int rockchip_pcie_rd_own_conf(struct rockchip_pcie_port *pp,
+				     int where, int size,
+				     u32 *val)
+{
+	void __iomem *addr = pp->apb_base + PCIE_RC_CONFIG_BASE + where;
+
+	if (!IS_ALIGNED((uintptr_t)addr, size)) {
+		*val = 0;
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	}
+
+	if (size == 4) {
+		*val = readl(addr);
+	} else if (size == 2) {
+		*val = readw(addr);
+	} else if (size == 1) {
+		*val = readb(addr);
+	} else {
+		*val = 0;
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	}
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int rockchip_pcie_wr_own_conf(struct rockchip_pcie_port *pp,
+				     int where, int size, u32 val)
+{
+	u32 mask, tmp, offset;
+
+	offset = (where & (~0x3));
+
+	if (size == 4) {
+		writel(val, pp->apb_base + PCIE_RC_CONFIG_BASE + offset);
+		return PCIBIOS_SUCCESSFUL;
+	}
+
+	mask = ~(((1 << (size * 8)) - 1) << ((where & 0x3) * 8));
+
+	tmp = readl(pp->apb_base + PCIE_RC_CONFIG_BASE + offset) & mask;
+	tmp |= val << ((where & 0x3) * 8);
+	writel(tmp, pp->apb_base + PCIE_RC_CONFIG_BASE + offset);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int rockchip_pcie_rd_other_conf(struct rockchip_pcie_port *pp,
+				       struct pci_bus *bus, u32 devfn,
+				       int where, int size, u32 *val)
+{
+	u32 busdev;
+
+	busdev = PCIE_ECAM_ADDR(bus->number, PCI_SLOT(devfn),
+				PCI_FUNC(devfn), where);
+
+	if (!IS_ALIGNED(busdev, size)) {
+		*val = 0;
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	}
+
+	if (size == 4) {
+		*val = readl(pp->reg_base + busdev);
+	} else if (size == 2) {
+		*val = readw(pp->reg_base + busdev);
+	} else if (size == 1) {
+		*val = readb(pp->reg_base + busdev);
+	} else {
+		*val = 0;
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	}
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int rockchip_pcie_wr_other_conf(struct rockchip_pcie_port *pp,
+				       struct pci_bus *bus, u32 devfn,
+				       int where, int size, u32 val)
+{
+	u32 busdev;
+
+	busdev = PCIE_ECAM_ADDR(bus->number, PCI_SLOT(devfn),
+				PCI_FUNC(devfn), where);
+	if (!IS_ALIGNED(busdev, size))
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+
+	if (size == 4)
+		writel(val, pp->reg_base + busdev);
+	else if (size == 2)
+		writew(val, pp->reg_base + busdev);
+	else if (size == 1)
+		writeb(val, pp->reg_base + busdev);
+	else
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int rockchip_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
+				 int size, u32 *val)
+{
+	struct rockchip_pcie_port *pp = bus->sysdata;
+
+	if (rockchip_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) {
+		*val = 0xffffffff;
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	}
+
+	if (bus->number != pp->root_bus_nr)
+		return rockchip_pcie_rd_other_conf(pp, bus, devfn,
+						  where, size, val);
+
+	return rockchip_pcie_rd_own_conf(pp, where, size, val);
+}
+
+static int rockchip_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
+				 int where, int size, u32 val)
+{
+	struct rockchip_pcie_port *pp = bus->sysdata;
+	int ret;
+
+	if (rockchip_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	if (bus->number != pp->root_bus_nr)
+		ret = rockchip_pcie_wr_other_conf(pp, bus, devfn,
+						  where, size, val);
+	else
+		ret = rockchip_pcie_wr_own_conf(pp, where, size, val);
+
+	return ret;
+}
+
+static struct pci_ops rockchip_pcie_ops = {
+	.read = rockchip_pcie_rd_conf,
+	.write = rockchip_pcie_wr_conf,
+};
+
+/**
+ * rockchip_pcie_init_port - Initialize hardware
+ * @port: PCIe port information
+ */
+static int rockchip_pcie_init_port(struct rockchip_pcie_port *port)
+{
+	int err;
+	u32 status;
+	unsigned long timeout;
+
+	gpiod_set_value(port->ep_gpio, 0);
+
+	err = phy_init(port->phy);
+	if (err < 0) {
+		dev_err(port->dev, "fail to init phy, err %d\n", err);
+		return err;
+	}
+
+	err = reset_control_assert(port->core_rst);
+	if (err) {
+		dev_err(port->dev, "assert core_rst err %d\n", err);
+		return err;
+	}
+
+	err = reset_control_assert(port->mgmt_rst);
+	if (err) {
+		dev_err(port->dev, "assert mgmt_rst err %d\n", err);
+		return err;
+	}
+
+	err = reset_control_assert(port->mgmt_sticky_rst);
+	if (err) {
+		dev_err(port->dev, "assert mgmt_sticky_rst err %d\n", err);
+		return err;
+	}
+
+	err = reset_control_assert(port->pipe_rst);
+	if (err) {
+		dev_err(port->dev, "assert pipe_rst err %d\n", err);
+		return err;
+	}
+
+	pcie_write(port,
+		   HIWORD_UPDATE(PCIE_CLIENT_CONF_ENABLE,
+				 PCIE_CLIENT_CONF_ENABLE_MASK,
+				 PCIE_CLIENT_CONF_ENABLE_SHIFT) |
+		   HIWORD_UPDATE(PCIE_CLIENT_CONF_LANE_NUM(port->lanes),
+				 PCIE_CLIENT_CONF_LANE_NUM_MASK,
+				 PCIE_CLIENT_CONF_LANE_NUM_SHIFT) |
+		   HIWORD_UPDATE(PCIE_CLIENT_MODE_RC,
+				 PCIE_CLIENT_MODE_MASK,
+				 PCIE_CLIENT_MODE_SHIFT) |
+		   HIWORD_UPDATE(PCIE_CLIENT_ARI_ENABLE,
+				 PCIE_CLIENT_ARI_ENABLE_MASK,
+				 PCIE_CLIENT_ARI_ENABLE_SHIFT) |
+		   HIWORD_UPDATE(PCIE_CLIENT_GEN_SEL_2,
+				 PCIE_CLIENT_GEN_SEL_MASK,
+				 PCIE_CLIENT_GEN_SEL_SHIFT),
+		   PCIE_CLIENT_BASE);
+
+	err = phy_power_on(port->phy);
+	if (err) {
+		dev_err(port->dev, "fail to power on phy, err %d\n", err);
+		return err;
+	}
+
+	err = reset_control_deassert(port->core_rst);
+	if (err) {
+		dev_err(port->dev, "deassert core_rst err %d\n", err);
+		return err;
+	}
+
+	err = reset_control_deassert(port->mgmt_rst);
+	if (err) {
+		dev_err(port->dev, "deassert mgmt_rst err %d\n", err);
+		return err;
+	}
+
+	err = reset_control_deassert(port->mgmt_sticky_rst);
+	if (err) {
+		dev_err(port->dev, "deassert mgmt_sticky_rst err %d\n", err);
+		return err;
+	}
+
+	err = reset_control_deassert(port->pipe_rst);
+	if (err) {
+		dev_err(port->dev, "deassert pipe_rst err %d\n", err);
+		return err;
+	}
+
+	/* Enable Gen1 training */
+	pcie_write(port,
+		   HIWORD_UPDATE(PCIE_CLIENT_LINK_TRAIN_ENABLE,
+				 PCIE_CLIENT_LINK_TRAIN_MASK,
+				 PCIE_CLIENT_LINK_TRAIN_SHIFT),
+		   PCIE_CLIENT_BASE);
+
+	gpiod_set_value(port->ep_gpio, 1);
+
+	/* 500ms timeout value should be enough for Gen1/2 training */
+	timeout = jiffies + msecs_to_jiffies(500);
+
+	for (;;) {
+		status = pcie_read(port, PCIE_CLIENT_BASIC_STATUS1);
+		if (((status >> PCIE_CLIENT_LINK_STATUS_SHIFT) &
+		      PCIE_CLIENT_LINK_STATUS_MASK) ==
+		      PCIE_CLIENT_LINK_STATUS_UP) {
+			dev_dbg(port->dev, "PCIe link training gen1 pass!\n");
+			break;
+		}
+
+		msleep(20);
+
+		if (!time_before(jiffies, timeout)) {
+			err = -ETIMEDOUT;
+			break;
+		}
+
+	}
+
+	/* Double check gen1 training */
+	if (err) {
+		status = pcie_read(port, PCIE_CLIENT_BASIC_STATUS1);
+		err = (((status >> PCIE_CLIENT_LINK_STATUS_SHIFT) &
+			PCIE_CLIENT_LINK_STATUS_MASK) ==
+			PCIE_CLIENT_LINK_STATUS_UP) ? 0 : -ETIMEDOUT;
+		if (err) {
+			dev_err(port->dev, "PCIe link training gen1 timeout!\n");
+			return err;
+		}
+	}
+
+	/*
+	 * Enable retrain for gen2. This should be configured only after
+	 * gen1 finished.
+	 */
+	status = pcie_read(port,
+			   PCIE_RC_CONFIG_LCS + PCIE_RC_CONFIG_BASE);
+	status |= PCIE_CORE_LCSR_RETRAIN_LINK;
+	pcie_write(port, status,
+		   PCIE_RC_CONFIG_LCS + PCIE_RC_CONFIG_BASE);
+
+	timeout = jiffies + msecs_to_jiffies(500);
+	for (;;) {
+		status = pcie_read(port, PCIE_CORE_CTRL_MGMT_BASE);
+		if (((status >> PCIE_CORE_PL_CONF_SPEED_SHIFT) &
+		     PCIE_CORE_PL_CONF_SPEED_MASK) ==
+		     PCIE_CORE_PL_CONF_SPEED_5G) {
+			dev_dbg(port->dev, "PCIe link training gen2 pass!\n");
+			break;
+		}
+
+		msleep(20);
+
+		if (!time_before(jiffies, timeout)) {
+			err = -ETIMEDOUT;
+			break;
+		}
+	}
+
+	/* Double check gen2 training */
+	if (err) {
+		status = pcie_read(port, PCIE_CORE_CTRL_MGMT_BASE);
+		err = (((status >> PCIE_CORE_PL_CONF_SPEED_SHIFT) &
+			PCIE_CORE_PL_CONF_SPEED_MASK) ==
+			PCIE_CORE_PL_CONF_SPEED_5G) ? 0 : -ETIMEDOUT;
+		if (err)
+			dev_dbg(port->dev, "PCIe link training gen2 timeout, fall back to gen1!\n");
+	}
+
+	/* Check the final link width from negotiated lane counter from MGMT */
+	status = pcie_read(port, PCIE_CORE_CTRL_MGMT_BASE);
+	status =  0x1 << ((status >> PCIE_CORE_PL_CONF_LANE_SHIFT) &
+			   PCIE_CORE_PL_CONF_LANE_MASK);
+	dev_dbg(port->dev, "current link width is x%d\n", status);
+
+	pcie_write(port, ROCKCHIP_VENDOR_ID, PCIE_RC_CONFIG_BASE);
+	pcie_write(port, PCI_CLASS_BRIDGE_PCI << PCIE_CORE_RC_CONF_SCC_SHIFT,
+		   PCIE_RC_CONFIG_BASE + PCIE_RC_CONFIG_RID_CCR);
+	pcie_write(port, 0x0, PCIE_CORE_CTRL_MGMT_BASE + PCIE_RC_BAR_CONF);
+
+	pcie_write(port, (RC_REGION_0_ADDR_TRANS_L + RC_REGION_0_PASS_BITS),
+		   PCIE_CORE_AXI_CONF_BASE);
+	pcie_write(port, RC_REGION_0_ADDR_TRANS_H,
+		   PCIE_CORE_AXI_CONF_BASE + PCIE_CORE_OB_REGION_ADDR1);
+	pcie_write(port, 0x0080000a,
+		   PCIE_CORE_AXI_CONF_BASE + PCIE_CORE_OB_REGION_DESC0);
+	pcie_write(port, 0x0,
+		   PCIE_CORE_AXI_CONF_BASE + PCIE_CORE_OB_REGION_DESC1);
+
+	return 0;
+}
+
+static irqreturn_t rockchip_pcie_subsys_irq_handler(int irq, void *arg)
+{
+	struct rockchip_pcie_port *pp = arg;
+	u32 reg;
+	u32 sub_reg;
+
+	reg = pcie_read(pp, PCIE_CLIENT_INT_STATUS);
+	if (reg & PCIE_CLIENT_INT_LOCAL) {
+		dev_dbg(pp->dev, "local interrupt received\n");
+		sub_reg = pcie_read(pp, PCIE_CORE_INT_STATUS);
+		if (sub_reg & PCIE_CORE_INT_PRFPE)
+			dev_dbg(pp->dev, "parity error detected while reading from the PNP receive FIFO RAM\n");
+
+		if (sub_reg & PCIE_CORE_INT_CRFPE)
+			dev_dbg(pp->dev, "parity error detected while reading from the Completion Receive FIFO RAM\n");
+
+		if (sub_reg & PCIE_CORE_INT_RRPE)
+			dev_dbg(pp->dev, "parity error detected while reading from replay buffer RAM\n");
+
+		if (sub_reg & PCIE_CORE_INT_PRFO)
+			dev_dbg(pp->dev, "overflow occurred in the PNP receive FIFO\n");
+
+		if (sub_reg & PCIE_CORE_INT_CRFO)
+			dev_dbg(pp->dev, "overflow occurred in the completion receive FIFO\n");
+
+		if (sub_reg & PCIE_CORE_INT_RT)
+			dev_dbg(pp->dev, "replay timer timed out\n");
+
+		if (sub_reg & PCIE_CORE_INT_RTR)
+			dev_dbg(pp->dev, "replay timer rolled over after 4 transmissions of the same TLP\n");
+
+		if (sub_reg & PCIE_CORE_INT_PE)
+			dev_dbg(pp->dev, "phy error detected on receive side\n");
+
+		if (sub_reg & PCIE_CORE_INT_MTR)
+			dev_dbg(pp->dev, "malformed TLP received from the link\n");
+
+		if (sub_reg & PCIE_CORE_INT_UCR)
+			dev_dbg(pp->dev, "malformed TLP received from the link\n");
+
+		if (sub_reg & PCIE_CORE_INT_FCE)
+			dev_dbg(pp->dev, "an error was observed in the flow control advertisements from the other side\n");
+
+		if (sub_reg & PCIE_CORE_INT_CT)
+			dev_dbg(pp->dev, "a request timed out waiting for completion\n");
+
+		if (sub_reg & PCIE_CORE_INT_UTC)
+			dev_dbg(pp->dev, "unmapped TC error\n");
+
+		if (sub_reg & PCIE_CORE_INT_MMVC)
+			dev_dbg(pp->dev, "MSI mask register changes\n");
+
+		pcie_write(pp, sub_reg, PCIE_CORE_INT_STATUS);
+	} else if (reg & PCIE_CLIENT_INT_PHY) {
+		dev_dbg(pp->dev, "phy link changes\n");
+		rockchip_pcie_clr_bw_int(pp);
+	}
+
+	pcie_write(pp, reg & PCIE_CLIENT_INT_LOCAL, PCIE_CLIENT_INT_STATUS);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t rockchip_pcie_client_irq_handler(int irq, void *arg)
+{
+	struct rockchip_pcie_port *pp = arg;
+	u32 reg;
+
+	reg = pcie_read(pp, PCIE_CLIENT_INT_STATUS);
+	if (reg & PCIE_CLIENT_INT_LEGACY_DONE)
+		dev_dbg(pp->dev, "legacy done interrupt received\n");
+
+	if (reg & PCIE_CLIENT_INT_MSG)
+		dev_dbg(pp->dev, "message done interrupt received\n");
+
+	if (reg & PCIE_CLIENT_INT_HOT_RST)
+		dev_dbg(pp->dev, "hot reset interrupt received\n");
+
+	if (reg & PCIE_CLIENT_INT_DPA)
+		dev_dbg(pp->dev, "dpa interrupt received\n");
+
+	if (reg & PCIE_CLIENT_INT_FATAL_ERR)
+		dev_dbg(pp->dev, "fatal error interrupt received\n");
+
+	if (reg & PCIE_CLIENT_INT_NFATAL_ERR)
+		dev_dbg(pp->dev, "no fatal error interrupt received\n");
+
+	if (reg & PCIE_CLIENT_INT_CORR_ERR)
+		dev_dbg(pp->dev, "correctable error interrupt received\n");
+
+	if (reg & PCIE_CLIENT_INT_PHY)
+		dev_dbg(pp->dev, "phy interrupt received\n");
+
+	pcie_write(pp, reg & (PCIE_CLIENT_INT_LEGACY_DONE |
+			      PCIE_CLIENT_INT_MSG | PCIE_CLIENT_INT_HOT_RST |
+			      PCIE_CLIENT_INT_DPA | PCIE_CLIENT_INT_FATAL_ERR |
+			      PCIE_CLIENT_INT_NFATAL_ERR |
+			      PCIE_CLIENT_INT_CORR_ERR),
+			      PCIE_CLIENT_INT_STATUS |
+			      PCIE_CLIENT_INT_PHY);
+	return IRQ_HANDLED;
+}
+
+static void rockchip_pcie_legacy_int_handler(struct irq_desc *desc)
+{
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	struct rockchip_pcie_port *port;
+	u32 reg;
+	u32 hwirq;
+	u32 virq;
+
+	chained_irq_enter(chip, desc);
+	port = irq_desc_get_handler_data(desc);
+
+	reg = pcie_read(port, PCIE_CLIENT_INT_STATUS);
+	reg = (reg & ROCKCHIP_PCIE_RPIFR1_INTR_MASK) >>
+	       ROCKCHIP_PCIE_RPIFR1_INTR_SHIFT;
+
+	while (reg) {
+		hwirq = ffs(reg) - 1;
+		reg &= ~BIT(hwirq);
+
+		virq = irq_find_mapping(port->irq_domain, hwirq);
+		if (virq)
+			generic_handle_irq(virq);
+		else
+			dev_err(port->dev, "unexpected IRQ, INT%d\n", hwirq);
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+
+/**
+ * rockchip_pcie_parse_dt - Parse Device tree
+ * @port: PCIe port information
+ *
+ * Return: '0' on success and error value on failure
+ */
+static int rockchip_pcie_parse_dt(struct rockchip_pcie_port *port)
+{
+	struct device *dev = port->dev;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct device_node *node = dev->of_node;
+	struct resource *regs;
+	int irq;
+	int err;
+
+	regs = platform_get_resource_byname(pdev,
+					    IORESOURCE_MEM,
+					    "axi-base");
+	if (!regs) {
+		dev_err(dev, "missing axi-base property\n");
+		return -ENODEV;
+	}
+
+	port->reg_base = devm_ioremap_resource(dev, regs);
+	if (IS_ERR(port->reg_base))
+		return PTR_ERR(port->reg_base);
+
+	regs = platform_get_resource_byname(pdev,
+					    IORESOURCE_MEM,
+					    "apb-base");
+	if (!regs) {
+		dev_err(dev, "missing apb-base property\n");
+		return -ENODEV;
+	}
+
+	port->apb_base = devm_ioremap_resource(dev, regs);
+	if (IS_ERR(port->apb_base))
+		return PTR_ERR(port->apb_base);
+
+	port->phy = devm_phy_get(dev, "pcie-phy");
+	if (IS_ERR(port->phy)) {
+		if (PTR_ERR(port->phy) != -EPROBE_DEFER)
+			dev_err(dev, "Missing phy\n");
+		return PTR_ERR(port->phy);
+	}
+
+	port->lanes = 1;
+	err = of_property_read_u32(node, "num-lanes", &port->lanes);
+	if (!err && ((port->lanes == 0) ||
+		     (port->lanes == 3) ||
+		     (port->lanes > 4))) {
+		dev_warn(dev, "invalid num-lanes, default use one lane\n");
+		port->lanes = 1;
+	}
+
+	port->core_rst = devm_reset_control_get(dev, "core");
+	if (IS_ERR(port->core_rst)) {
+		if (PTR_ERR(port->core_rst) != -EPROBE_DEFER)
+			dev_err(dev, "missing core rst property in node\n");
+		return PTR_ERR(port->core_rst);
+	}
+
+	port->mgmt_rst = devm_reset_control_get(dev, "mgmt");
+	if (IS_ERR(port->mgmt_rst)) {
+		if (PTR_ERR(port->mgmt_rst) != -EPROBE_DEFER)
+			dev_err(dev, "missing mgmt rst property in node\n");
+		return PTR_ERR(port->mgmt_rst);
+	}
+
+	port->mgmt_sticky_rst = devm_reset_control_get(dev, "mgmt-sticky");
+	if (IS_ERR(port->mgmt_sticky_rst)) {
+		if (PTR_ERR(port->mgmt_sticky_rst) != -EPROBE_DEFER)
+			dev_err(dev, "missing mgmt-sticky rst property in node\n");
+		return PTR_ERR(port->mgmt_sticky_rst);
+	}
+
+	port->pipe_rst = devm_reset_control_get(dev, "pipe");
+	if (IS_ERR(port->pipe_rst)) {
+		if (PTR_ERR(port->pipe_rst) != -EPROBE_DEFER)
+			dev_err(dev, "missing pipe rst property in node\n");
+		return PTR_ERR(port->pipe_rst);
+	}
+
+	port->ep_gpio = devm_gpiod_get(dev, "ep", GPIOD_OUT_HIGH);
+	if (IS_ERR(port->ep_gpio)) {
+		dev_err(dev, "missing ep-gpios property in node\n");
+		return PTR_ERR(port->ep_gpio);
+	}
+
+	port->aclk_pcie = devm_clk_get(dev, "aclk");
+	if (IS_ERR(port->aclk_pcie)) {
+		dev_err(dev, "aclk clock not found\n");
+		return PTR_ERR(port->aclk_pcie);
+	}
+
+	port->aclk_perf_pcie = devm_clk_get(dev, "aclk-perf");
+	if (IS_ERR(port->aclk_perf_pcie)) {
+		dev_err(dev, "aclk_perf clock not found\n");
+		return PTR_ERR(port->aclk_perf_pcie);
+	}
+
+	port->hclk_pcie = devm_clk_get(dev, "hclk");
+	if (IS_ERR(port->hclk_pcie)) {
+		dev_err(dev, "hclk clock not found\n");
+		return PTR_ERR(port->hclk_pcie);
+	}
+
+	port->clk_pcie_pm = devm_clk_get(dev, "pm");
+	if (IS_ERR(port->clk_pcie_pm)) {
+		dev_err(dev, "pm clock not found\n");
+		return PTR_ERR(port->clk_pcie_pm);
+	}
+
+	irq = platform_get_irq_byname(pdev, "sys");
+	if (irq < 0) {
+		dev_err(dev, "missing sys IRQ resource\n");
+		return -EINVAL;
+	}
+
+	err = devm_request_irq(dev, irq, rockchip_pcie_subsys_irq_handler,
+			       IRQF_SHARED, "pcie-sys", port);
+	if (err) {
+		dev_err(dev, "failed to request pcie subsystem IRQ\n");
+		return err;
+	}
+
+	irq = platform_get_irq_byname(pdev, "legacy");
+	if (irq < 0) {
+		dev_err(dev, "missing legacy IRQ resource\n");
+		return -EINVAL;
+	}
+
+	irq_set_chained_handler_and_data(irq,
+					 rockchip_pcie_legacy_int_handler,
+					 port);
+
+	irq = platform_get_irq_byname(pdev, "client");
+	if (irq < 0) {
+		dev_err(dev, "missing client IRQ resource\n");
+		return -EINVAL;
+	}
+
+	err = devm_request_irq(dev, irq, rockchip_pcie_client_irq_handler,
+			       IRQF_SHARED, "pcie-client", port);
+	if (err) {
+		dev_err(dev, "failed to request pcie client IRQ\n");
+		return err;
+	}
+
+	port->vpcie3v3 = devm_regulator_get_optional(dev, "vpcie3v3");
+	if (IS_ERR(port->vpcie3v3)) {
+		if (PTR_ERR(port->vpcie3v3) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+		dev_info(dev, "no vpcie3v3 regulator found\n");
+	}
+
+	port->vpcie1v8 = devm_regulator_get_optional(dev, "vpcie1v8");
+	if (IS_ERR(port->vpcie1v8)) {
+		if (PTR_ERR(port->vpcie1v8) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+		dev_info(dev, "no vpcie1v8 regulator found\n");
+	}
+
+	port->vpcie0v9 = devm_regulator_get_optional(dev, "vpcie0v9");
+	if (IS_ERR(port->vpcie0v9)) {
+		if (PTR_ERR(port->vpcie0v9) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+		dev_info(dev, "no vpcie0v9 regulator found\n");
+	}
+
+	return 0;
+}
+
+static int rockchip_pcie_set_vpcie(struct rockchip_pcie_port *port)
+{
+	int err;
+
+	if (!IS_ERR(port->vpcie3v3)) {
+		err = regulator_enable(port->vpcie3v3);
+		if (err) {
+			dev_err(port->dev, "Fail to enable vpcie3v3 regulator.\n");
+			goto err_out;
+		}
+	}
+
+	if (!IS_ERR(port->vpcie1v8)) {
+		err = regulator_enable(port->vpcie1v8);
+		if (err) {
+			dev_err(port->dev, "Fail to enable vpcie1v8 regulator.\n");
+			goto err_disable_3v3;
+		}
+	}
+
+	if (!IS_ERR(port->vpcie0v9)) {
+		err = regulator_enable(port->vpcie0v9);
+		if (err) {
+			dev_err(port->dev, "Fail to enable vpcie0v9 regulator.\n");
+			goto err_disable_1v8;
+		}
+	}
+
+	return 0;
+
+err_disable_1v8:
+	if (!IS_ERR(port->vpcie1v8))
+		regulator_disable(port->vpcie1v8);
+err_disable_3v3:
+	if (!IS_ERR(port->vpcie3v3))
+		regulator_disable(port->vpcie3v3);
+err_out:
+	return err;
+}
+
+static void rockchip_pcie_enable_interrupts(struct rockchip_pcie_port *port)
+{
+	pcie_write(port, (PCIE_CLIENT_INT_CLI << 16) &
+		   (~PCIE_CLIENT_INT_CLI), PCIE_CLIENT_INT_MASK);
+	pcie_write(port, (u32)(~PCIE_CORE_INT), PCIE_CORE_INT_MASK);
+
+	rockchip_pcie_enable_bw_int(port);
+}
+
+static int rockchip_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
+				  irq_hw_number_t hwirq)
+{
+	irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq);
+	irq_set_chip_data(irq, domain->host_data);
+
+	return 0;
+}
+
+static const struct irq_domain_ops intx_domain_ops = {
+	.map = rockchip_pcie_intx_map,
+};
+
+static int rockchip_pcie_init_irq_domain(struct rockchip_pcie_port *pp)
+{
+	struct device *dev = pp->dev;
+	struct device_node *intc = of_get_next_child(dev->of_node, NULL);
+
+	if (!intc) {
+		dev_err(dev, "Missing child interrupt-controller node\n");
+		return -EINVAL;
+	}
+
+	pp->irq_domain = irq_domain_add_linear(intc, 4, &intx_domain_ops, pp);
+	if (!pp->irq_domain) {
+		dev_err(dev, "Failed to get a INTx IRQ domain\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int rockchip_pcie_prog_ob_atu(struct rockchip_pcie_port *pp,
+				     int region_no,
+				     int type, u8 num_pass_bits,
+				     u32 lower_addr, u32 upper_addr)
+{
+	u32 ob_addr_0;
+	u32 ob_addr_1;
+	u32 ob_desc_0;
+	void __iomem *aw_base;
+
+	if (region_no >= MAX_AXI_WRAPPER_REGION_NUM)
+		return -EINVAL;
+	if ((num_pass_bits + 1) < 8)
+		return -EINVAL;
+	if (num_pass_bits > 63)
+		return -EINVAL;
+	if (region_no == 0) {
+		if (AXI_REGION_0_SIZE < (2ULL << num_pass_bits))
+		return -EINVAL;
+	}
+	if (region_no != 0) {
+		if (AXI_REGION_SIZE < (2ULL << num_pass_bits))
+		return -EINVAL;
+	}
+	aw_base = pp->apb_base + PCIE_CORE_AXI_CONF_BASE;
+	aw_base += (region_no << OB_REG_SIZE_SHIFT);
+
+	ob_addr_0 = num_pass_bits & PCIE_CORE_OB_REGION_ADDR0_NUM_BITS;
+	ob_addr_0 |= lower_addr & PCIE_CORE_OB_REGION_ADDR0_LO_ADDR;
+	ob_addr_1 = upper_addr;
+	ob_desc_0 = (1 << 23 | type);
+
+	writel(ob_addr_0, aw_base);
+	writel(ob_addr_1, aw_base + PCIE_CORE_OB_REGION_ADDR1);
+	writel(ob_desc_0, aw_base + PCIE_CORE_OB_REGION_DESC0);
+	writel(0, aw_base + PCIE_CORE_OB_REGION_DESC1);
+
+	return 0;
+}
+
+static int rockchip_pcie_prog_ib_atu(struct rockchip_pcie_port *pp,
+				     int region_no,
+				     u8 num_pass_bits,
+				     u32 lower_addr,
+				     u32 upper_addr)
+{
+	u32 ib_addr_0;
+	u32 ib_addr_1;
+	void __iomem *aw_base;
+
+	if (!pp)
+		return -EINVAL;
+	if (region_no > MAX_AXI_IB_ROOTPORT_REGION_NUM)
+		return -EINVAL;
+	if ((num_pass_bits + 1) < MIN_AXI_ADDR_BITS_PASSED)
+		return -EINVAL;
+	if (num_pass_bits > 63)
+		return -EINVAL;
+	aw_base = pp->apb_base + PCIE_CORE_AXI_INBOUND_BASE;
+	aw_base += (region_no << IB_ROOT_PORT_REG_SIZE_SHIFT);
+
+	ib_addr_0 = num_pass_bits & PCIE_CORE_IB_REGION_ADDR0_NUM_BITS;
+	ib_addr_0 |= (lower_addr << 8) & PCIE_CORE_IB_REGION_ADDR0_LO_ADDR;
+	ib_addr_1 = upper_addr;
+
+	writel(ib_addr_0, aw_base);
+	writel(ib_addr_1, aw_base + PCIE_RP_IB_ADDR_TRANS);
+
+	return 0;
+}
+
+static int rockchip_pcie_probe(struct platform_device *pdev)
+{
+	struct rockchip_pcie_port *port;
+	struct device *dev = &pdev->dev;
+	struct pci_bus *bus, *child;
+	struct resource_entry *win;
+	resource_size_t io_base;
+	struct resource	*busn;
+	struct resource	*mem;
+	struct resource	*io;
+	phys_addr_t io_bus_addr;
+	u32 io_size;
+	phys_addr_t mem_bus_addr;
+	u32 mem_size;
+	int reg_no;
+	int err;
+	int offset;
+
+	LIST_HEAD(res);
+
+	if (!dev->of_node)
+		return -ENODEV;
+
+	port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+	if (!port)
+		return -ENOMEM;
+
+	port->dev = dev;
+
+	err = rockchip_pcie_parse_dt(port);
+	if (err)
+		return err;
+
+	err = clk_prepare_enable(port->aclk_pcie);
+	if (err) {
+		dev_err(dev, "Unable to enable aclk_pcie clock.\n");
+		goto err_aclk_pcie;
+	}
+
+	err = clk_prepare_enable(port->aclk_perf_pcie);
+	if (err) {
+		dev_err(dev, "Unable to enable aclk_perf_pcie clock.\n");
+		goto err_aclk_perf_pcie;
+	}
+
+	err = clk_prepare_enable(port->hclk_pcie);
+	if (err) {
+		dev_err(dev, "Unable to enable hclk_pcie clock.\n");
+		goto err_hclk_pcie;
+	}
+
+	err = clk_prepare_enable(port->clk_pcie_pm);
+	if (err) {
+		dev_err(dev, "Unable to enable hclk_pcie clock.\n");
+		goto err_pcie_pm;
+	}
+
+	err = rockchip_pcie_set_vpcie(port);
+	if (err) {
+		dev_err(port->dev, "Fail to set vpcie regulator.\n");
+		goto err_set_vpcie;
+	}
+
+	err = rockchip_pcie_init_port(port);
+	if (err)
+		goto err_vpcie;
+
+	platform_set_drvdata(pdev, port);
+
+	rockchip_pcie_enable_interrupts(port);
+
+	err = rockchip_pcie_init_irq_domain(port);
+	if (err < 0)
+		goto err_vpcie;
+
+	err = of_pci_get_host_bridge_resources(dev->of_node, 0, 0xff,
+					       &res, &io_base);
+	if (err)
+		goto err_vpcie;
+
+	err = devm_request_pci_bus_resources(dev, &res);
+	if (err)
+		goto err_vpcie;
+
+	/* Get the I/O and memory ranges from DT */
+	resource_list_for_each_entry(win, &res) {
+		switch (resource_type(win->res)) {
+		case IORESOURCE_IO:
+			io = win->res;
+			io->name = "I/O";
+			io_size = resource_size(io);
+			io_bus_addr = io->start - win->offset;
+			err = pci_remap_iospace(io, io_base);
+			if (err) {
+				dev_warn(port->dev, "error %d: failed to map resource %pR\n",
+					 err, io);
+				continue;
+			}
+			break;
+		case IORESOURCE_MEM:
+			mem = win->res;
+			mem->name = "MEM";
+			mem_size = resource_size(mem);
+			mem_bus_addr = mem->start - win->offset;
+			break;
+		case IORESOURCE_BUS:
+			busn = win->res;
+			break;
+		default:
+			continue;
+		}
+	}
+
+	if (mem_size)
+		for (reg_no = 0; reg_no < (mem_size >> 20); reg_no++) {
+			err = rockchip_pcie_prog_ob_atu(port, reg_no + 1,
+							AXI_WRAPPER_MEM_WRITE,
+							20 - 1,
+							mem_bus_addr +
+							(reg_no << 20),
+							0);
+			if (err) {
+				dev_err(dev, "Program RC mem outbound ATU failed\n");
+				goto err_vpcie;
+			}
+		}
+
+	err = rockchip_pcie_prog_ib_atu(port, 2, 32 - 1, 0x0, 0);
+	if (err) {
+		dev_err(dev, "Program RC mem inbound ATU failed\n");
+		goto err_vpcie;
+	}
+
+	offset = mem_size >> 20;
+
+	if (io_size)
+		for (reg_no = 0; reg_no < (io_size >> 20); reg_no++) {
+			err = rockchip_pcie_prog_ob_atu(port,
+							reg_no + 1 + offset,
+							AXI_WRAPPER_IO_WRITE,
+							20 - 1,
+							io_bus_addr +
+							(reg_no << 20),
+							0);
+			if (err) {
+				dev_err(dev, "Program RC io outbound ATU failed\n");
+				goto err_vpcie;
+			}
+		}
+
+	if (busn)
+		port->root_bus_nr = busn->start;
+
+	bus = pci_scan_root_bus(&pdev->dev, 0,
+				&rockchip_pcie_ops, port, &res);
+
+	if (!bus) {
+		err = -ENOMEM;
+		goto err_vpcie;
+	}
+
+	pci_bus_size_bridges(bus);
+	pci_bus_assign_resources(bus);
+	list_for_each_entry(child, &bus->children, node)
+		pcie_bus_configure_settings(child);
+
+	pci_bus_add_devices(bus);
+
+	dev_warn(dev, "only 32-bit config accesses supported; smaller writes may corrupt adjacent RW1C fields\n");
+
+	return err;
+
+err_vpcie:
+	if (!IS_ERR(port->vpcie3v3))
+		regulator_disable(port->vpcie3v3);
+	if (!IS_ERR(port->vpcie1v8))
+		regulator_disable(port->vpcie1v8);
+	if (!IS_ERR(port->vpcie0v9))
+		regulator_disable(port->vpcie0v9);
+err_set_vpcie:
+	clk_disable_unprepare(port->clk_pcie_pm);
+err_pcie_pm:
+	clk_disable_unprepare(port->hclk_pcie);
+err_hclk_pcie:
+	clk_disable_unprepare(port->aclk_perf_pcie);
+err_aclk_perf_pcie:
+	clk_disable_unprepare(port->aclk_pcie);
+err_aclk_pcie:
+	return err;
+}
+
+static const struct of_device_id rockchip_pcie_of_match[] = {
+	{ .compatible = "rockchip,rk3399-pcie", },
+	{}
+};
+
+static struct platform_driver rockchip_pcie_driver = {
+	.driver = {
+		.name = "rockchip-pcie",
+		.of_match_table = rockchip_pcie_of_match,
+	},
+	.probe = rockchip_pcie_probe,
+
+};
+builtin_platform_driver(rockchip_pcie_driver);
+
-- 
2.3.7




From shawn.lin at rock-chips.com  Thu Jul 28 21:14:59 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Fri, 29 Jul 2016 12:14:59 +0800
Subject: [PATCH 1/5] Documentation: mmc: synopsys-dw-mshc: add power domain
 description
In-Reply-To: <1469765703-30999-1-git-send-email-shawn.lin@rock-chips.com>
References: <1469765703-30999-1-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <1469765703-30999-2-git-send-email-shawn.lin@rock-chips.com>

This patch adds power domain as a optional property for dw_mmc.
We should handle the power domain if available as it's in off
state dut to the policy of power-saving after booting up. Otherwise
dw_mmc can not be accessed.

Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>

---

 Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
index 8636f5a..f48febd 100644
--- a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
+++ b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt
@@ -81,6 +81,8 @@ Optional properties:
 * dma-names: request names for generic DMA client binding. Must be "rx-tx".
   Refer to dma.txt for details.
 
+* power-domains: Must contain a reference to the PM domain, if available.
+
 Aliases:
 
 - All the MSHC controller nodes should be represented in the aliases node using
-- 
2.3.7




From shawn.lin at rock-chips.com  Thu Jul 28 21:14:58 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Fri, 29 Jul 2016 12:14:58 +0800
Subject: [PATCH 0/5] Add power domain support for dw_mmc/sdhci-of-arasan
Message-ID: <1469765703-30999-1-git-send-email-shawn.lin@rock-chips.com>


We need to enable the power domain manually while probing as the
power policy will turn off the pd which is not referenced. Otherwise
we may take risk of breaking kernel once Feng Xiao land the rockchip pd
relate stuff. So, let's do it in advance.

This pachset is based on the latest linux-next[1] which should contain
the next branch of linux-mmc and dt stuff. It's ideal to pack these all
into Ulf' tree with Heiko's ack for dts, Rob's ack for Doc and Jaehoon's
ack for dw_mmc. But it's okay to be merged seperately.

[1] git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git

Thanks.



Shawn Lin (5):
  Documentation: mmc: synopsys-dw-mshc: add power domain description
  Documentation: mmc: sdhci-of-arasan: add power domain description
  arm64: dts: rockchip: Provide power-domains for sd/sdio/emmc
    controllers
  mmc: dw_mmc: add power domain support
  mmc: sdhci-of-arasan: add power domain support

 Documentation/devicetree/bindings/mmc/arasan,sdhci.txt     |  1 +
 Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt |  2 ++
 arch/arm64/boot/dts/rockchip/rk3399.dtsi                   |  3 +++
 drivers/mmc/host/dw_mmc.c                                  | 11 +++++++++++
 drivers/mmc/host/sdhci-of-arasan.c                         | 10 ++++++++++
 5 files changed, 27 insertions(+)

-- 
2.3.7




From shawn.lin at rock-chips.com  Thu Jul 28 21:15:00 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Fri, 29 Jul 2016 12:15:00 +0800
Subject: [PATCH 2/5] Documentation: mmc: sdhci-of-arasan: add power domain
 description
In-Reply-To: <1469765703-30999-1-git-send-email-shawn.lin@rock-chips.com>
References: <1469765703-30999-1-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <1469765703-30999-3-git-send-email-shawn.lin@rock-chips.com>

This patch adds power domain as a optional property for sdhci-of-arasan.
We should handle the power domain if available as it's in off
state due to the policy of power-saving after booting up. Otherwise
controller can not be accessed.

Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
---

 Documentation/devicetree/bindings/mmc/arasan,sdhci.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt b/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt
index 3404afa..b5fb563 100644
--- a/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt
+++ b/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt
@@ -36,6 +36,7 @@ Optional Properties:
   - #clock-cells: If specified this should be the value <0>.  With this property
     in place we will export a clock representing the Card Clock.  This clock
     is expected to be consumed by our PHY.  You must also specify
+  - power-domains: Must contain a reference to the PM domain, if available.
 
 Example:
 	sdhci at e0100000 {
-- 
2.3.7




From shawn.lin at rock-chips.com  Thu Jul 28 21:15:01 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Fri, 29 Jul 2016 12:15:01 +0800
Subject: [PATCH 3/5] arm64: dts: rockchip: Provide power-domains for
 sd/sdio/emmc controllers
In-Reply-To: <1469765703-30999-1-git-send-email-shawn.lin@rock-chips.com>
References: <1469765703-30999-1-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <1469765703-30999-4-git-send-email-shawn.lin@rock-chips.com>

We add these two properties as we need to enable it manually in case
of been turned off by pd code if not referenced. We can't take risk of
it, so let's do it.

Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
---

 arch/arm64/boot/dts/rockchip/rk3399.dtsi | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index 4c84229..5744883 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -210,6 +210,7 @@
 			 <&cru SCLK_SDIO_DRV>, <&cru SCLK_SDIO_SAMPLE>;
 		clock-names = "biu", "ciu", "ciu-drive", "ciu-sample";
 		fifo-depth = <0x100>;
+		power-domains = <&power RK3399_PD_SDIOAUDIO>;
 		status = "disabled";
 	};
 
@@ -223,6 +224,7 @@
 			 <&cru SCLK_SDMMC_DRV>, <&cru SCLK_SDMMC_SAMPLE>;
 		clock-names = "biu", "ciu", "ciu-drive", "ciu-sample";
 		fifo-depth = <0x100>;
+		power-domains = <&power RK3399_PD_SD>;
 		status = "disabled";
 	};
 
@@ -239,6 +241,7 @@
 		#clock-cells = <0>;
 		phys = <&emmc_phy>;
 		phy-names = "phy_arasan";
+		power-domains = <&power RK3399_PD_EMMC>;
 		status = "disabled";
 	};
 
-- 
2.3.7




From shawn.lin at rock-chips.com  Thu Jul 28 21:15:02 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Fri, 29 Jul 2016 12:15:02 +0800
Subject: [PATCH 4/5] mmc: dw_mmc: add power domain support
In-Reply-To: <1469765703-30999-1-git-send-email-shawn.lin@rock-chips.com>
References: <1469765703-30999-1-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <1469765703-30999-5-git-send-email-shawn.lin@rock-chips.com>

We should enable power domain once provided. Otherwise
we take risk of bus err as it's maybe in off state before
probing.

Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
---

 drivers/mmc/host/dw_mmc.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 32380d5..8b39168 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -22,6 +22,8 @@
 #include <linux/ioport.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_opp.h>
 #include <linux/seq_file.h>
 #include <linux/slab.h>
 #include <linux/stat.h>
@@ -3024,6 +3026,9 @@ int dw_mci_probe(struct dw_mci *host)
 		host->bus_hz = clk_get_rate(host->ciu_clk);
 	}
 
+	pm_runtime_enable(host->dev);
+	pm_runtime_get_sync(host->dev);
+
 	if (!host->bus_hz) {
 		dev_err(host->dev,
 			"Platform data must supply bus speed\n");
@@ -3197,6 +3202,9 @@ err_clk_biu:
 	if (!IS_ERR(host->biu_clk))
 		clk_disable_unprepare(host->biu_clk);
 
+	pm_runtime_put(host->dev);
+	pm_runtime_disable(host->dev);
+
 	return ret;
 }
 EXPORT_SYMBOL(dw_mci_probe);
@@ -3226,6 +3234,9 @@ void dw_mci_remove(struct dw_mci *host)
 
 	if (!IS_ERR(host->biu_clk))
 		clk_disable_unprepare(host->biu_clk);
+
+	pm_runtime_put(host->dev);
+	pm_runtime_disable(host->dev);
 }
 EXPORT_SYMBOL(dw_mci_remove);
 
-- 
2.3.7




From shawn.lin at rock-chips.com  Thu Jul 28 21:16:45 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Fri, 29 Jul 2016 12:16:45 +0800
Subject: [PATCH 5/5] mmc: sdhci-of-arasan: add power domain support
In-Reply-To: <1469765703-30999-1-git-send-email-shawn.lin@rock-chips.com>
References: <1469765703-30999-1-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <1469765805-31049-1-git-send-email-shawn.lin@rock-chips.com>

We should enable power domain once provided. Otherwise
we take risk of bus err as it's maybe in off state before
probing.

Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
---

 drivers/mmc/host/sdhci-of-arasan.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c
index e0f193f..e507e94 100644
--- a/drivers/mmc/host/sdhci-of-arasan.c
+++ b/drivers/mmc/host/sdhci-of-arasan.c
@@ -25,6 +25,8 @@
 #include <linux/of_device.h>
 #include <linux/phy/phy.h>
 #include <linux/regmap.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_opp.h>
 #include "sdhci-pltfm.h"
 
 #define SDHCI_ARASAN_CLK_CTRL_OFFSET	0x2c
@@ -515,6 +517,9 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
 		goto clk_dis_ahb;
 	}
 
+	pm_runtime_enable(host->dev);
+	pm_runtime_get_sync(host->dev);
+
 	sdhci_get_of_property(pdev);
 	pltfm_host->clk = clk_xin;
 
@@ -577,6 +582,8 @@ clk_dis_ahb:
 	clk_disable_unprepare(sdhci_arasan->clk_ahb);
 err_pltfm_free:
 	sdhci_pltfm_free(pdev);
+	pm_runtime_put(host->dev);
+	pm_runtime_disable(host->dev);
 	return ret;
 }
 
@@ -599,6 +606,9 @@ static int sdhci_arasan_remove(struct platform_device *pdev)
 
 	clk_disable_unprepare(clk_ahb);
 
+	pm_runtime_put(host->dev);
+	pm_runtime_disable(host->dev);
+
 	return ret;
 }
 
-- 
2.3.7




From zyw at rock-chips.com  Fri Jul 29 00:48:17 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Fri, 29 Jul 2016 15:48:17 +0800
Subject: [v8 PATCH 0/6] Rockchip Type-C and DisplayPort driver
Message-ID: <1469778503-29193-1-git-send-email-zyw@rock-chips.com>


Hi all

This series patch is for rockchip Type-C phy and DisplayPort controller
driver.

The USB Type-C PHY is designed to support the USB3 and DP applications.
The PHY basically has two main components: USB3 and DisplyPort. USB3
operates in SuperSpeed mode and the DP can operate at RBR, HBR and HBR2
data rates. The Type-C cable orientation detection and Power Delivery
(PD) is accomplished using a PD PHY or a exernal PD chip.

The DP controller is compliant with DisplayPort Specification,
Version 1.3, This IP is compatible with the rockchip type-c PHY IP.
There is a uCPU in DP controller, it need a firmware to work, please
put the firmware file[0] to /lib/firmware/rockchip/dptx.bin. The uCPU
in charge of aux communication and link training, the host use mailbox
to communicate with the ucpu.

The DP contoller has register a notification with extcon API, to get the
alt mode from PD, the PD driver need call the devm_extcon_dev_allocate
to create a extcon device and use extcon_set_state to notify DP
controller. And call extcon_set_cable_property to set orientation.

About the DP audio, cdn-dp registered 2 DAIs: 0 is I2S, 1 is SPDIF.
We can reference them in simple-card.

This series is based on Mark Yao's branch[1] and Chanwoo Choi's
extcon-test branch[2]. The extcon patch is base on a test branch, so
please do not review the "1/6 extcon: Add Type-C and DP support".

I test this patches on the rk3399-evb board, with a fusb302 driver,
this branch has no rk3399.dtsi, so the patch about dts is not included
in this series.

[0]
https://patchwork.kernel.org/patch/9249693/
[1]
https://github.com/markyzq/kernel-drm-rockchip/tree/drm-rockchip-next-2016-05-23
[2]
https://git.kernel.org/cgit/linux/kernel/git/chanwoo/extcon.git/log/?h=extcon-test
- extcon: Add the extcon_type to gather each connector into five category
- extcon: Add the support for extcon property according to extcon type
- extcon: Add the support for the capability of each property
- extcon: Rename the extcon_set/get_state() to maintain the function naming
pattern
- extcon: Add the synchronization extcon APIs to support the notification


Changes in v8:
- set the default cable id to EXTCON_USB_HOST
- optimization Error log
- optimization the err log

Changes in v7:
- support new API of extcon
- support firmware standby when no dptx connection
- optimization the calculation of tu size and valid symbol

Changes in v6:
- add assigned-clocks and assigned-clock-rates
- delete the support of PIN_ASSIGN_A/B
- set the default mode to MODE_DFP_USB
- disable DP PLL at USB3 only mode
- add assigned-clocks and assigned-clock-rates
- add power-domains
- add a port struct
- select SND_SOC_HDMI_CODEC
- force reset the phy when hpd detected

Changes in v5:
- support get property from extcon
- remove PIN ASSIGN A/B support
- alphabetical order
- do not use long, use u32 or u64
- return MODE_CLOCK_HIGH when requested > actual
- Optimized Coding Style
- add a formula to get better tu size and symbol value.
- modify according to Sean Paul's comments
- fixed the fw_wait always 0

Changes in v4:
- add a #phy-cells node
- select EXTCON
- use phy framework to control the USB3 and DP function
- rename PIN_MAP_ to PIN_ASSIGN_
- add a reset node
- support 2 phys
- use phy framework to control DP phy
- support 2 phys

Changes in v3:
- use compatible: rockchip,rk3399-typec-phy
- use dashes instead of underscores.
- remove the phy framework(Kishon Vijay Abraham I)
- add parentheses around the macro
- use a single space between type and name
- add spaces after opening and before closing braces.
- use u16 for register value
- remove type-c phy header file
- CodingStyle optimization
- use some cable extcon to get type-c port information
- add a extcon to notify Display Port
- add SoC specific compatible string
- remove reg = <1>;
- use EXTCON_DISP_DP and EXTCON_DISP_DP_ALT cable to get dp port state.
- reset spdif before config it
- modify the firmware clk to 100Mhz
- retry load firmware if fw file is requested too early

Changes in v2:
- add some registers description
- select RESET_CONTROLLER
- alphabetic order
- modify some spelling mistakes
- make mode cleaner
- use bool for enable/disable
- check all of the return value
- return a better err number
- use more readx_poll_timeout()
- clk_disable_unprepare(tcphy->clk_ref);
- remove unuse functions, rockchip_typec_phy_power_on/off
- remove unnecessary typecast from void *
- use dts node to distinguish between phys.
- Alphabetic order
- remove excess error message
- use define clk_rate
- check all return value
- remove dev_set_name(dp->dev, "cdn-dp");
- use schedule_delayed_work
- remove never-called functions
- remove some unnecessary ()

Changes in v1:
- add extcon node description
- move the registers in phy driver
- remove the suffix of reset
- update the licence note
- init core clock to 50MHz
- use extcon API
- remove unused global
- add some comments for magic num
- change usleep_range(1000, 2000) tousleep_range(1000, 1050)
- remove __func__ from dev_err
- return err number when get clk failed
- remove ADDR_ADJ define
- use devm_clk_get(&pdev->dev, "tcpdcore")
- add extcon node description
- add #sound-dai-cells description
- use extcon API
- use hdmi-codec for the DP Asoc
- do not initialize the "ret"
- printk a err log when drm_of_encoder_active_endpoint_id
- modify the dclk pin_pol to a single line

Chris Zhong (6):
  extcon: Add EXTCON_DISP_DP and the property for USB Type-C
  Documentation: bindings: add dt doc for Rockchip USB Type-C PHY
  phy: Add USB Type-C PHY driver for rk3399
  arm64: dts: rockchip: add Type-C phy for RK3399
  Documentation: bindings: add dt documentation for cdn DP controller
  drm/rockchip: cdn-dp: add cdn DP support for rk3399

 .../bindings/display/rockchip/cdn-dp-rockchip.txt  |  74 ++
 .../devicetree/bindings/phy/phy-rockchip-typec.txt |  84 ++
 arch/arm64/boot/dts/rockchip/rk3399.dtsi           |  44 +
 drivers/extcon/extcon.c                            |   5 +
 drivers/gpu/drm/rockchip/Kconfig                   |  10 +
 drivers/gpu/drm/rockchip/Makefile                  |   1 +
 drivers/gpu/drm/rockchip/cdn-dp-core.c             | 867 +++++++++++++++++++
 drivers/gpu/drm/rockchip/cdn-dp-core.h             | 103 +++
 drivers/gpu/drm/rockchip/cdn-dp-reg.c              | 961 +++++++++++++++++++++
 drivers/gpu/drm/rockchip/cdn-dp-reg.h              | 482 +++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c        |  13 +-
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h        |   9 +
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c        |   2 +
 drivers/phy/Kconfig                                |   9 +
 drivers/phy/Makefile                               |   1 +
 drivers/phy/phy-rockchip-typec.c                   | 951 ++++++++++++++++++++
 include/linux/extcon.h                             |   8 +-
 17 files changed, 3620 insertions(+), 4 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/display/rockchip/cdn-dp-rockchip.txt
 create mode 100644 Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.c
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.h
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.c
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.h
 create mode 100644 drivers/phy/phy-rockchip-typec.c

-- 
2.6.3



From zyw at rock-chips.com  Fri Jul 29 00:48:18 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Fri, 29 Jul 2016 15:48:18 +0800
Subject: [v8 PATCH 1/6] extcon: Add EXTCON_DISP_DP and the property for USB
 Type-C
In-Reply-To: <1469778503-29193-1-git-send-email-zyw@rock-chips.com>
References: <1469778503-29193-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1469778503-29193-2-git-send-email-zyw@rock-chips.com>

Add EXTCON_DISP_DP for the Display external connector. For Type-C
connector the DisplayPort can work as an Alternate Mode(VESA DisplayPort
Alt Mode on USB Type-C Standard). The Type-C support both normal
and flipped orientation, so add a property to extcon.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
Signed-off-by: Chanwoo Choi <cw00.choi at samsung.com>
Reviewed-by: Guenter Roeck <groeck at chromium.org>
---

Changes in v8: None
Changes in v7: None
Changes in v6: None
Changes in v5: None
Changes in v4: None
Changes in v3: None
Changes in v2: None
Changes in v1: None

 drivers/extcon/extcon.c | 5 +++++
 include/linux/extcon.h  | 8 +++++++-
 2 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c
index 68890bb..6c7488a 100644
--- a/drivers/extcon/extcon.c
+++ b/drivers/extcon/extcon.c
@@ -157,6 +157,11 @@ struct __extcon_info {
 		.id = EXTCON_DISP_VGA,
 		.name = "VGA",
 	},
+	[EXTCON_DISP_DP] = {
+		.type = EXTCON_TYPE_DISP | EXTCON_TYPE_USB,
+		.id = EXTCON_DISP_DP,
+		.name = "DP",
+	},
 
 	/* Miscellaneous external connector */
 	[EXTCON_DOCK] = {
diff --git a/include/linux/extcon.h b/include/linux/extcon.h
index f686204..9670d3f 100644
--- a/include/linux/extcon.h
+++ b/include/linux/extcon.h
@@ -69,6 +69,7 @@
 #define EXTCON_DISP_MHL		41	/* Mobile High-Definition Link */
 #define EXTCON_DISP_DVI		42	/* Digital Visual Interface */
 #define EXTCON_DISP_VGA		43	/* Video Graphics Array */
+#define EXTCON_DISP_DP		44	/* Display Port */
 
 /* Miscellaneous external connector */
 #define EXTCON_DOCK		60
@@ -106,12 +107,17 @@
  * @type:	integer (intval)
  * @value:	0 (low) or 1 (high)
  * @default:	0 (low)
+ * - EXTCON_PROP_USB_TYPEC_POLARITY
+ * @type:	integer (intval)
+ * @value:	0 (normal) or 1 (flip)
+ * @default:	0 (normal)
  */
 #define EXTCON_PROP_USB_ID		0
 #define EXTCON_PROP_USB_VBUS		1
+#define EXTCON_PROP_USB_TYPEC_POLARITY	2
 
 #define EXTCON_PROP_USB_MIN		0
-#define EXTCON_PROP_USB_MAX		1
+#define EXTCON_PROP_USB_MAX		2
 #define EXTCON_PROP_USB_CNT	(EXTCON_PROP_USB_MAX - EXTCON_PROP_USB_MIN + 1)
 
 /* Properties of EXTCON_TYPE_CHG. */
-- 
2.6.3



From zyw at rock-chips.com  Fri Jul 29 00:48:19 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Fri, 29 Jul 2016 15:48:19 +0800
Subject: [v8 PATCH 2/6] Documentation: bindings: add dt doc for Rockchip USB
 Type-C PHY
In-Reply-To: <1469778503-29193-1-git-send-email-zyw@rock-chips.com>
References: <1469778503-29193-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1469778503-29193-3-git-send-email-zyw@rock-chips.com>

This patch adds a binding that describes the Rockchip USB Type-C PHY
for rk3399

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
Reviewed-by: Tomasz Figa <tfiga at chromium.org>
Reviewed-by: Kever Yang <kever.yang at rock-chips.com>
Acked-by: Rob Herring <robh at kernel.org>

---

Changes in v8: None
Changes in v7: None
Changes in v6:
- add assigned-clocks and assigned-clock-rates

Changes in v5: None
Changes in v4:
- add a #phy-cells node

Changes in v3:
- use compatible: rockchip,rk3399-typec-phy
- use dashes instead of underscores.

Changes in v2:
- add some registers description

Changes in v1:
- add extcon node description
- move the registers in phy driver
- remove the suffix of reset

 .../devicetree/bindings/phy/phy-rockchip-typec.txt | 84 ++++++++++++++++++++++
 1 file changed, 84 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt

diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt b/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt
new file mode 100644
index 0000000..e384056
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt
@@ -0,0 +1,84 @@
+* ROCKCHIP type-c PHY
+---------------------
+
+Required properties:
+ - compatible : must be "rockchip,rk3399-typec-phy"
+ - reg: Address and length of the usb phy control register set
+ - rockchip,grf : phandle to the syscon managing the "general
+   register files"
+ - clocks : phandle + clock specifier for the phy clocks
+ - clock-names : string, clock name, must be "tcpdcore", "tcpdphy-ref";
+ - assigned-clocks: main clock, should be <&cru SCLK_UPHY0_TCPDCORE> or
+		    <&cru SCLK_UPHY1_TCPDCORE>;
+ - assigned-clock-rates : the phy core clk frequency, shall be: 50000000
+ - resets : a list of phandle + reset specifier pairs
+ - reset-names : string reset name, must be:
+		 "uphy", "uphy-pipe", "uphy-tcphy"
+ - extcon : extcon specifier for the Power Delivery
+ - #phy-cells: must be 0. See ./phy-bindings.txt for details.
+
+Note, there are 2 type-c phys for RK3399, and they are almost identical, except
+these registers(description below), every register node contains 3 sections:
+offset, enable bit, write mask bit.
+ - rockchip,typec-conn-dir : the register of type-c connector direction,
+   for type-c phy0, it must be <0xe580 0 16>;
+   for type-c phy1, it must be <0xe58c 0 16>;
+ - rockchip,usb3tousb2-en : the register of type-c force usb3 to usb2 enable
+   control.
+   for type-c phy0, it must be <0xe580 3 19>;
+   for type-c phy1, it must be <0xe58c 3 19>;
+ - rockchip,external-psm : the register of type-c phy external psm clock
+   selection.
+   for type-c phy0, it must be <0xe588 14 30>;
+   for type-c phy1, it must be <0xe594 14 30>;
+ - rockchip,pipe-status : the register of type-c phy pipe status.
+   for type-c phy0, it must be <0xe5c0 0 0>;
+   for type-c phy1, it must be <0xe5c0 16 16>;
+ - rockchip,uphy-dp-sel : the register of type-c phy selection for DP
+   for type-c phy0, it must be <0x6268 19 19>;
+   for type-c phy1, it must be <0x6268 3 19>;
+
+Example:
+	tcphy0: phy at ff7c0000 {
+		compatible = "rockchip,rk3399-typec-phy";
+		reg = <0x0 0xff7c0000 0x0 0x40000>;
+		rockchip,grf = <&grf>;
+		#phy-cells = <0>;
+		extcon = <&fusb0>;
+		clocks = <&cru SCLK_UPHY0_TCPDCORE>,
+			 <&cru SCLK_UPHY0_TCPDPHY_REF>;
+		clock-names = "tcpdcore", "tcpdphy-ref";
+		assigned-clocks = <&cru SCLK_UPHY0_TCPDCORE>;
+		assigned-clock-rates = <50000000>;
+		resets = <&cru SRST_UPHY0>,
+			 <&cru SRST_UPHY0_PIPE_L00>,
+			 <&cru SRST_P_UPHY0_TCPHY>;
+		reset-names = "uphy", "uphy-pipe", "uphy-tcphy";
+		rockchip,typec-conn-dir = <0xe580 0 16>;
+		rockchip,usb3tousb2-en = <0xe580 3 19>;
+		rockchip,external-psm = <0xe588 14 30>;
+		rockchip,pipe-status = <0xe5c0 0 0>;
+		rockchip,uphy-dp-sel = <0x6268 19 19>;
+	};
+
+	tcphy1: phy at ff800000 {
+		compatible = "rockchip,rk3399-typec-phy";
+		reg = <0x0 0xff800000 0x0 0x40000>;
+		rockchip,grf = <&grf>;
+		#phy-cells = <0>;
+		extcon = <&fusb1>;
+		clocks = <&cru SCLK_UPHY1_TCPDCORE>,
+			 <&cru SCLK_UPHY1_TCPDPHY_REF>;
+		clock-names = "tcpdcore", "tcpdphy-ref";
+		assigned-clocks = <&cru SCLK_UPHY1_TCPDCORE>;
+		assigned-clock-rates = <50000000>;
+		resets = <&cru SRST_UPHY1>,
+		         <&cru SRST_UPHY1_PIPE_L00>,
+			 <&cru SRST_P_UPHY1_TCPHY>;
+		reset-names = "uphy", "uphy-pipe", "uphy-tcphy";
+		rockchip,typec-conn-dir = <0xe58c 0 16>;
+		rockchip,usb3tousb2-en = <0xe58c 3 19>;
+		rockchip,external-psm = <0xe594 14 30>;
+		rockchip,pipe-status = <0xe5c0 16 16>;
+		rockchip,uphy-dp-sel = <0x6268 3 19>;
+	};
-- 
2.6.3



From zyw at rock-chips.com  Fri Jul 29 00:48:20 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Fri, 29 Jul 2016 15:48:20 +0800
Subject: [v8 PATCH 3/6] phy: Add USB Type-C PHY driver for rk3399
In-Reply-To: <1469778503-29193-1-git-send-email-zyw@rock-chips.com>
References: <1469778503-29193-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1469778503-29193-4-git-send-email-zyw@rock-chips.com>

Add a PHY provider driver for the rk3399 SoC Type-c PHY. The USB
Type-C PHY is designed to support the USB3 and DP applications. The
PHY basically has two main components: USB3 and DisplyPort. USB3
operates in SuperSpeed mode and the DP can operate at RBR, HBR and
HBR2 data rates.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
Signed-off-by: Kever Yang <kever.yang at rock-chips.com>

---

Changes in v8:
- set the default cable id to EXTCON_USB_HOST
- optimization Error log

Changes in v7:
- support new API of extcon

Changes in v6:
- delete the support of PIN_ASSIGN_A/B
- set the default mode to MODE_DFP_USB
- disable DP PLL at USB3 only mode

Changes in v5:
- support get property from extcon
- remove PIN ASSIGN A/B support

Changes in v4:
- select EXTCON
- use phy framework to control the USB3 and DP function
- rename PIN_MAP_ to PIN_ASSIGN_

Changes in v3:
- remove the phy framework(Kishon Vijay Abraham I)
- add parentheses around the macro
- use a single space between type and name
- add spaces after opening and before closing braces.
- use u16 for register value
- remove type-c phy header file
- CodingStyle optimization
- use some cable extcon to get type-c port information
- add a extcon to notify Display Port

Changes in v2:
- select RESET_CONTROLLER
- alphabetic order
- modify some spelling mistakes
- make mode cleaner
- use bool for enable/disable
- check all of the return value
- return a better err number
- use more readx_poll_timeout()
- clk_disable_unprepare(tcphy->clk_ref);
- remove unuse functions, rockchip_typec_phy_power_on/off
- remove unnecessary typecast from void *
- use dts node to distinguish between phys.

Changes in v1:
- update the licence note
- init core clock to 50MHz
- use extcon API
- remove unused global
- add some comments for magic num
- change usleep_range(1000, 2000) tousleep_range(1000, 1050)
- remove __func__ from dev_err
- return err number when get clk failed
- remove ADDR_ADJ define
- use devm_clk_get(&pdev->dev, "tcpdcore")

 drivers/phy/Kconfig              |   9 +
 drivers/phy/Makefile             |   1 +
 drivers/phy/phy-rockchip-typec.c | 951 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 961 insertions(+)
 create mode 100644 drivers/phy/phy-rockchip-typec.c

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 26566db..83706a5 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -351,6 +351,15 @@ config PHY_ROCKCHIP_DP
 	help
 	  Enable this to support the Rockchip Display Port PHY.
 
+config PHY_ROCKCHIP_TYPEC
+	tristate "Rockchip TYPEC PHY Driver"
+	depends on ARCH_ROCKCHIP && OF
+	select EXTCON
+	select GENERIC_PHY
+	select RESET_CONTROLLER
+	help
+	  Enable this to support the Rockchip USB TYPEC PHY.
+
 config PHY_ST_SPEAR1310_MIPHY
 	tristate "ST SPEAR1310-MIPHY driver"
 	select GENERIC_PHY
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 24596a9..91fa413 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_PHY_QCOM_APQ8064_SATA)	+= phy-qcom-apq8064-sata.o
 obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o
 obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o
 obj-$(CONFIG_PHY_ROCKCHIP_DP)		+= phy-rockchip-dp.o
+obj-$(CONFIG_PHY_ROCKCHIP_TYPEC) += phy-rockchip-typec.o
 obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA)	+= phy-qcom-ipq806x-sata.o
 obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY)	+= phy-spear1310-miphy.o
 obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY)	+= phy-spear1340-miphy.o
diff --git a/drivers/phy/phy-rockchip-typec.c b/drivers/phy/phy-rockchip-typec.c
new file mode 100644
index 0000000..7cd5c38
--- /dev/null
+++ b/drivers/phy/phy-rockchip-typec.c
@@ -0,0 +1,951 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Chris Zhong <zyw at rock-chips.com>
+ *         Kever Yang <kever.yang at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/extcon.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include <linux/mfd/syscon.h>
+#include <linux/phy/phy.h>
+
+#define CMN_SSM_BANDGAP			(0x21 << 2)
+#define CMN_SSM_BIAS			(0x22 << 2)
+#define CMN_PLLSM0_PLLEN		(0x29 << 2)
+#define CMN_PLLSM0_PLLPRE		(0x2a << 2)
+#define CMN_PLLSM0_PLLVREF		(0x2b << 2)
+#define CMN_PLLSM0_PLLLOCK		(0x2c << 2)
+#define CMN_PLLSM1_PLLEN		(0x31 << 2)
+#define CMN_PLLSM1_PLLPRE		(0x32 << 2)
+#define CMN_PLLSM1_PLLVREF		(0x33 << 2)
+#define CMN_PLLSM1_PLLLOCK		(0x34 << 2)
+#define CMN_PLLSM1_USER_DEF_CTRL	(0x37 << 2)
+#define CMN_ICAL_OVRD			(0xc1 << 2)
+#define CMN_PLL0_VCOCAL_OVRD		(0x83 << 2)
+#define CMN_PLL0_VCOCAL_INIT		(0x84 << 2)
+#define CMN_PLL0_VCOCAL_ITER		(0x85 << 2)
+#define CMN_PLL0_LOCK_REFCNT_START	(0x90 << 2)
+#define CMN_PLL0_LOCK_PLLCNT_START	(0x92 << 2)
+#define CMN_PLL0_LOCK_PLLCNT_THR	(0x93 << 2)
+#define CMN_PLL0_INTDIV			(0x94 << 2)
+#define CMN_PLL0_FRACDIV		(0x95 << 2)
+#define CMN_PLL0_HIGH_THR		(0x96 << 2)
+#define CMN_PLL0_DSM_DIAG		(0x97 << 2)
+#define CMN_PLL0_SS_CTRL1		(0x98 << 2)
+#define CMN_PLL0_SS_CTRL2		(0x99 << 2)
+#define CMN_PLL1_VCOCAL_START		(0xa1 << 2)
+#define CMN_PLL1_VCOCAL_OVRD		(0xa3 << 2)
+#define CMN_PLL1_VCOCAL_INIT		(0xa4 << 2)
+#define CMN_PLL1_VCOCAL_ITER		(0xa5 << 2)
+#define CMN_PLL1_LOCK_REFCNT_START	(0xb0 << 2)
+#define CMN_PLL1_LOCK_PLLCNT_START	(0xb2 << 2)
+#define CMN_PLL1_LOCK_PLLCNT_THR	(0xb3 << 2)
+#define CMN_PLL1_INTDIV			(0xb4 << 2)
+#define CMN_PLL1_FRACDIV		(0xb5 << 2)
+#define CMN_PLL1_HIGH_THR		(0xb6 << 2)
+#define CMN_PLL1_DSM_DIAG		(0xb7 << 2)
+#define CMN_PLL1_SS_CTRL1		(0xb8 << 2)
+#define CMN_PLL1_SS_CTRL2		(0xb9 << 2)
+#define CMN_RXCAL_OVRD			(0xd1 << 2)
+#define CMN_TXPUCAL_CTRL		(0xe0 << 2)
+#define CMN_TXPUCAL_OVRD		(0xe1 << 2)
+#define CMN_TXPDCAL_OVRD		(0xf1 << 2)
+#define CMN_DIAG_PLL0_FBH_OVRD		(0x1c0 << 2)
+#define CMN_DIAG_PLL0_FBL_OVRD		(0x1c1 << 2)
+#define CMN_DIAG_PLL0_OVRD		(0x1c2 << 2)
+#define CMN_DIAG_PLL0_V2I_TUNE		(0x1c5 << 2)
+#define CMN_DIAG_PLL0_CP_TUNE		(0x1c6 << 2)
+#define CMN_DIAG_PLL0_LF_PROG		(0x1c7 << 2)
+#define CMN_DIAG_PLL1_FBH_OVRD		(0x1d0 << 2)
+#define CMN_DIAG_PLL1_FBL_OVRD		(0x1d1 << 2)
+#define CMN_DIAG_PLL1_OVRD		(0x1d2 << 2)
+#define CMN_DIAG_PLL1_V2I_TUNE		(0x1d5 << 2)
+#define CMN_DIAG_PLL1_CP_TUNE		(0x1d6 << 2)
+#define CMN_DIAG_PLL1_LF_PROG		(0x1d7 << 2)
+#define CMN_DIAG_PLL1_PTATIS_TUNE1	(0x1d8 << 2)
+#define CMN_DIAG_PLL1_PTATIS_TUNE2	(0x1d9 << 2)
+#define CMN_DIAG_PLL1_INCLK_CTRL	(0x1da << 2)
+#define CMN_DIAG_HSCLK_SEL		(0x1e0 << 2)
+
+#define XCVR_PSM_RCTRL(n)		((0x4001 | ((n) << 9)) << 2)
+#define XCVR_PSM_CAL_TMR(n)		((0x4002 | ((n) << 9)) << 2)
+#define XCVR_PSM_A0IN_TMR(n)		((0x4003 | ((n) << 9)) << 2)
+#define TX_TXCC_CAL_SCLR_MULT(n)	((0x4047 | ((n) << 9)) << 2)
+#define TX_TXCC_CPOST_MULT_00(n)	((0x404c | ((n) << 9)) << 2)
+#define TX_TXCC_CPOST_MULT_01(n)	((0x404d | ((n) << 9)) << 2)
+#define TX_TXCC_CPOST_MULT_10(n)	((0x404e | ((n) << 9)) << 2)
+#define TX_TXCC_CPOST_MULT_11(n)	((0x404f | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_000(n)	((0x4050 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_001(n)	((0x4051 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_010(n)	((0x4052 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_011(n)	((0x4053 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_100(n)	((0x4054 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_101(n)	((0x4055 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_110(n)	((0x4056 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_111(n)	((0x4057 | ((n) << 9)) << 2)
+#define XCVR_DIAG_PLLDRC_CTRL(n)	((0x40e0 | ((n) << 9)) << 2)
+#define XCVR_DIAG_BIDI_CTRL(n)		((0x40e8 | ((n) << 9)) << 2)
+#define XCVR_DIAG_LANE_FCM_EN_MGN(n)	((0x40f2 | ((n) << 9)) << 2)
+#define TX_PSC_A0(n)			((0x4100 | ((n) << 9)) << 2)
+#define TX_PSC_A1(n)			((0x4101 | ((n) << 9)) << 2)
+#define TX_PSC_A2(n)			((0x4102 | ((n) << 9)) << 2)
+#define TX_PSC_A3(n)			((0x4103 | ((n) << 9)) << 2)
+#define TX_RCVDET_CTRL(n)		((0x4120 | ((n) << 9)) << 2)
+#define TX_RCVDET_EN_TMR(n)		((0x4122 | ((n) << 9)) << 2)
+#define TX_RCVDET_ST_TMR(n)		((0x4123 | ((n) << 9)) << 2)
+#define TX_DIAG_TX_DRV(n)		((0x41e1 | ((n) << 9)) << 2)
+#define TX_DIAG_BGREF_PREDRV_DELAY	(0x41e7 << 2)
+#define TX_ANA_CTRL_REG_1		(0x5020 << 2)
+#define TX_ANA_CTRL_REG_2		(0x5021 << 2)
+#define TXDA_COEFF_CALC_CTRL		(0x5022 << 2)
+#define TX_DIG_CTRL_REG_2		(0x5024 << 2)
+#define TXDA_CYA_AUXDA_CYA		(0x5025 << 2)
+#define TX_ANA_CTRL_REG_3		(0x5026 << 2)
+#define TX_ANA_CTRL_REG_4		(0x5027 << 2)
+#define TX_ANA_CTRL_REG_5		(0x5029 << 2)
+
+#define RX_PSC_A0(n)			((0x8000 | ((n) << 9)) << 2)
+#define RX_PSC_A1(n)			((0x8001 | ((n) << 9)) << 2)
+#define RX_PSC_A2(n)			((0x8002 | ((n) << 9)) << 2)
+#define RX_PSC_A3(n)			((0x8003 | ((n) << 9)) << 2)
+#define RX_PSC_CAL(n)			((0x8006 | ((n) << 9)) << 2)
+#define RX_PSC_RDY(n)			((0x8007 | ((n) << 9)) << 2)
+#define RX_IQPI_ILL_CAL_OVRD		(0x8023 << 2)
+#define RX_EPI_ILL_CAL_OVRD		(0x8033 << 2)
+#define RX_SDCAL0_OVRD			(0x8041 << 2)
+#define RX_SDCAL1_OVRD			(0x8049 << 2)
+#define RX_SLC_INIT			(0x806d << 2)
+#define RX_SLC_RUN			(0x806e << 2)
+#define RX_CDRLF_CNFG2			(0x8081 << 2)
+#define RX_SIGDET_HL_FILT_TMR(n)	((0x8090 | ((n) << 9)) << 2)
+#define RX_SLC_IOP0_OVRD		(0x8101 << 2)
+#define RX_SLC_IOP1_OVRD		(0x8105 << 2)
+#define RX_SLC_QOP0_OVRD		(0x8109 << 2)
+#define RX_SLC_QOP1_OVRD		(0x810d << 2)
+#define RX_SLC_EOP0_OVRD		(0x8111 << 2)
+#define RX_SLC_EOP1_OVRD		(0x8115 << 2)
+#define RX_SLC_ION0_OVRD		(0x8119 << 2)
+#define RX_SLC_ION1_OVRD		(0x811d << 2)
+#define RX_SLC_QON0_OVRD		(0x8121 << 2)
+#define RX_SLC_QON1_OVRD		(0x8125 << 2)
+#define RX_SLC_EON0_OVRD		(0x8129 << 2)
+#define RX_SLC_EON1_OVRD		(0x812d << 2)
+#define RX_SLC_IEP0_OVRD		(0x8131 << 2)
+#define RX_SLC_IEP1_OVRD		(0x8135 << 2)
+#define RX_SLC_QEP0_OVRD		(0x8139 << 2)
+#define RX_SLC_QEP1_OVRD		(0x813d << 2)
+#define RX_SLC_EEP0_OVRD		(0x8141 << 2)
+#define RX_SLC_EEP1_OVRD		(0x8145 << 2)
+#define RX_SLC_IEN0_OVRD		(0x8149 << 2)
+#define RX_SLC_IEN1_OVRD		(0x814d << 2)
+#define RX_SLC_QEN0_OVRD		(0x8151 << 2)
+#define RX_SLC_QEN1_OVRD		(0x8155 << 2)
+#define RX_SLC_EEN0_OVRD		(0x8159 << 2)
+#define RX_SLC_EEN1_OVRD		(0x815d << 2)
+#define RX_DIAG_SIGDET_TUNE(n)		((0x81dc | ((n) << 9)) << 2)
+#define RX_DIAG_SC2C_DELAY		(0x81e1 << 2)
+
+#define PMA_LANE_CFG			(0xc000 << 2)
+#define PIPE_CMN_CTRL1			(0xc001 << 2)
+#define PIPE_CMN_CTRL2			(0xc002 << 2)
+#define PIPE_COM_LOCK_CFG1		(0xc003 << 2)
+#define PIPE_COM_LOCK_CFG2		(0xc004 << 2)
+#define PIPE_RCV_DET_INH		(0xc005 << 2)
+#define DP_MODE_CTL			(0xc008 << 2)
+#define DP_CLK_CTL			(0xc009 << 2)
+#define STS				(0xc00F << 2)
+#define PHY_ISO_CMN_CTRL		(0xc010 << 2)
+#define PHY_DP_TX_CTL			(0xc408 << 2)
+#define PMA_CMN_CTRL1			(0xc800 << 2)
+#define PHY_PMA_ISO_CMN_CTRL		(0xc810 << 2)
+#define PHY_ISOLATION_CTRL		(0xc81f << 2)
+#define PHY_PMA_ISO_XCVR_CTRL(n)	((0xcc11 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_LINK_MODE(n)	((0xcc12 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_PWRST_CTRL(n)	((0xcc13 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_TX_DATA_LO(n)	((0xcc14 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_TX_DATA_HI(n)	((0xcc15 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_RX_DATA_LO(n)	((0xcc16 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_RX_DATA_HI(n)	((0xcc17 | ((n) << 6)) << 2)
+#define TX_BIST_CTRL(n)			((0x4140 | ((n) << 9)) << 2)
+#define TX_BIST_UDDWR(n)		((0x4141 | ((n) << 9)) << 2)
+
+/*
+ * Selects which PLL clock will be driven on the analog high speed
+ * clock 0: PLL 0 div 1
+ * clock 1: PLL 1 div 2
+ */
+#define CLK_PLL_CONFIG			0X30
+#define CLK_PLL_MASK			0x33
+
+#define CMN_READY			BIT(0)
+
+#define DP_PLL_CLOCK_ENABLE		BIT(2)
+#define DP_PLL_ENABLE			BIT(0)
+#define DP_PLL_DATA_RATE_RBR		((2 << 12) | (4 << 8))
+#define DP_PLL_DATA_RATE_HBR		((2 << 12) | (4 << 8))
+#define DP_PLL_DATA_RATE_HBR2		((1 << 12) | (2 << 8))
+
+#define GRF_SOC_CON26			0x6268
+#define UPHY_DP_SEL			BIT(3)
+#define UPHY_DP_SEL_MASK		BIT(19)
+#define DPTX_HPD_SEL			(3 << 12)
+#define DPTX_HPD_DEL			(2 << 12)
+#define DPTX_HPD_SEL_MASK		(3 << 28)
+
+#define PHY_MODE_SET_TIMEOUT		1000000
+
+#define PIN_ASSIGN_A			BIT(0)
+#define PIN_ASSIGN_B			BIT(1)
+#define PIN_ASSIGN_C			BIT(2)
+#define PIN_ASSIGN_D			BIT(3)
+#define PIN_ASSIGN_E			BIT(4)
+#define PIN_ASSIGN_F			BIT(5)
+
+#define MODE_DISCONNECT			0
+#define MODE_UFP_USB			BIT(0)
+#define MODE_DFP_USB			BIT(1)
+#define MODE_DFP_DP			BIT(2)
+
+struct usb3phy_reg {
+	u32 offset;
+	u32 enable_bit;
+	u32 write_enable;
+};
+
+struct rockchip_usb3phy_port_cfg {
+	struct usb3phy_reg typec_conn_dir;
+	struct usb3phy_reg usb3tousb2_en;
+	struct usb3phy_reg external_psm;
+	struct usb3phy_reg pipe_status;
+	struct usb3phy_reg uphy_dp_sel;
+};
+
+struct rockchip_typec_phy {
+	struct device *dev;
+	void __iomem *base;
+	struct extcon_dev *extcon;
+	struct phy *phy;
+	struct regmap *grf_regs;
+	struct clk *clk_core;
+	struct clk *clk_ref;
+	struct reset_control *uphy_rst;
+	struct reset_control *pipe_rst;
+	struct reset_control *tcphy_rst;
+	struct rockchip_usb3phy_port_cfg port_cfgs;
+
+	bool flip;
+	u8 mode;
+	u8 pin_assign;
+};
+
+struct phy_reg {
+	u16 value;
+	u32 addr;
+};
+
+struct phy_reg usb_pll_cfg[] = {
+	{ 0xf0,		CMN_PLL0_VCOCAL_INIT },
+	{ 0x18,		CMN_PLL0_VCOCAL_ITER },
+	{ 0xd0,		CMN_PLL0_INTDIV },
+	{ 0x4a4a,	CMN_PLL0_FRACDIV },
+	{ 0x34,		CMN_PLL0_HIGH_THR },
+	{ 0x1ee,	CMN_PLL0_SS_CTRL1 },
+	{ 0x7f03,	CMN_PLL0_SS_CTRL2 },
+	{ 0x20,		CMN_PLL0_DSM_DIAG },
+	{ 0,		CMN_DIAG_PLL0_OVRD },
+	{ 0,		CMN_DIAG_PLL0_FBH_OVRD },
+	{ 0,		CMN_DIAG_PLL0_FBL_OVRD },
+	{ 0x7,		CMN_DIAG_PLL0_V2I_TUNE },
+	{ 0x45,		CMN_DIAG_PLL0_CP_TUNE },
+	{ 0x8,		CMN_DIAG_PLL0_LF_PROG },
+};
+
+struct phy_reg dp_pll_cfg[] = {
+	{ 0xf0,		CMN_PLL1_VCOCAL_INIT },
+	{ 0x18,		CMN_PLL1_VCOCAL_ITER },
+	{ 0x30b9,	CMN_PLL1_VCOCAL_START },
+	{ 0x21c,	CMN_PLL1_INTDIV },
+	{ 0,		CMN_PLL1_FRACDIV },
+	{ 0x5,		CMN_PLL1_HIGH_THR },
+	{ 0x35,		CMN_PLL1_SS_CTRL1 },
+	{ 0x7f1e,	CMN_PLL1_SS_CTRL2 },
+	{ 0x20,		CMN_PLL1_DSM_DIAG },
+	{ 0,		CMN_PLLSM1_USER_DEF_CTRL },
+	{ 0,		CMN_DIAG_PLL1_OVRD },
+	{ 0,		CMN_DIAG_PLL1_FBH_OVRD },
+	{ 0,		CMN_DIAG_PLL1_FBL_OVRD },
+	{ 0x6,		CMN_DIAG_PLL1_V2I_TUNE },
+	{ 0x45,		CMN_DIAG_PLL1_CP_TUNE },
+	{ 0x8,		CMN_DIAG_PLL1_LF_PROG },
+	{ 0x100,	CMN_DIAG_PLL1_PTATIS_TUNE1 },
+	{ 0x7,		CMN_DIAG_PLL1_PTATIS_TUNE2 },
+	{ 0x4,		CMN_DIAG_PLL1_INCLK_CTRL },
+};
+
+static void tcphy_cfg_24m(struct rockchip_typec_phy *tcphy,
+			  u32 num_lanes)
+{
+	u32 i, rdata;
+
+	/*
+	 * cmn_ref_clk_sel = 3, select the 24Mhz for clk parent
+	 * cmn_psm_clk_dig_div = 2, set the clk division to 2
+	 */
+	writel(0x830, tcphy->base + PMA_CMN_CTRL1);
+	for (i = 0; i < num_lanes; i++) {
+		/*
+		 * The following PHY configuration assumes a 24 MHz reference
+		 * clock.
+		 */
+		writel(0x90, tcphy->base + XCVR_DIAG_LANE_FCM_EN_MGN(i));
+		writel(0x960, tcphy->base + TX_RCVDET_EN_TMR(i));
+		writel(0x30, tcphy->base + TX_RCVDET_ST_TMR(i));
+	}
+
+	rdata = readl(tcphy->base + CMN_DIAG_HSCLK_SEL);
+	rdata &= ~CLK_PLL_MASK;
+	rdata |= CLK_PLL_CONFIG;
+	writel(rdata, tcphy->base + CMN_DIAG_HSCLK_SEL);
+}
+
+static void tcphy_cfg_usb_pll(struct rockchip_typec_phy *tcphy)
+{
+	u32 i;
+
+	/* load the configuration of PLL0 */
+	for (i = 0; i < ARRAY_SIZE(usb_pll_cfg); i++)
+		writel(usb_pll_cfg[i].value, tcphy->base + usb_pll_cfg[i].addr);
+}
+
+static void tcphy_cfg_dp_pll(struct rockchip_typec_phy *tcphy)
+{
+	u32 i;
+
+	/* set the default mode to RBR */
+	writel(DP_PLL_CLOCK_ENABLE | DP_PLL_ENABLE | DP_PLL_DATA_RATE_RBR,
+	       tcphy->base + DP_CLK_CTL);
+
+	/* load the configuration of PLL1 */
+	for (i = 0; i < ARRAY_SIZE(dp_pll_cfg); i++)
+		writel(dp_pll_cfg[i].value, tcphy->base + dp_pll_cfg[i].addr);
+}
+
+static void tcphy_tx_usb_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
+{
+	writel(0x7799, tcphy->base + TX_PSC_A0(lane));
+	writel(0x7798, tcphy->base + TX_PSC_A1(lane));
+	writel(0x5098, tcphy->base + TX_PSC_A2(lane));
+	writel(0x5098, tcphy->base + TX_PSC_A3(lane));
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
+	writel(0xbf, tcphy->base + XCVR_DIAG_BIDI_CTRL(lane));
+}
+
+static void tcphy_rx_usb_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
+{
+	writel(0xa6fd, tcphy->base + RX_PSC_A0(lane));
+	writel(0xa6fd, tcphy->base + RX_PSC_A1(lane));
+	writel(0xa410, tcphy->base + RX_PSC_A2(lane));
+	writel(0x2410, tcphy->base + RX_PSC_A3(lane));
+	writel(0x23ff, tcphy->base + RX_PSC_CAL(lane));
+	writel(0x13, tcphy->base + RX_SIGDET_HL_FILT_TMR(lane));
+	writel(0x1004, tcphy->base + RX_DIAG_SIGDET_TUNE(lane));
+	writel(0x2010, tcphy->base + RX_PSC_RDY(lane));
+	writel(0xfb, tcphy->base + XCVR_DIAG_BIDI_CTRL(lane));
+}
+
+static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
+{
+	u16 rdata;
+
+	writel(0xbefc, tcphy->base + XCVR_PSM_RCTRL(lane));
+	writel(0x6799, tcphy->base + TX_PSC_A0(lane));
+	writel(0x6798, tcphy->base + TX_PSC_A1(lane));
+	writel(0x98, tcphy->base + TX_PSC_A2(lane));
+	writel(0x98, tcphy->base + TX_PSC_A3(lane));
+
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_001(lane));
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_010(lane));
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_011(lane));
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_100(lane));
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_101(lane));
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_110(lane));
+	writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_111(lane));
+	writel(0, tcphy->base + TX_TXCC_CPOST_MULT_10(lane));
+	writel(0, tcphy->base + TX_TXCC_CPOST_MULT_01(lane));
+	writel(0, tcphy->base + TX_TXCC_CPOST_MULT_00(lane));
+	writel(0, tcphy->base + TX_TXCC_CPOST_MULT_11(lane));
+
+	writel(0x128, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
+	writel(0x400, tcphy->base + TX_DIAG_TX_DRV(lane));
+
+	rdata = readl(tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
+	rdata = (rdata & 0x8fff) | 0x6000;
+	writel(rdata, tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
+}
+
+static void tcphy_cfg_pin_assign(struct rockchip_typec_phy *tcphy)
+{
+	switch (tcphy->pin_assign) {
+	case PIN_ASSIGN_A:
+		writel(0x19d5, tcphy->base + PMA_LANE_CFG);
+		break;
+	case PIN_ASSIGN_B:
+		writel(0x1500, tcphy->base + PMA_LANE_CFG);
+		break;
+	case PIN_ASSIGN_C:
+	case PIN_ASSIGN_E:
+		writel(0x51d9, tcphy->base + PMA_LANE_CFG);
+		break;
+	case PIN_ASSIGN_D:
+	case PIN_ASSIGN_F:
+		writel(0x5100, tcphy->base + PMA_LANE_CFG);
+		break;
+	};
+}
+
+static inline int property_enable(struct rockchip_typec_phy *tcphy,
+				  const struct usb3phy_reg *reg, bool en)
+{
+	u32 mask = 1 << reg->write_enable;
+	u32 val = en << reg->enable_bit;
+
+	return regmap_write(tcphy->grf_regs, reg->offset, val | mask);
+}
+
+static void tcphy_lanes_config(struct rockchip_typec_phy *tcphy)
+{
+	struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+	u32 i;
+
+	property_enable(tcphy, &cfg->typec_conn_dir, tcphy->flip);
+
+	tcphy_cfg_24m(tcphy, 0x4);
+
+	switch (tcphy->mode) {
+	case MODE_UFP_USB:
+	case MODE_DFP_USB:
+		tcphy_cfg_usb_pll(tcphy);
+		if (tcphy->flip) {
+			tcphy_tx_usb_cfg_lane(tcphy, 3);
+			tcphy_rx_usb_cfg_lane(tcphy, 2);
+		} else {
+			tcphy_tx_usb_cfg_lane(tcphy, 0);
+			tcphy_rx_usb_cfg_lane(tcphy, 1);
+		}
+		break;
+	case MODE_DFP_DP:
+		tcphy_cfg_dp_pll(tcphy);
+		for (i = 0; i < 4; i++)
+			tcphy_dp_cfg_lane(tcphy, i);
+		break;
+	case MODE_DFP_USB | MODE_DFP_DP:
+		tcphy_cfg_usb_pll(tcphy);
+		tcphy_cfg_dp_pll(tcphy);
+		if (tcphy->flip) {
+			tcphy_tx_usb_cfg_lane(tcphy, 3);
+			tcphy_rx_usb_cfg_lane(tcphy, 2);
+			tcphy_dp_cfg_lane(tcphy, 0);
+			tcphy_dp_cfg_lane(tcphy, 1);
+		} else {
+			tcphy_tx_usb_cfg_lane(tcphy, 0);
+			tcphy_rx_usb_cfg_lane(tcphy, 1);
+			tcphy_dp_cfg_lane(tcphy, 2);
+			tcphy_dp_cfg_lane(tcphy, 3);
+		}
+		break;
+	}
+}
+
+static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy)
+{
+	u16 rdata, rdata2, val;
+
+	/* disable txda_cal_latch_en for rewrite the calibration values */
+	rdata = readl(tcphy->base + TX_ANA_CTRL_REG_1);
+	val = rdata & 0xdfff;
+	writel(val, tcphy->base + TX_ANA_CTRL_REG_1);
+
+	/*
+	 * read a resistor calibration code from CMN_TXPUCAL_CTRL[6:0] and
+	 * write it to TX_DIG_CTRL_REG_2[6:0], and delay 1ms to make sure it
+	 * works.
+	 */
+	rdata = readl(tcphy->base + TX_DIG_CTRL_REG_2);
+	rdata = rdata & 0xffc0;
+
+	rdata2 = readl(tcphy->base + CMN_TXPUCAL_CTRL);
+	rdata2 = rdata2 & 0x3f;
+
+	val = rdata | rdata2;
+	writel(val, tcphy->base + TX_DIG_CTRL_REG_2);
+	usleep_range(1000, 1050);
+
+	/*
+	 * Enable signal for latch that sample and holds calibration values.
+	 * Activate this signal for 1 clock cycle to sample new calibration
+	 * values.
+	 */
+	rdata = readl(tcphy->base + TX_ANA_CTRL_REG_1);
+	val = rdata | 0x2000;
+	writel(val, tcphy->base + TX_ANA_CTRL_REG_1);
+	usleep_range(150, 200);
+
+	/* set TX Voltage Level and TX Deemphasis to 0 */
+	writel(0, tcphy->base + PHY_DP_TX_CTL);
+	/* re-enable decap */
+	writel(0x100, tcphy->base + TX_ANA_CTRL_REG_2);
+	writel(0x300, tcphy->base + TX_ANA_CTRL_REG_2);
+	writel(0x2008, tcphy->base + TX_ANA_CTRL_REG_1);
+	writel(0x2018, tcphy->base + TX_ANA_CTRL_REG_1);
+
+	writel(0, tcphy->base + TX_ANA_CTRL_REG_5);
+
+	/*
+	 * Programs txda_drv_ldo_prog[15:0], Sets driver LDO
+	 * voltage 16'h1001 for DP-AUX-TX and RX
+	 */
+	writel(0x1001, tcphy->base + TX_ANA_CTRL_REG_4);
+
+	/* re-enables Bandgap reference for LDO */
+	writel(0x2098, tcphy->base + TX_ANA_CTRL_REG_1);
+	writel(0x2198, tcphy->base + TX_ANA_CTRL_REG_1);
+
+	/*
+	 * re-enables the transmitter pre-driver, driver data selection MUX,
+	 * and receiver detect circuits.
+	 */
+	writel(0x301, tcphy->base + TX_ANA_CTRL_REG_2);
+	writel(0x303, tcphy->base + TX_ANA_CTRL_REG_2);
+
+	/*
+	 * BIT 12: Controls auxda_polarity, which selects the polarity of the
+	 * xcvr:
+	 * 1, Reverses the polarity (If TYPEC, Pulls ups aux_p and pull
+	 * down aux_m)
+	 * 0, Normal polarity (if TYPE_C, pulls up aux_m and pulls down
+	 * aux_p)
+	 */
+	val = 0xa078;
+	if (!tcphy->flip)
+		val |= BIT(12);
+	writel(val, tcphy->base + TX_ANA_CTRL_REG_1);
+
+	writel(0, tcphy->base + TX_ANA_CTRL_REG_3);
+	writel(0, tcphy->base + TX_ANA_CTRL_REG_4);
+	writel(0, tcphy->base + TX_ANA_CTRL_REG_5);
+
+	/*
+	 * Controls low_power_swing_en, set the voltage swing of the driver
+	 * to 400mv. The values	below are peak to peak (differential) values.
+	 */
+	writel(4, tcphy->base + TXDA_COEFF_CALC_CTRL);
+	writel(0, tcphy->base + TXDA_CYA_AUXDA_CYA);
+
+	/* Controls tx_high_z_tm_en */
+	val = readl(tcphy->base + TX_DIG_CTRL_REG_2);
+	val |= BIT(15);
+	writel(val, tcphy->base + TX_DIG_CTRL_REG_2);
+}
+
+static int tcphy_usb3_init(struct rockchip_typec_phy *tcphy)
+{
+	struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+	const struct usb3phy_reg *reg = &cfg->pipe_status;
+	int timeout;
+	unsigned int val;
+
+	/* wait TCPHY for pipe ready */
+	for (timeout = 0; timeout < 100; timeout++) {
+		regmap_read(tcphy->grf_regs, reg->offset, &val);
+		if (!(val & BIT(reg->enable_bit)))
+			return 0;
+		usleep_range(10, 20);
+	}
+
+	dev_err(tcphy->dev, "wait pipe ready timeout!\n");
+	return -EBUSY;
+}
+
+static int tcphy_dp_init(struct rockchip_typec_phy *tcphy)
+{
+	struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+	u16 val;
+	int ret;
+
+	property_enable(tcphy, &cfg->uphy_dp_sel, 1);
+
+	ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
+				 val, val & BIT(6), 1000, PHY_MODE_SET_TIMEOUT);
+	if (ret < 0) {
+		dev_err(tcphy->dev, "failed to wait TCPHY for DP ready\n");
+		return ret;
+	}
+
+	tcphy_dp_aux_calibration(tcphy);
+
+	if (tcphy->mode & MODE_DFP_USB)
+		writel(0xc101, tcphy->base + DP_MODE_CTL);
+	else
+		writel(0x0101, tcphy->base + DP_MODE_CTL);
+
+	ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
+				 val, val & BIT(4), 1000, PHY_MODE_SET_TIMEOUT);
+	if (ret < 0) {
+		dev_warn(tcphy->dev, "failed to wait TCPHY enter A0\n");
+		return -EPROBE_DEFER;
+	}
+
+	return 0;
+}
+
+static int tcphy_phy_init(struct rockchip_typec_phy *tcphy)
+{
+	u32 val;
+	int ret;
+
+	ret = clk_prepare_enable(tcphy->clk_core);
+	if (ret) {
+		dev_err(tcphy->dev, "Failed to prepare_enable core clock\n");
+		return ret;
+	}
+
+	ret = clk_prepare_enable(tcphy->clk_ref);
+	if (ret) {
+		dev_err(tcphy->dev, "Failed to prepare_enable ref clock\n");
+		goto err_clk_core;
+	}
+
+	reset_control_deassert(tcphy->tcphy_rst);
+
+	tcphy_lanes_config(tcphy);
+
+	tcphy_cfg_pin_assign(tcphy);
+
+	if (tcphy->mode & MODE_DFP_DP) {
+		if (tcphy->mode & MODE_DFP_USB)
+			writel(0xc104, tcphy->base + DP_MODE_CTL);
+		else
+			writel(0x0104, tcphy->base + DP_MODE_CTL);
+	}
+
+	reset_control_deassert(tcphy->uphy_rst);
+
+	ret = readx_poll_timeout(readl, tcphy->base + PMA_CMN_CTRL1,
+				 val, val & CMN_READY, 10,
+				 PHY_MODE_SET_TIMEOUT);
+	if (ret < 0) {
+		dev_err(tcphy->dev, "wait pma ready timeout\n");
+		goto timeout_pma_ready;
+	}
+
+	reset_control_deassert(tcphy->pipe_rst);
+
+	return 0;
+
+timeout_pma_ready:
+	reset_control_assert(tcphy->tcphy_rst);
+	reset_control_assert(tcphy->uphy_rst);
+	clk_disable_unprepare(tcphy->clk_ref);
+err_clk_core:
+	clk_disable_unprepare(tcphy->clk_core);
+	return ret;
+}
+
+static void tcphy_phy_deinit(struct rockchip_typec_phy *tcphy)
+{
+	reset_control_assert(tcphy->tcphy_rst);
+	reset_control_assert(tcphy->uphy_rst);
+	reset_control_assert(tcphy->pipe_rst);
+	clk_disable_unprepare(tcphy->clk_core);
+	clk_disable_unprepare(tcphy->clk_ref);
+}
+
+static int rockchip_typec_phy_power_on(struct phy *phy)
+{
+	struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
+	struct extcon_dev *edev = tcphy->extcon;
+	union extcon_property_value property;
+	unsigned int id = EXTCON_USB_HOST;
+	bool dfp, ufp, dp;
+	u8 mode;
+	int ret;
+
+	ufp = extcon_get_state(edev, EXTCON_USB);
+	dfp = extcon_get_state(edev, EXTCON_USB_HOST);
+	dp = extcon_get_state(edev, EXTCON_DISP_DP);
+
+	mode = MODE_DFP_USB;
+	tcphy->pin_assign = PIN_ASSIGN_D;
+
+	if (ufp) {
+		mode = MODE_UFP_USB;
+		id = EXTCON_USB;
+	} else if (dfp && dp) {
+		mode = MODE_DFP_USB | MODE_DFP_DP;
+		tcphy->pin_assign = PIN_ASSIGN_D;
+	} else if (dp) {
+		mode = MODE_DFP_DP;
+		tcphy->pin_assign = PIN_ASSIGN_C;
+		id = EXTCON_DISP_DP;
+	}
+
+	if (tcphy->mode == mode)
+		return 0;
+
+	tcphy->mode = mode;
+	ret = extcon_get_property(edev, id, EXTCON_PROP_USB_TYPEC_POLARITY,
+				  &property);
+	if (ret) {
+		dev_err(tcphy->dev, "get property failed\n");
+		goto err_power_init;
+	}
+
+	tcphy->flip = property.intval ? 1 : 0;
+
+	regmap_write(tcphy->grf_regs, GRF_SOC_CON26,
+		     DPTX_HPD_SEL_MASK | DPTX_HPD_DEL);
+
+	ret = tcphy_phy_init(tcphy);
+	if (ret)
+		goto err_power_init;
+
+	if (mode & (MODE_UFP_USB | MODE_DFP_USB)) {
+		ret = tcphy_usb3_init(tcphy);
+		if (ret)
+			goto err_phy_power_on;
+	}
+
+	if (mode & MODE_DFP_DP) {
+		ret = tcphy_dp_init(tcphy);
+		if (ret)
+			goto err_phy_power_on;
+
+		regmap_write(tcphy->grf_regs, GRF_SOC_CON26,
+			     DPTX_HPD_SEL_MASK | DPTX_HPD_SEL);
+	}
+
+	return 0;
+
+err_phy_power_on:
+	tcphy_phy_deinit(tcphy);
+err_power_init:
+	tcphy->mode = MODE_DISCONNECT;
+	return ret;
+}
+
+static int rockchip_typec_phy_power_off(struct phy *phy)
+{
+	struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
+
+	tcphy_phy_deinit(tcphy);
+	tcphy->mode = MODE_DISCONNECT;
+
+	return 0;
+}
+
+static const struct phy_ops rockchip_tcphy_ops = {
+	.power_on	= rockchip_typec_phy_power_on,
+	.power_off	= rockchip_typec_phy_power_off,
+	.owner		= THIS_MODULE,
+};
+
+static int tcphy_get_param(struct device *dev,
+			   struct usb3phy_reg *reg,
+			   const char *name)
+{
+	u32 buffer[3];
+	int ret;
+
+	ret = of_property_read_u32_array(dev->of_node, name, buffer, 3);
+	if (ret) {
+		dev_err(dev, "Can not parse %s\n", name);
+		return ret;
+	}
+
+	reg->offset = buffer[0];
+	reg->enable_bit = buffer[1];
+	reg->write_enable = buffer[2];
+	return 0;
+}
+
+static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
+			  struct device *dev)
+{
+	struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+	int ret;
+
+	ret = tcphy_get_param(dev, &cfg->typec_conn_dir,
+			      "rockchip,typec-conn-dir");
+	if (ret)
+		return ret;
+
+	ret = tcphy_get_param(dev, &cfg->usb3tousb2_en,
+			      "rockchip,usb3tousb2-en");
+	if (ret)
+		return ret;
+
+	ret = tcphy_get_param(dev, &cfg->external_psm,
+			      "rockchip,external-psm");
+	if (ret)
+		return ret;
+
+	ret = tcphy_get_param(dev, &cfg->pipe_status,
+			      "rockchip,pipe-status");
+	if (ret)
+		return ret;
+
+	ret = tcphy_get_param(dev, &cfg->uphy_dp_sel,
+			      "rockchip,uphy-dp-sel");
+	if (ret)
+		return ret;
+
+	tcphy->grf_regs = syscon_regmap_lookup_by_phandle(dev->of_node,
+							  "rockchip,grf");
+	if (IS_ERR(tcphy->grf_regs)) {
+		dev_err(dev, "could not find grf dt node\n");
+		return PTR_ERR(tcphy->grf_regs);
+	}
+
+	tcphy->clk_core = devm_clk_get(dev, "tcpdcore");
+	if (IS_ERR(tcphy->clk_core)) {
+		dev_err(dev, "could not get uphy core clock\n");
+		return PTR_ERR(tcphy->clk_core);
+	}
+
+	tcphy->clk_ref = devm_clk_get(dev, "tcpdphy-ref");
+	if (IS_ERR(tcphy->clk_ref)) {
+		dev_err(dev, "could not get uphy ref clock\n");
+		return PTR_ERR(tcphy->clk_ref);
+	}
+
+	tcphy->uphy_rst = devm_reset_control_get(dev, "uphy");
+	if (IS_ERR(tcphy->uphy_rst)) {
+		dev_err(dev, "no uphy_rst reset control found\n");
+		return PTR_ERR(tcphy->uphy_rst);
+	}
+
+	tcphy->pipe_rst = devm_reset_control_get(dev, "uphy-pipe");
+	if (IS_ERR(tcphy->pipe_rst)) {
+		dev_err(dev, "no pipe_rst reset control found\n");
+		return PTR_ERR(tcphy->pipe_rst);
+	}
+
+	tcphy->tcphy_rst = devm_reset_control_get(dev, "uphy-tcphy");
+	if (IS_ERR(tcphy->tcphy_rst)) {
+		dev_err(dev, "no tcphy_rst reset control found\n");
+		return PTR_ERR(tcphy->tcphy_rst);
+	}
+
+	return 0;
+}
+
+static void typec_phy_pre_init(struct rockchip_typec_phy *tcphy)
+{
+	struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+
+	reset_control_assert(tcphy->tcphy_rst);
+	reset_control_assert(tcphy->uphy_rst);
+	reset_control_assert(tcphy->pipe_rst);
+
+	/* select external psm clock */
+	property_enable(tcphy, &cfg->external_psm, 1);
+	property_enable(tcphy, &cfg->usb3tousb2_en, 0);
+
+	tcphy->mode = MODE_DISCONNECT;
+}
+
+static int rockchip_typec_phy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct rockchip_typec_phy *tcphy;
+	struct phy_provider *phy_provider;
+	struct resource *res;
+	int ret;
+
+	tcphy = devm_kzalloc(dev, sizeof(*tcphy), GFP_KERNEL);
+	if (!tcphy)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	tcphy->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(tcphy->base))
+		return PTR_ERR(tcphy->base);
+
+	ret = tcphy_parse_dt(tcphy, dev);
+	if (ret)
+		return ret;
+
+	tcphy->dev = dev;
+	platform_set_drvdata(pdev, tcphy);
+
+	typec_phy_pre_init(tcphy);
+
+	tcphy->extcon = extcon_get_edev_by_phandle(dev, 0);
+	if (IS_ERR(tcphy->extcon)) {
+		if (PTR_ERR(tcphy->extcon) != -EPROBE_DEFER)
+			dev_err(dev, "Invalid or missing extcon\n");
+		return PTR_ERR(tcphy->extcon);
+	}
+
+	tcphy->phy = devm_phy_create(dev, NULL, &rockchip_tcphy_ops);
+	if (IS_ERR(tcphy->phy)) {
+		dev_err(dev, "failed to create Tepy-C phy\n");
+		return PTR_ERR(tcphy->phy);
+	}
+
+	phy_set_drvdata(tcphy->phy, tcphy);
+
+	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+	if (IS_ERR(phy_provider)) {
+		dev_err(dev, "Failed to register phy provider\n");
+		return PTR_ERR(phy_provider);
+	}
+
+	return 0;
+}
+
+static const struct of_device_id rockchip_typec_phy_dt_ids[] = {
+	{ .compatible = "rockchip,rk3399-typec-phy" },
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, rockchip_typec_phy_dt_ids);
+
+static struct platform_driver rockchip_typec_phy_driver = {
+	.probe		= rockchip_typec_phy_probe,
+	.driver		= {
+		.name	= "rockchip-typec-phy",
+		.of_match_table = rockchip_typec_phy_dt_ids,
+	},
+};
+
+module_platform_driver(rockchip_typec_phy_driver);
+
+MODULE_AUTHOR("Chris Zhong <zyw at rock-chips.com>");
+MODULE_AUTHOR("Kever Yang <kever.yang at rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip USB TYPE-C PHY driver");
+MODULE_LICENSE("GPL v2");
-- 
2.6.3



From zyw at rock-chips.com  Fri Jul 29 00:48:21 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Fri, 29 Jul 2016 15:48:21 +0800
Subject: [v8 PATCH 4/6] arm64: dts: rockchip: add Type-C phy for RK3399
In-Reply-To: <1469778503-29193-1-git-send-email-zyw@rock-chips.com>
References: <1469778503-29193-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1469778503-29193-5-git-send-email-zyw@rock-chips.com>

There are 2 Type-C phy on RK3399, they are almost same, except the
address of register. They support USB3.0 Type-C and DisplayPort1.3
Alt Mode on USB Type-C. Register a phy, supply it to USB3 controller
and DP controller.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
Reviewed-by: Guenter Roeck <linux at roeck-us.net>
---

Changes in v8: None
Changes in v7: None
Changes in v6: None
Changes in v5: None
Changes in v4: None
Changes in v3: None
Changes in v2: None
Changes in v1: None

 arch/arm64/boot/dts/rockchip/rk3399.dtsi | 44 ++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index 0af03a3..7095ddf 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -952,6 +952,50 @@
 		};
 	};
 
+	tcphy0: phy at ff7c0000 {
+		compatible = "rockchip,rk3399-typec-phy";
+		reg = <0x0 0xff7c0000 0x0 0x40000>;
+		rockchip,grf = <&grf>;
+		#phy-cells = <0>;
+		clocks = <&cru SCLK_UPHY0_TCPDCORE>,
+			 <&cru SCLK_UPHY0_TCPDPHY_REF>;
+		clock-names = "tcpdcore", "tcpdphy-ref";
+		assigned-clocks = <&cru SCLK_UPHY0_TCPDCORE>;
+		assigned-clock-rates = <50000000>;
+		resets = <&cru SRST_UPHY0>,
+			 <&cru SRST_UPHY0_PIPE_L00>,
+			 <&cru SRST_P_UPHY0_TCPHY>;
+		reset-names = "uphy", "uphy-pipe", "uphy-tcphy";
+		rockchip,typec-conn-dir = <0xe580 0 16>;
+		rockchip,usb3tousb2-en = <0xe580 3 19>;
+		rockchip,external-psm = <0xe588 14 30>;
+		rockchip,pipe-status = <0xe5c0 0 0>;
+		rockchip,uphy-dp-sel = <0x6268 19 19>;
+		status = "disabled";
+	};
+
+	tcphy1: phy at ff800000 {
+		compatible = "rockchip,rk3399-typec-phy";
+		reg = <0x0 0xff800000 0x0 0x40000>;
+		rockchip,grf = <&grf>;
+		#phy-cells = <0>;
+		clocks = <&cru SCLK_UPHY1_TCPDCORE>,
+			 <&cru SCLK_UPHY1_TCPDPHY_REF>;
+		clock-names = "tcpdcore", "tcpdphy-ref";
+		assigned-clocks = <&cru SCLK_UPHY1_TCPDCORE>;
+		assigned-clock-rates = <50000000>;
+		resets = <&cru SRST_UPHY1>,
+			 <&cru SRST_UPHY1_PIPE_L00>,
+			 <&cru SRST_P_UPHY1_TCPHY>;
+		reset-names = "uphy", "uphy-pipe", "uphy-tcphy";
+		rockchip,typec-conn-dir = <0xe58c 0 16>;
+		rockchip,usb3tousb2-en = <0xe58c 3 19>;
+		rockchip,external-psm = <0xe594 14 30>;
+		rockchip,pipe-status = <0xe5c0 16 16>;
+		rockchip,uphy-dp-sel = <0x6268 3 19>;
+		status = "disabled";
+	};
+
 	watchdog at ff840000 {
 		compatible = "snps,dw-wdt";
 		reg = <0x0 0xff840000 0x0 0x100>;
-- 
2.6.3



From zyw at rock-chips.com  Fri Jul 29 00:48:22 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Fri, 29 Jul 2016 15:48:22 +0800
Subject: [v8 PATCH 5/6] Documentation: bindings: add dt documentation for cdn
 DP controller
In-Reply-To: <1469778503-29193-1-git-send-email-zyw@rock-chips.com>
References: <1469778503-29193-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1469778503-29193-6-git-send-email-zyw@rock-chips.com>

This patch adds a binding that describes the cdn DP controller for
rk3399.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
Acked-by: Rob Herring <robh at kernel.org>

---

Changes in v8: None
Changes in v7: None
Changes in v6:
- add assigned-clocks and assigned-clock-rates
- add power-domains

Changes in v5: None
Changes in v4:
- add a reset node
- support 2 phys

Changes in v3:
- add SoC specific compatible string
- remove reg = <1>;

Changes in v2: None
Changes in v1:
- add extcon node description
- add #sound-dai-cells description

 .../bindings/display/rockchip/cdn-dp-rockchip.txt  | 74 ++++++++++++++++++++++
 1 file changed, 74 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/rockchip/cdn-dp-rockchip.txt

diff --git a/Documentation/devicetree/bindings/display/rockchip/cdn-dp-rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/cdn-dp-rockchip.txt
new file mode 100644
index 0000000..af87dcc
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/rockchip/cdn-dp-rockchip.txt
@@ -0,0 +1,74 @@
+Rockchip RK3399 specific extensions to the cdn Display Port
+================================
+
+Required properties:
+- compatible: must be "rockchip,rk3399-cdn-dp"
+
+- reg: physical base address of the controller and length
+
+- clocks: from common clock binding: handle to dp clock.
+
+- clock-names: from common clock binding:
+	       Required elements: "core-clk" "pclk" "spdif"
+
+- resets : a list of phandle + reset specifier pairs
+- reset-names : string reset name, must be:
+		"spdif"
+- power-domains : power-domain property defined with a phandle
+		  to respective power domain.
+- assigned-clocks: main clock, should be <&cru SCLK_DP_CORE>
+- assigned-clock-rates : the DP core clk frequency, shall be: 100000000
+
+- rockchip,grf: this soc should set GRF regs, so need get grf here.
+
+- ports: contain a port nodes with endpoint definitions as defined in
+	 Documentation/devicetree/bindings/media/video-interfaces.txt.
+	 contained 2 endpoints, connecting to the output of vop.
+
+- phys: from general PHY binding: the phandle for the PHY device.
+
+- extcon: extcon specifier for the Power Delivery
+
+- #sound-dai-cells = it must be 1 if your system is using 2 DAIs: I2S, SPDIF
+
+-------------------------------------------------------------------------------
+
+Example:
+	cdn_dp: dp at fec00000 {
+		compatible = "rockchip,rk3399-cdn-dp";
+		reg = <0x0 0xfec00000 0x0 0x100000>;
+		interrupts = <GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&cru SCLK_DP_CORE>, <&cru PCLK_DP_CTRL>,
+			 <&cru SCLK_SPDIF_REC_DPTX>;
+		clock-names = "core-clk", "pclk", "spdif";
+		assigned-clocks = <&cru SCLK_DP_CORE>;
+		assigned-clock-rates = <100000000>;
+		power-domains = <&power RK3399_PD_HDCP>;
+		phys = <&tcphy0>, <&tcphy1>;
+		resets = <&cru SRST_DPTX_SPDIF_REC>;
+		reset-names = "spdif";
+		extcon = <&fusb0>, <&fusb1>;
+		rockchip,grf = <&grf>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		#sound-dai-cells = <1>;
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			dp_in: port {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				dp_in_vopb: endpoint at 0 {
+					reg = <0>;
+					remote-endpoint = <&vopb_out_dp>;
+				};
+
+				dp_in_vopl: endpoint at 1 {
+					reg = <1>;
+					remote-endpoint = <&vopl_out_dp>;
+				};
+			};
+		};
+	};
-- 
2.6.3



From zyw at rock-chips.com  Fri Jul 29 00:48:23 2016
From: zyw at rock-chips.com (Chris Zhong)
Date: Fri, 29 Jul 2016 15:48:23 +0800
Subject: [v8 PATCH 6/6] drm/rockchip: cdn-dp: add cdn DP support for rk3399
In-Reply-To: <1469778503-29193-1-git-send-email-zyw@rock-chips.com>
References: <1469778503-29193-1-git-send-email-zyw@rock-chips.com>
Message-ID: <1469778503-29193-7-git-send-email-zyw@rock-chips.com>

Add support for cdn DP controller which is embedded in the rk3399
SoCs. The DP is compliant with DisplayPort Specification,
Version 1.3, This IP is compatible with the rockchip type-c PHY IP.
There is a uCPU in DP controller, it need a firmware to work,
please put the firmware file to /lib/firmware/rockchip/dptx.bin. The
uCPU in charge of aux communication and link training, the host use
mailbox to communicate with the ucpu.
The dclk pin_pol of vop must not be invert for DP.

Signed-off-by: Chris Zhong <zyw at rock-chips.com>
Reviewed-by: Sean Paul <seanpaul at chromium.org>
Acked-by: Mark Yao <mark.yao at rock-chips.com>

---

Changes in v8:
- optimization the err log

Changes in v7:
- support firmware standby when no dptx connection
- optimization the calculation of tu size and valid symbol

Changes in v6:
- add a port struct
- select SND_SOC_HDMI_CODEC
- force reset the phy when hpd detected

Changes in v5:
- alphabetical order
- do not use long, use u32 or u64
- return MODE_CLOCK_HIGH when requested > actual
- Optimized Coding Style
- add a formula to get better tu size and symbol value.
- modify according to Sean Paul's comments
- fixed the fw_wait always 0

Changes in v4:
- use phy framework to control DP phy
- support 2 phys

Changes in v3:
- use EXTCON_DISP_DP and EXTCON_DISP_DP_ALT cable to get dp port state.
- reset spdif before config it
- modify the firmware clk to 100Mhz
- retry load firmware if fw file is requested too early

Changes in v2:
- Alphabetic order
- remove excess error message
- use define clk_rate
- check all return value
- remove dev_set_name(dp->dev, "cdn-dp");
- use schedule_delayed_work
- remove never-called functions
- remove some unnecessary ()

Changes in v1:
- use extcon API
- use hdmi-codec for the DP Asoc
- do not initialize the "ret"
- printk a err log when drm_of_encoder_active_endpoint_id
- modify the dclk pin_pol to a single line

 drivers/gpu/drm/rockchip/Kconfig            |  10 +
 drivers/gpu/drm/rockchip/Makefile           |   1 +
 drivers/gpu/drm/rockchip/cdn-dp-core.c      | 867 +++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/cdn-dp-core.h      | 103 +++
 drivers/gpu/drm/rockchip/cdn-dp-reg.c       | 961 ++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/cdn-dp-reg.h       | 482 ++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c |  13 +-
 drivers/gpu/drm/rockchip/rockchip_drm_vop.h |   9 +
 drivers/gpu/drm/rockchip/rockchip_vop_reg.c |   2 +
 9 files changed, 2445 insertions(+), 3 deletions(-)
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.c
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-core.h
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.c
 create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-reg.h

diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index d30bdc3..20aaafe 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -25,6 +25,16 @@ config ROCKCHIP_ANALOGIX_DP
 	  for the Analogix Core DP driver. If you want to enable DP
 	  on RK3288 based SoC, you should selet this option.
 
+config ROCKCHIP_CDN_DP
+        tristate "Rockchip cdn DP"
+        depends on DRM_ROCKCHIP
+	select SND_SOC_HDMI_CODEC if SND_SOC
+        help
+	  This selects support for Rockchip SoC specific extensions
+	  for the cdn DP driver. If you want to enable Dp on
+	  RK3399 based SoC, you should select this
+	  option.
+
 config ROCKCHIP_DW_HDMI
         tristate "Rockchip specific extensions for Synopsys DW HDMI"
         depends on DRM_ROCKCHIP
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index 05d0713..abdecd5 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -7,6 +7,7 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
 rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
 
 obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
+obj-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
 obj-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
 obj-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o
 obj-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
new file mode 100644
index 0000000..f47f2c3
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
@@ -0,0 +1,867 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Chris Zhong <zyw at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_of.h>
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/extcon.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include <linux/mfd/syscon.h>
+#include <linux/phy/phy.h>
+
+#include <sound/hdmi-codec.h>
+
+#include "cdn-dp-core.h"
+#include "cdn-dp-reg.h"
+#include "rockchip_drm_vop.h"
+
+#define connector_to_dp(c) \
+		container_of(c, struct cdn_dp_device, connector)
+
+#define encoder_to_dp(c) \
+		container_of(c, struct cdn_dp_device, encoder)
+
+/* dp grf register offset */
+#define DP_VOP_SEL		0x6224
+#define DP_SEL_VOP_LIT		BIT(12)
+#define MAX_FW_WAIT_SECS	64
+#define CDN_DP_FIRMWARE		"rockchip/dptx.bin"
+
+struct cdn_dp_data {
+	u8 max_phy;
+};
+
+struct cdn_dp_data rk3399_cdn_dp = {
+	.max_phy = 2,
+};
+
+static const struct of_device_id cdn_dp_dt_ids[] = {
+	{ .compatible = "rockchip,rk3399-cdn-dp",
+		.data = (void *)&rk3399_cdn_dp },
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, cdn_dp_dt_ids);
+
+static int cdn_dp_clk_enable(struct cdn_dp_device *dp)
+{
+	int ret;
+	u32 rate;
+
+	ret = clk_prepare_enable(dp->pclk);
+	if (ret < 0) {
+		dev_err(dp->dev, "cannot enable dp pclk %d\n", ret);
+		goto err_pclk;
+	}
+
+	ret = clk_prepare_enable(dp->core_clk);
+	if (ret < 0) {
+		dev_err(dp->dev, "cannot enable core_clk %d\n", ret);
+		goto err_core_clk;
+	}
+
+	rate = clk_get_rate(dp->core_clk);
+	if (rate < 0) {
+		dev_err(dp->dev, "get clk rate failed: %d\n", rate);
+		goto err_set_rate;
+	}
+
+	cdn_dp_set_fw_clk(dp, rate);
+
+	return 0;
+
+err_set_rate:
+	clk_disable_unprepare(dp->core_clk);
+err_core_clk:
+	clk_disable_unprepare(dp->pclk);
+err_pclk:
+	return ret;
+}
+
+static enum drm_connector_status
+cdn_dp_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct cdn_dp_device *dp = connector_to_dp(connector);
+
+	return dp->hpd_status;
+}
+
+static void cdn_dp_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+static struct drm_connector_funcs cdn_dp_atomic_connector_funcs = {
+	.dpms = drm_atomic_helper_connector_dpms,
+	.detect = cdn_dp_connector_detect,
+	.destroy = cdn_dp_connector_destroy,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int cdn_dp_connector_get_modes(struct drm_connector *connector)
+{
+	struct cdn_dp_device *dp = connector_to_dp(connector);
+	struct edid *edid;
+	int ret = 0;
+
+	edid = drm_do_get_edid(connector, cdn_dp_get_edid_block, dp);
+	if (edid) {
+		dev_dbg(dp->dev, "got edid: width[%d] x height[%d]\n",
+			edid->width_cm, edid->height_cm);
+
+		dp->sink_has_audio = drm_detect_monitor_audio(edid);
+		ret = drm_add_edid_modes(connector, edid);
+		if (ret) {
+			drm_mode_connector_update_edid_property(connector,
+								edid);
+			drm_edid_to_eld(connector, edid);
+		}
+		kfree(edid);
+	}
+
+	return ret;
+}
+
+static struct drm_encoder *
+	cdn_dp_connector_best_encoder(struct drm_connector *connector)
+{
+	struct cdn_dp_device *dp = connector_to_dp(connector);
+
+	return &dp->encoder;
+}
+
+static int cdn_dp_connector_mode_valid(struct drm_connector *connector,
+				       struct drm_display_mode *mode)
+{
+	struct cdn_dp_device *dp = connector_to_dp(connector);
+	struct drm_display_info *display_info = &dp->connector.display_info;
+	u32 requested = mode->clock * display_info->bpc * 3 / 1000;
+	u32 actual, rate, sink_max, source_max = 0;
+	u8 lanes, i;
+
+	/* find the running port */
+	for (i = 0; i < dp->ports; i++) {
+		if (dp->port[i]->phy_status) {
+			source_max = dp->port[i]->cap_lanes;
+			break;
+		}
+	}
+
+	sink_max = drm_dp_max_lane_count(dp->dpcd);
+	lanes = min(source_max, sink_max);
+
+	source_max = drm_dp_bw_code_to_link_rate(CDN_DP_MAX_LINK_RATE);
+	sink_max = drm_dp_max_link_rate(dp->dpcd);
+	rate = min(source_max, sink_max);
+
+	actual = rate * lanes / 100;
+
+	/* efficiency is about 0.8 */
+	actual = actual * 8 / 10;
+
+	if (requested > actual) {
+		dev_dbg(dp->dev, "requested=%d, actual=%d, clock=%d\n",
+			requested, actual, mode->clock);
+		return MODE_CLOCK_HIGH;
+	}
+
+	return MODE_OK;
+}
+
+static struct drm_connector_helper_funcs cdn_dp_connector_helper_funcs = {
+	.get_modes = cdn_dp_connector_get_modes,
+	.best_encoder = cdn_dp_connector_best_encoder,
+	.mode_valid = cdn_dp_connector_mode_valid,
+};
+
+static void cdn_dp_commit(struct drm_encoder *encoder)
+{
+	struct cdn_dp_device *dp = encoder_to_dp(encoder);
+	int ret;
+
+	ret = cdn_dp_training_start(dp);
+	if (ret)
+		return;
+
+	ret = cdn_dp_get_training_status(dp);
+	if (ret)
+		return;
+
+	dev_info(dp->dev, "rate:0x%x, lanes:%d\n",
+		 dp->link.rate, dp->link.num_lanes);
+
+	if (cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE))
+		return;
+
+	if (cdn_dp_config_video(dp))
+		return;
+
+	if (cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID))
+		return;
+
+	dp->dpms_mode = DRM_MODE_DPMS_ON;
+}
+
+static void cdn_dp_encoder_mode_set(struct drm_encoder *encoder,
+				    struct drm_display_mode *mode,
+				    struct drm_display_mode *adjusted)
+{
+	struct cdn_dp_device *dp = encoder_to_dp(encoder);
+	struct drm_display_info *display_info = &dp->connector.display_info;
+	struct rockchip_crtc_state *state;
+	struct video_info *video = &dp->video_info;
+	int ret, val;
+
+	switch (display_info->bpc) {
+	case 16:
+	case 12:
+	case 10:
+		video->color_depth = 10;
+		break;
+	case 6:
+		video->color_depth = 6;
+		break;
+	default:
+		video->color_depth = 8;
+		break;
+	}
+
+	video->color_fmt = PXL_RGB;
+
+	video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC);
+	video->h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC);
+
+	ret = drm_of_encoder_active_endpoint_id(dp->dev->of_node, encoder);
+	if (ret < 0) {
+		dev_err(dp->dev, "Could not get vop id, %d", ret);
+		return;
+	}
+
+	dev_dbg(dp->dev, "vop %s output to cdn-dp\n", (ret) ? "LIT" : "BIG");
+	state = to_rockchip_crtc_state(encoder->crtc->state);
+	if (ret) {
+		val = DP_SEL_VOP_LIT | (DP_SEL_VOP_LIT << 16);
+		state->output_mode = ROCKCHIP_OUT_MODE_P888;
+	} else {
+		val = DP_SEL_VOP_LIT << 16;
+		state->output_mode = ROCKCHIP_OUT_MODE_AAAA;
+	}
+	ret = regmap_write(dp->grf, DP_VOP_SEL, val);
+	if (ret != 0)
+		dev_err(dp->dev, "Could not write to GRF: %d\n", ret);
+
+	memcpy(&dp->mode, adjusted, sizeof(*mode));
+}
+
+static void cdn_dp_encoder_enable(struct drm_encoder *encoder)
+{
+	struct cdn_dp_device *dp = encoder_to_dp(encoder);
+
+	if (dp->dpms_mode != DRM_MODE_DPMS_ON)
+		cdn_dp_commit(encoder);
+}
+
+static void cdn_dp_encoder_disable(struct drm_encoder *encoder)
+{
+	struct cdn_dp_device *dp = encoder_to_dp(encoder);
+
+	dp->dpms_mode = DRM_MODE_DPMS_OFF;
+}
+
+static int
+cdn_dp_encoder_atomic_check(struct drm_encoder *encoder,
+			    struct drm_crtc_state *crtc_state,
+			    struct drm_connector_state *conn_state)
+{
+	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+
+	s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
+	s->output_type = DRM_MODE_CONNECTOR_DisplayPort;
+
+	return 0;
+}
+
+static struct drm_encoder_helper_funcs cdn_dp_encoder_helper_funcs = {
+	.mode_set = cdn_dp_encoder_mode_set,
+	.enable = cdn_dp_encoder_enable,
+	.disable = cdn_dp_encoder_disable,
+	.atomic_check = cdn_dp_encoder_atomic_check,
+};
+
+static struct drm_encoder_funcs cdn_dp_encoder_funcs = {
+	.destroy = drm_encoder_cleanup,
+};
+
+static int cdn_dp_firmware_init(struct cdn_dp_device *dp)
+{
+	int ret;
+	const u32 *iram_data, *dram_data;
+	const struct firmware *fw = dp->fw;
+	const struct cdn_firmware_header *hdr;
+
+	hdr = (struct cdn_firmware_header *)fw->data;
+	if (fw->size != le32_to_cpu(hdr->size_bytes)) {
+		dev_err(dp->dev, "firmware is invalid\n");
+		return -EINVAL;
+	}
+
+	iram_data = (const u32 *)(fw->data + hdr->header_size);
+	dram_data = (const u32 *)(fw->data + hdr->header_size + hdr->iram_size);
+
+	ret = cdn_dp_load_firmware(dp, iram_data, hdr->iram_size,
+				   dram_data, hdr->dram_size);
+	if (ret)
+		return ret;
+
+	ret = cdn_dp_set_firmware_active(dp, true);
+	if (ret) {
+		dev_err(dp->dev, "active ucpu failed: %d\n", ret);
+		return ret;
+	}
+
+	dp->fw_loaded = 1;
+
+	return cdn_dp_event_config(dp);
+}
+
+static int cdn_dp_init(struct cdn_dp_device *dp)
+{
+	struct device *dev = dp->dev;
+	struct device_node *np = dev->of_node;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct resource *res;
+	int ret;
+
+	dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+	if (IS_ERR(dp->grf)) {
+		dev_err(dev, "cdn-dp needs rockchip,grf property\n");
+		return PTR_ERR(dp->grf);
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	dp->regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(dp->regs)) {
+		dev_err(dev, "ioremap reg failed\n");
+		return PTR_ERR(dp->regs);
+	}
+
+	dp->core_clk = devm_clk_get(dev, "core-clk");
+	if (IS_ERR(dp->core_clk)) {
+		dev_err(dev, "cannot get core_clk_dp\n");
+		return PTR_ERR(dp->core_clk);
+	}
+
+	dp->pclk = devm_clk_get(dev, "pclk");
+	if (IS_ERR(dp->pclk)) {
+		dev_err(dev, "cannot get pclk\n");
+		return PTR_ERR(dp->pclk);
+	}
+
+	dp->spdif_clk = devm_clk_get(dev, "spdif");
+	if (IS_ERR(dp->spdif_clk)) {
+		dev_err(dev, "cannot get spdif_clk\n");
+		return PTR_ERR(dp->spdif_clk);
+	}
+
+	dp->spdif_rst = devm_reset_control_get(dev, "spdif");
+	if (IS_ERR(dp->spdif_rst)) {
+		dev_err(dev, "no spdif reset control found\n");
+		return PTR_ERR(dp->spdif_rst);
+	}
+
+	dp->dpms_mode = DRM_MODE_DPMS_OFF;
+
+	ret = cdn_dp_clk_enable(dp);
+	if (ret < 0)
+		return ret;
+
+	cdn_dp_clock_reset(dp);
+
+	return 0;
+}
+
+static int cdn_dp_audio_hw_params(struct device *dev,  void *data,
+				  struct hdmi_codec_daifmt *daifmt,
+				  struct hdmi_codec_params *params)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	struct audio_info audio = {
+		.sample_width = params->sample_width,
+		.sample_rate = params->sample_rate,
+		.channels = params->channels,
+	};
+	int ret;
+
+	if (!dp->encoder.crtc)
+		return -ENODEV;
+
+	switch (daifmt->fmt) {
+	case HDMI_I2S:
+		audio.format = AFMT_I2S;
+		break;
+	case HDMI_SPDIF:
+		audio.format = AFMT_SPDIF;
+		break;
+	default:
+		dev_err(dev, "%s: Invalid format %d\n", __func__, daifmt->fmt);
+		return -EINVAL;
+	}
+
+	ret = cdn_dp_audio_config(dp, &audio);
+	if (!ret)
+		dp->audio_info = audio;
+
+	return ret;
+}
+
+static void cdn_dp_audio_shutdown(struct device *dev, void *data)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	int ret;
+
+	if (!dp->encoder.crtc)
+		return;
+
+	ret = cdn_dp_audio_stop(dp, &dp->audio_info);
+	if (!ret)
+		dp->audio_info.format = AFMT_UNUSED;
+}
+
+static int cdn_dp_audio_digital_mute(struct device *dev, void *data,
+				     bool enable)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+
+	if (!dp->encoder.crtc)
+		return -ENODEV;
+
+	return cdn_dp_audio_mute(dp, enable);
+}
+
+static int cdn_dp_audio_get_eld(struct device *dev, void *data,
+				u8 *buf, size_t len)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	struct drm_mode_config *config = &dp->encoder.dev->mode_config;
+	struct drm_connector *connector;
+	int ret = -ENODEV;
+
+	mutex_lock(&config->mutex);
+	list_for_each_entry(connector, &config->connector_list, head) {
+		if (&dp->encoder == connector->encoder) {
+			memcpy(buf, connector->eld,
+			       min(sizeof(connector->eld), len));
+			ret = 0;
+		}
+	}
+	mutex_unlock(&config->mutex);
+
+	return ret;
+}
+
+static const struct hdmi_codec_ops audio_codec_ops = {
+	.hw_params = cdn_dp_audio_hw_params,
+	.audio_shutdown = cdn_dp_audio_shutdown,
+	.digital_mute = cdn_dp_audio_digital_mute,
+	.get_eld = cdn_dp_audio_get_eld,
+};
+
+static int cdn_dp_audio_codec_init(struct cdn_dp_device *dp,
+				   struct device *dev)
+{
+	struct hdmi_codec_pdata codec_data = {
+		.i2s = 1,
+		.spdif = 1,
+		.ops = &audio_codec_ops,
+		.max_i2s_channels = 8,
+	};
+
+	dp->audio_pdev = platform_device_register_data(
+			 dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO,
+			 &codec_data, sizeof(codec_data));
+
+	return PTR_ERR_OR_ZERO(dp->audio_pdev);
+}
+
+static int cdn_dp_get_cap_lanes(struct cdn_dp_device *dp,
+				struct extcon_dev *edev)
+{
+	bool dfp, dptx;
+	u8 lanes;
+
+	dfp = extcon_get_state(edev, EXTCON_USB_HOST);
+	dptx = extcon_get_state(edev, EXTCON_DISP_DP);
+
+	if (dfp && dptx)
+		lanes = 2;
+	else if (dptx)
+		lanes = 4;
+	else
+		lanes = 0;
+
+	return lanes;
+}
+
+static int cdn_dp_phy_power_ctrl(struct cdn_dp_device *dp,
+				 struct cdn_dp_port *port, bool enable)
+{
+	struct phy *phy = port->phy;
+	int ret;
+	int count = 0;
+
+	while (phy->power_count > 0) {
+		ret = phy_power_off(phy);
+		if (ret)
+			return ret;
+		count++;
+	}
+
+	count = enable ? count + 1 : count - 1;
+	while (count-- > 0) {
+		ret = phy_power_on(phy);
+		if (ret)
+			return ret;
+	}
+
+	port->phy_status = enable;
+	return 0;
+}
+
+static int cdn_dp_pd_event(struct notifier_block *nb,
+			   unsigned long event, void *priv)
+{
+	struct cdn_dp_port *port;
+
+	port = container_of(nb, struct cdn_dp_port, event_nb);
+
+	schedule_delayed_work(&port->event_wq, 0);
+
+	return 0;
+}
+
+static void cdn_dp_pd_event_wq(struct work_struct *work)
+{
+	struct cdn_dp_port *port = container_of(work, struct cdn_dp_port,
+						event_wq.work);
+	struct cdn_dp_device *dp = port->dp;
+	u8 new_cap_lanes, i;
+	int ret;
+
+	new_cap_lanes = cdn_dp_get_cap_lanes(dp, port->extcon);
+
+	if (new_cap_lanes == port->cap_lanes) {
+		dev_dbg(dp->dev, "lanes count does not change: %d\n",
+			new_cap_lanes);
+		return;
+	}
+
+	if (!new_cap_lanes) {
+		ret = cdn_dp_phy_power_ctrl(dp, port, false);
+		if (ret) {
+			dev_err(dp->dev, "phy power off failed: %d", ret);
+			return;
+		}
+		port->phy_status = false;
+		port->cap_lanes = new_cap_lanes;
+		for (i = 0; i < dp->ports; i++) {
+			if (dp->port[i]->phy_status)
+				return;
+		}
+
+		dp->hpd_status = connector_status_disconnected;
+		drm_helper_hpd_irq_event(dp->drm_dev);
+		cdn_dp_set_firmware_active(dp, false);
+		return;
+	}
+
+	/* if other phy is running, do not touch the hpd_status, and return */
+	for (i = 0; i < dp->ports; i++) {
+		if (dp->port[i]->phy_status) {
+			dev_warn(dp->dev, "busy, phy[%d] is running",
+				 dp->port[i]->id);
+			return;
+		}
+	}
+
+	if (!dp->fw_loaded) {
+		ret = request_firmware(&dp->fw, CDN_DP_FIRMWARE, dp->dev);
+		if (ret == -ENOENT && dp->fw_wait <= MAX_FW_WAIT_SECS) {
+			unsigned long time = msecs_to_jiffies(dp->fw_wait * HZ);
+
+			/*
+			 * If can not find the file, retry to load the firmware
+			 * in several seconds, if still failed after 1 minute,
+			 * give up.
+			 */
+			schedule_delayed_work(&port->event_wq, time);
+			dp->fw_wait *= 2;
+			return;
+		} else if (ret) {
+			dev_err(dp->dev, "failed to request firmware: %d\n",
+				ret);
+			return;
+		}
+	}
+
+	if (dp->fw_loaded)
+		cdn_dp_set_firmware_active(dp, true);
+
+	ret = cdn_dp_phy_power_ctrl(dp, port, true);
+	if (ret) {
+		if (!dp->fw_loaded)
+			release_firmware(dp->fw);
+		dev_err(dp->dev, "phy power on failed: %d\n", ret);
+		return;
+	}
+
+	if (!dp->fw_loaded) {
+		ret = cdn_dp_firmware_init(dp);
+		release_firmware(dp->fw);
+		if (ret) {
+			dev_err(dp->dev, "firmware init failed: %d", ret);
+			goto err_firmware;
+		}
+	}
+
+	ret = cdn_dp_get_hpd_status(dp);
+	if (ret <= 0) {
+		if (!ret)
+			dev_err(dp->dev, "hpd does not exist\n");
+		goto err_firmware;
+	}
+
+	ret = cdn_dp_set_host_cap(dp, new_cap_lanes);
+	if (ret) {
+		dev_err(dp->dev, "set host capabilities failed: %d\n", ret);
+		goto err_firmware;
+	}
+
+	/*
+	 * Native read with retry for link status and receiver capability reads
+	 * for cases where the sink may still be asleep.
+	 *
+	 * Sinks are *supposed* to come up within 1ms from an off state, but
+	 * we're also supposed to retry 3 times per the spec.
+	 */
+	for (i = 0; i < 3; i++) {
+		ret = cdn_dp_dpcd_read(dp, 0x000, dp->dpcd,
+				       DP_RECEIVER_CAP_SIZE);
+		if (!ret) {
+			dev_info(dp->dev, "get dpcd success!\n");
+			port->cap_lanes = new_cap_lanes;
+			dp->hpd_status = connector_status_connected;
+			drm_helper_hpd_irq_event(dp->drm_dev);
+			return;
+		}
+		usleep_range(1000, 1050);
+	}
+
+err_firmware:
+	ret = cdn_dp_phy_power_ctrl(dp, port, false);
+	if (ret)
+		dev_err(dp->dev, "phy power off failed: %d", ret);
+	if (dp->fw_loaded)
+		cdn_dp_set_firmware_active(dp, false);
+}
+
+static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	struct drm_encoder *encoder;
+	struct drm_connector *connector;
+	struct cdn_dp_port *port;
+	struct drm_device *drm_dev = data;
+	int ret, i;
+
+	ret = cdn_dp_init(dp);
+	if (ret < 0)
+		return ret;
+
+	dp->drm_dev = drm_dev;
+	dp->hpd_status = connector_status_disconnected;
+	dp->fw_wait = 1;
+
+	encoder = &dp->encoder;
+
+	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
+							     dev->of_node);
+	DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
+
+	ret = drm_encoder_init(drm_dev, encoder, &cdn_dp_encoder_funcs,
+			       DRM_MODE_ENCODER_TMDS, NULL);
+	if (ret) {
+		DRM_ERROR("failed to initialize encoder with drm\n");
+		return ret;
+	}
+
+	drm_encoder_helper_add(encoder, &cdn_dp_encoder_helper_funcs);
+
+	connector = &dp->connector;
+	connector->polled = DRM_CONNECTOR_POLL_HPD;
+	connector->dpms = DRM_MODE_DPMS_OFF;
+
+	ret = drm_connector_init(drm_dev, connector,
+				 &cdn_dp_atomic_connector_funcs,
+				 DRM_MODE_CONNECTOR_DisplayPort);
+	if (ret) {
+		DRM_ERROR("failed to initialize connector with drm\n");
+		goto err_free_encoder;
+	}
+
+	drm_connector_helper_add(connector, &cdn_dp_connector_helper_funcs);
+
+	ret = drm_mode_connector_attach_encoder(connector, encoder);
+	if (ret) {
+		DRM_ERROR("failed to attach connector and encoder\n");
+		goto err_free_connector;
+	}
+
+	cdn_dp_audio_codec_init(dp, dev);
+
+	for (i = 0; i < dp->ports; i++) {
+		port = dp->port[i];
+
+		port->event_nb.notifier_call = cdn_dp_pd_event;
+		INIT_DELAYED_WORK(&port->event_wq, cdn_dp_pd_event_wq);
+		ret = extcon_register_notifier(port->extcon, EXTCON_DISP_DP,
+					       &port->event_nb);
+		if (ret) {
+			dev_err(dev, "register EXTCON_DISP_DP notifier err\n");
+			return ret;
+		}
+
+		if (extcon_get_state(port->extcon, EXTCON_DISP_DP))
+			schedule_delayed_work(&port->event_wq, 0);
+	}
+
+	return 0;
+
+err_free_connector:
+	drm_connector_cleanup(connector);
+err_free_encoder:
+	drm_encoder_cleanup(encoder);
+	return ret;
+}
+
+static void cdn_dp_unbind(struct device *dev, struct device *master, void *data)
+{
+	struct cdn_dp_device *dp = dev_get_drvdata(dev);
+	struct drm_encoder *encoder = &dp->encoder;
+	struct drm_connector *connector = &dp->connector;
+	struct cdn_dp_port *port;
+	int i;
+
+	platform_device_unregister(dp->audio_pdev);
+	cdn_dp_encoder_disable(encoder);
+	encoder->funcs->destroy(encoder);
+	connector->funcs->destroy(connector);
+
+	for (i = 0; i < dp->ports; i++) {
+		port = dp->port[i];
+		extcon_unregister_notifier(port->extcon, EXTCON_DISP_DP,
+					   &port->event_nb);
+	}
+}
+
+static const struct component_ops cdn_dp_component_ops = {
+	.bind = cdn_dp_bind,
+	.unbind = cdn_dp_unbind,
+};
+
+static int cdn_dp_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	const struct of_device_id *match;
+	struct cdn_dp_data *dp_data;
+	struct cdn_dp_port *port;
+	struct cdn_dp_device *dp;
+	struct extcon_dev *extcon;
+	struct phy *phy;
+	int i;
+
+	dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
+	if (!dp)
+		return -ENOMEM;
+	dp->dev = dev;
+
+	match = of_match_node(cdn_dp_dt_ids, pdev->dev.of_node);
+	dp_data = (struct cdn_dp_data *)match->data;
+
+	for (i = 0; i < dp_data->max_phy; i++) {
+		extcon = extcon_get_edev_by_phandle(dev, i);
+		phy = devm_of_phy_get_by_index(dev, dev->of_node, i);
+
+		if (PTR_ERR(extcon) == -EPROBE_DEFER ||
+		    PTR_ERR(phy) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+
+		if (IS_ERR(extcon) || IS_ERR(phy))
+			continue;
+
+		port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+		if (!dp)
+			return -ENOMEM;
+
+		port->extcon = extcon;
+		port->phy = phy;
+		port->dp = dp;
+		port->id = i;
+		dp->port[dp->ports++] = port;
+	}
+
+	if (!dp->ports) {
+		dev_err(dev, "missing extcon or phy\n");
+		return -EINVAL;
+	}
+
+	dev_set_drvdata(dev, dp);
+
+	return component_add(dev, &cdn_dp_component_ops);
+}
+
+static int cdn_dp_remove(struct platform_device *pdev)
+{
+	component_del(&pdev->dev, &cdn_dp_component_ops);
+
+	return 0;
+}
+
+static struct platform_driver cdn_dp_driver = {
+	.probe = cdn_dp_probe,
+	.remove = cdn_dp_remove,
+	.driver = {
+		   .name = "cdn-dp",
+		   .owner = THIS_MODULE,
+		   .of_match_table = of_match_ptr(cdn_dp_dt_ids),
+	},
+};
+
+module_platform_driver(cdn_dp_driver);
+
+MODULE_AUTHOR("Chris Zhong <zyw at rock-chips.com>");
+MODULE_DESCRIPTION("cdn DP Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
new file mode 100644
index 0000000..78424f9
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2016 Chris Zhong <zyw at rock-chips.com>
+ * Copyright (C) 2016 ROCKCHIP, Inc.
+ *
+ * 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.
+ */
+
+#ifndef _CDN_DP_CORE_H
+#define _CDN_DP_CORE_H
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_panel.h>
+#include "rockchip_drm_drv.h"
+
+#define MAX_PHY		2
+
+enum audio_format {
+	AFMT_I2S = 0,
+	AFMT_SPDIF = 1,
+	AFMT_UNUSED,
+};
+
+struct audio_info {
+	enum audio_format format;
+	int sample_rate;
+	int channels;
+	int sample_width;
+};
+
+enum vic_pxl_encoding_format {
+	PXL_RGB = 0x1,
+	YCBCR_4_4_4 = 0x2,
+	YCBCR_4_2_2 = 0x4,
+	YCBCR_4_2_0 = 0x8,
+	Y_ONLY = 0x10,
+};
+
+struct video_info {
+	bool h_sync_polarity;
+	bool v_sync_polarity;
+	bool interlaced;
+	int color_depth;
+	enum vic_pxl_encoding_format color_fmt;
+};
+
+struct cdn_firmware_header {
+	u32 size_bytes; /* size of the entire header+image(s) in bytes */
+	u32 header_size; /* size of just the header in bytes */
+	u32 iram_size; /* size of iram */
+	u32 dram_size; /* size of dram */
+};
+
+struct cdn_dp_port {
+	struct cdn_dp_device *dp;
+	struct notifier_block event_nb;
+	struct delayed_work event_wq;
+	struct extcon_dev *extcon;
+	struct phy *phy;
+	u8 cap_lanes;
+	bool phy_status;
+	u8 id;
+};
+
+struct cdn_dp_device {
+	struct device *dev;
+	struct drm_device *drm_dev;
+	struct drm_connector connector;
+	struct drm_encoder encoder;
+	struct drm_display_mode mode;
+	struct platform_device *audio_pdev;
+
+	const struct firmware *fw;	/* cdn dp firmware */
+	unsigned int fw_version;	/* cdn fw version */
+	u32 fw_wait;
+	bool fw_loaded;
+	bool fw_actived;
+	void __iomem *regs;
+	struct regmap *grf;
+	struct clk *core_clk;
+	struct clk *pclk;
+	struct clk *spdif_clk;
+	struct reset_control *spdif_rst;
+	struct audio_info audio_info;
+	struct video_info video_info;
+	struct drm_dp_link link;
+	struct cdn_dp_port *port[MAX_PHY];
+	u8 ports;
+
+	u8 dpcd[DP_RECEIVER_CAP_SIZE];
+	enum drm_connector_status hpd_status;
+	int dpms_mode;
+	bool sink_has_audio;
+};
+#endif  /* _CDN_DP_CORE_H */
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
new file mode 100644
index 0000000..ba90c93
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
@@ -0,0 +1,961 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Chris Zhong <zyw at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/clk.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/reset.h>
+
+#include "cdn-dp-core.h"
+#include "cdn-dp-reg.h"
+
+#define CDN_DP_SPDIF_CLK		200000000
+#define FW_ALIVE_TIMEOUT_US		1000000
+#define MAILBOX_RETRY_US		1000
+#define MAILBOX_TIMEOUT_US		5000000
+#define LINK_TRAINING_RETRY_MS		20
+#define LINK_TRAINING_TIMEOUT_MS	500
+
+void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, u32 clk)
+{
+	writel(clk / 1000000, dp->regs + SW_CLK_H);
+}
+
+void cdn_dp_clock_reset(struct cdn_dp_device *dp)
+{
+	u32 val;
+
+	val = DPTX_FRMR_DATA_CLK_RSTN_EN |
+	      DPTX_FRMR_DATA_CLK_EN |
+	      DPTX_PHY_DATA_RSTN_EN |
+	      DPTX_PHY_DATA_CLK_EN |
+	      DPTX_PHY_CHAR_RSTN_EN |
+	      DPTX_PHY_CHAR_CLK_EN |
+	      SOURCE_AUX_SYS_CLK_RSTN_EN |
+	      SOURCE_AUX_SYS_CLK_EN |
+	      DPTX_SYS_CLK_RSTN_EN |
+	      DPTX_SYS_CLK_EN |
+	      CFG_DPTX_VIF_CLK_RSTN_EN |
+	      CFG_DPTX_VIF_CLK_EN;
+	writel(val, dp->regs + SOURCE_DPTX_CAR);
+
+	val = SOURCE_PHY_RSTN_EN | SOURCE_PHY_CLK_EN;
+	writel(val, dp->regs + SOURCE_PHY_CAR);
+
+	val = SOURCE_PKT_SYS_RSTN_EN |
+	      SOURCE_PKT_SYS_CLK_EN |
+	      SOURCE_PKT_DATA_RSTN_EN |
+	      SOURCE_PKT_DATA_CLK_EN;
+	writel(val, dp->regs + SOURCE_PKT_CAR);
+
+	val = SPDIF_CDR_CLK_RSTN_EN |
+	      SPDIF_CDR_CLK_EN |
+	      SOURCE_AIF_SYS_RSTN_EN |
+	      SOURCE_AIF_SYS_CLK_EN |
+	      SOURCE_AIF_CLK_RSTN_EN |
+	      SOURCE_AIF_CLK_EN;
+	writel(val, dp->regs + SOURCE_AIF_CAR);
+
+	val = SOURCE_CIPHER_SYSTEM_CLK_RSTN_EN |
+	      SOURCE_CIPHER_SYS_CLK_EN |
+	      SOURCE_CIPHER_CHAR_CLK_RSTN_EN |
+	      SOURCE_CIPHER_CHAR_CLK_EN;
+	writel(val, dp->regs + SOURCE_CIPHER_CAR);
+
+	val = SOURCE_CRYPTO_SYS_CLK_RSTN_EN |
+	      SOURCE_CRYPTO_SYS_CLK_EN;
+	writel(val, dp->regs + SOURCE_CRYPTO_CAR);
+
+	val = ~(MAILBOX_INT_MASK_BIT | PIF_INT_MASK_BIT) & ALL_INT_MASK;
+	writel(val, dp->regs + APB_INT_MASK);
+}
+
+static int cdn_dp_mailbox_read(struct cdn_dp_device *dp, bool force)
+{
+	int val, ret;
+
+	if (!dp->fw_actived && !force)
+		return -EPERM;
+
+	ret = readx_poll_timeout(readl, dp->regs + MAILBOX_EMPTY_ADDR,
+				 val, !val, MAILBOX_RETRY_US,
+				 MAILBOX_TIMEOUT_US);
+	if (ret < 0)
+		return ret;
+
+	return readl(dp->regs + MAILBOX0_RD_DATA) & 0xff;
+}
+
+static int cdp_dp_mailbox_write(struct cdn_dp_device *dp, u8 val, bool force)
+{
+	int ret, full;
+
+	if (!dp->fw_actived && !force)
+		return -EPERM;
+
+	ret = readx_poll_timeout(readl, dp->regs + MAILBOX_FULL_ADDR,
+				 full, !full, MAILBOX_RETRY_US,
+				 MAILBOX_TIMEOUT_US);
+	if (ret < 0)
+		return ret;
+
+	writel(val, dp->regs + MAILBOX0_WR_DATA);
+
+	return 0;
+}
+
+static int cdn_dp_mailbox_validate_receive(struct cdn_dp_device *dp,
+					   u8 module_id, u8 opcode,
+					   u8 req_size)
+{
+	u32 mbox_size, i;
+	u8 header[4];
+	int ret;
+
+	/* read the header of the message */
+	for (i = 0; i < 4; i++) {
+		ret = cdn_dp_mailbox_read(dp, 0);
+		if (ret < 0)
+			return ret;
+
+		header[i] = ret;
+	}
+
+	mbox_size = (header[2] << 8) | header[3];
+
+	if (opcode != header[0] || module_id != header[1] ||
+	    req_size != mbox_size) {
+		/*
+		 * If the message in mailbox is not what we want, we need to
+		 * clear the mailbox by read.
+		 */
+		for (i = 0; i < mbox_size; i++)
+			if (cdn_dp_mailbox_read(dp, 0) < 0)
+				break;
+
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int cdn_dp_mailbox_read_receive(struct cdn_dp_device *dp,
+				       u8 *buff, u8 buff_size)
+{
+	u32 i;
+	int ret;
+
+	for (i = 0; i < buff_size; i++) {
+		ret = cdn_dp_mailbox_read(dp, 0);
+		if (ret < 0)
+			return ret;
+
+		buff[i] = ret;
+	}
+
+	return 0;
+}
+
+static int cdn_dp_mailbox_send(struct cdn_dp_device *dp, u8 module_id,
+			       u8 opcode, u16 size, u8 *message)
+{
+	u8 header[4];
+	int ret, i;
+
+	header[0] = opcode;
+	header[1] = module_id;
+	header[2] = (size >> 8) & 0xff;
+	header[3] = size & 0xff;
+
+	for (i = 0; i < 4; i++) {
+		ret = cdp_dp_mailbox_write(dp, header[i], 0);
+		if (ret)
+			return ret;
+	}
+
+	for (i = 0; i < size; i++) {
+		ret = cdp_dp_mailbox_write(dp, message[i], 0);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
+{
+	u8 msg[6];
+
+	msg[0] = (addr >> 8) & 0xff;
+	msg[1] = addr & 0xff;
+	msg[2] = (val >> 24) & 0xff;
+	msg[3] = (val >> 16) & 0xff;
+	msg[4] = (val >> 8) & 0xff;
+	msg[5] = val & 0xff;
+	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_REGISTER,
+				   sizeof(msg), msg);
+}
+
+static int cdn_dp_reg_write_bit(struct cdn_dp_device *dp, u16 addr,
+				u8 start_bit, u8 bits_no, u32 val)
+{
+	u8 field[8];
+
+	field[0] = (addr >> 8) & 0xff;
+	field[1] = addr & 0xff;
+	field[2] = start_bit;
+	field[3] = bits_no;
+	field[4] = (val >> 24) & 0xff;
+	field[5] = (val >> 16) & 0xff;
+	field[6] = (val >> 8) & 0xff;
+	field[7] = val & 0xff;
+
+	return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_FIELD,
+				   sizeof(field), field);
+}
+
+int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
+{
+	u8 msg[5], reg[5];
+	int ret;
+
+	msg[0] = (len >> 8) & 0xff;
+	msg[1] = len & 0xff;
+	msg[2] = (addr >> 16) & 0xff;
+	msg[3] = (addr >> 8) & 0xff;
+	msg[4] = addr & 0xff;
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_READ_DPCD,
+				  sizeof(msg), msg);
+	if (ret)
+		goto err_dpcd_read;
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_READ_DPCD,
+					      sizeof(reg) + len);
+	if (ret)
+		goto err_dpcd_read;
+
+	ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg));
+	if (ret)
+		goto err_dpcd_read;
+
+	ret = cdn_dp_mailbox_read_receive(dp, data, len);
+
+err_dpcd_read:
+	if (ret)
+		dev_err(dp->dev, "dpcd read failed: %d\n", ret);
+	return ret;
+}
+
+int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
+{
+	u8 msg[6], reg[5];
+	int ret;
+
+	msg[0] = 0;
+	msg[1] = 1;
+	msg[2] = (addr >> 16) & 0xff;
+	msg[3] = (addr >> 8) & 0xff;
+	msg[4] = addr & 0xff;
+	msg[5] = value;
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_DPCD,
+				  sizeof(msg), msg);
+	if (ret)
+		goto err_dpcd_write;
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_WRITE_DPCD, sizeof(reg));
+	if (ret)
+		goto err_dpcd_write;
+
+	ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg));
+	if (ret)
+		goto err_dpcd_write;
+
+	if (addr != (reg[2] << 16 | reg[3] << 8 | reg[4]))
+		ret = -EINVAL;
+
+err_dpcd_write:
+	if (ret)
+		dev_err(dp->dev, "dpcd write failed: %d\n", ret);
+	return ret;
+}
+
+int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
+			 u32 i_size, const u32 *d_mem, u32 d_size)
+{
+	u32 reg;
+	int i, ret;
+
+	/* reset ucpu before load firmware*/
+	writel(APB_IRAM_PATH | APB_DRAM_PATH | APB_XT_RESET,
+	       dp->regs + APB_CTRL);
+
+	for (i = 0; i < i_size; i += 4)
+		writel(*i_mem++, dp->regs + ADDR_IMEM + i);
+
+	for (i = 0; i < d_size; i += 4)
+		writel(*d_mem++, dp->regs + ADDR_DMEM + i);
+
+	/* un-reset ucpu */
+	writel(0, dp->regs + APB_CTRL);
+
+	/* check the keep alive register to make sure fw working */
+	ret = readx_poll_timeout(readl, dp->regs + KEEP_ALIVE,
+				 reg, reg, 2000, FW_ALIVE_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(dp->dev, "failed to loaded the FW reg = %x\n", reg);
+		return -EINVAL;
+	}
+
+	reg = readl(dp->regs + VER_L) & 0xff;
+	dp->fw_version = reg;
+	reg = readl(dp->regs + VER_H) & 0xff;
+	dp->fw_version |= reg << 8;
+	reg = readl(dp->regs + VER_LIB_L_ADDR) & 0xff;
+	dp->fw_version |= reg << 16;
+	reg = readl(dp->regs + VER_LIB_H_ADDR) & 0xff;
+	dp->fw_version |= reg << 24;
+
+	dev_dbg(dp->dev, "firmware version: %x\n", dp->fw_version);
+
+	return 0;
+}
+
+int cdn_dp_set_firmware_active(struct cdn_dp_device *dp, bool enable)
+{
+	u8 msg[5];
+	int ret, i;
+
+	msg[0] = GENERAL_MAIN_CONTROL;
+	msg[1] = MB_MODULE_ID_GENERAL;
+	msg[2] = 0;
+	msg[3] = 1;
+	msg[4] = enable ? FW_ACTIVE : FW_STANDBY;
+
+	for (i = 0; i < sizeof(msg); i++) {
+		ret = cdp_dp_mailbox_write(dp, msg[i], 1);
+		if (ret)
+			goto err_set_firmware_active;
+	}
+
+	/* read the firmware state */
+	for (i = 0; i < sizeof(msg); i++)  {
+		ret = cdn_dp_mailbox_read(dp, 1);
+		if (ret < 0)
+			goto err_set_firmware_active;
+
+		msg[i] = ret;
+	}
+
+	dp->fw_actived = (msg[4] == FW_ACTIVE);
+	ret = 0;
+
+err_set_firmware_active:
+	if (ret < 0)
+		dev_err(dp->dev, "set firmware active failed\n");
+	return ret;
+}
+
+int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes)
+{
+	u8 msg[8];
+	int ret;
+
+	msg[0] = CDN_DP_MAX_LINK_RATE;
+	msg[1] = lanes;
+	msg[2] = VOLTAGE_LEVEL_2;
+	msg[3] = PRE_EMPHASIS_LEVEL_3;
+	msg[4] = PTS1 | PTS2 | PTS3 | PTS4;
+	msg[5] = FAST_LT_NOT_SUPPORT;
+	msg[6] = LANE_MAPPING_NORMAL;
+	msg[7] = ENHANCED;
+
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
+				  DPTX_SET_HOST_CAPABILITIES,
+				  sizeof(msg), msg);
+	if (ret)
+		goto err_set_host_cap;
+
+	ret = cdn_dp_reg_write(dp, DP_AUX_SWAP_INVERSION_CONTROL,
+			       AUX_HOST_INVERT);
+
+err_set_host_cap:
+	if (ret)
+		dev_err(dp->dev, "set host cap failed: %d\n", ret);
+	return ret;
+}
+
+int cdn_dp_event_config(struct cdn_dp_device *dp)
+{
+	u8 msg[5];
+	int ret;
+
+	memset(msg, 0, sizeof(msg));
+
+	msg[0] = DPTX_EVENT_ENABLE_HPD | DPTX_EVENT_ENABLE_TRAINING;
+
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_ENABLE_EVENT,
+				  sizeof(msg), msg);
+	if (ret)
+		dev_err(dp->dev, "set event config failed: %d\n", ret);
+
+	return ret;
+}
+
+u32 cdn_dp_get_event(struct cdn_dp_device *dp)
+{
+	return readl(dp->regs + SW_EVENTS0);
+}
+
+int cdn_dp_get_hpd_status(struct cdn_dp_device *dp)
+{
+	u8 status;
+	int ret;
+
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_HPD_STATE,
+				  0, NULL);
+	if (ret)
+		goto err_get_hpd;
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_HPD_STATE, sizeof(status));
+	if (ret)
+		goto err_get_hpd;
+
+	ret = cdn_dp_mailbox_read_receive(dp, &status, sizeof(status));
+	if (ret)
+		goto err_get_hpd;
+
+	return status;
+
+err_get_hpd:
+	dev_err(dp->dev, "get hpd status failed: %d\n", ret);
+	return ret;
+}
+
+int cdn_dp_get_edid_block(void *data, u8 *edid,
+			  unsigned int block, size_t length)
+{
+	struct cdn_dp_device *dp = data;
+	u8 msg[2], reg[2];
+	int ret;
+
+	msg[0] = block / 2;
+	msg[1] = block % 2;
+
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_GET_EDID,
+				  sizeof(msg), msg);
+	if (ret)
+		goto err_get_edid;
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_GET_EDID,
+					      sizeof(reg) + length);
+	if (ret)
+		goto err_get_edid;
+
+	ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg));
+	if (ret)
+		goto err_get_edid;
+
+	ret = cdn_dp_mailbox_read_receive(dp, edid, length);
+	if (ret)
+		goto err_get_edid;
+
+	if (reg[0] != length || reg[1] != block / 2)
+		ret = -EINVAL;
+
+err_get_edid:
+	if (ret)
+		dev_err(dp->dev, "get block[%d] edid failed: %d\n", block, ret);
+	return ret;
+}
+
+int cdn_dp_training_start(struct cdn_dp_device *dp)
+{
+	unsigned long timeout;
+	u8 msg, event[2];
+	int ret;
+
+	msg = LINK_TRAINING_RUN;
+
+	/* start training */
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_TRAINING_CONTROL,
+				  sizeof(msg), &msg);
+	if (ret)
+		goto err_training_start;
+
+	timeout = jiffies + msecs_to_jiffies(LINK_TRAINING_TIMEOUT_MS);
+	while (time_before(jiffies, timeout)) {
+		msleep(LINK_TRAINING_RETRY_MS);
+		ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
+					  DPTX_READ_EVENT, 0, NULL);
+		if (ret)
+			goto err_training_start;
+
+		ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+						      DPTX_READ_EVENT,
+						      sizeof(event));
+		if (ret)
+			goto err_training_start;
+
+		ret = cdn_dp_mailbox_read_receive(dp, event, sizeof(event));
+		if (ret)
+			goto err_training_start;
+
+		if (event[1] & EQ_PHASE_FINISHED)
+			return 0;
+	}
+
+	ret = -ETIMEDOUT;
+
+err_training_start:
+	dev_err(dp->dev, "training failed: %d\n", ret);
+	return ret;
+}
+
+int cdn_dp_get_training_status(struct cdn_dp_device *dp)
+{
+	u8 status[10];
+	int ret;
+
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_READ_LINK_STAT,
+				  0, NULL);
+	if (ret)
+		goto err_get_training_status;
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_READ_LINK_STAT,
+					      sizeof(status));
+	if (ret)
+		goto err_get_training_status;
+
+	ret = cdn_dp_mailbox_read_receive(dp, status, sizeof(status));
+	if (ret)
+		goto err_get_training_status;
+
+	dp->link.rate = status[0];
+	dp->link.num_lanes = status[1];
+
+err_get_training_status:
+	if (ret)
+		dev_err(dp->dev, "get training status failed: %d\n", ret);
+	return ret;
+}
+
+int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active)
+{
+	u8 msg;
+	int ret;
+
+	msg = !!active;
+
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_SET_VIDEO,
+				  sizeof(msg), &msg);
+	if (ret)
+		dev_err(dp->dev, "set video status failed: %d\n", ret);
+
+	return ret;
+}
+
+static int cdn_dp_get_msa_misc(struct video_info *video,
+			       struct drm_display_mode *mode)
+{
+	u32 msa_misc;
+	u8 val[2];
+
+	switch (video->color_fmt) {
+	case PXL_RGB:
+	case Y_ONLY:
+		val[0] = 0;
+		break;
+	/* set YUV default color space conversion to BT601 */
+	case YCBCR_4_4_4:
+		val[0] = 6 + BT_601 * 8;
+		break;
+	case YCBCR_4_2_2:
+		val[0] = 5 + BT_601 * 8;
+		break;
+	case YCBCR_4_2_0:
+		val[0] = 5;
+		break;
+	};
+
+	switch (video->color_depth) {
+	case 6:
+		val[1] = 0;
+		break;
+	case 8:
+		val[1] = 1;
+		break;
+	case 10:
+		val[1] = 2;
+		break;
+	case 12:
+		val[1] = 3;
+		break;
+	case 16:
+		val[1] = 4;
+		break;
+	};
+
+	msa_misc = 2 * val[0] + 32 * val[1] +
+		   ((video->color_fmt == Y_ONLY) ? (1 << 14) : 0);
+
+	return msa_misc;
+}
+
+int cdn_dp_config_video(struct cdn_dp_device *dp)
+{
+	struct video_info *video = &dp->video_info;
+	struct drm_display_mode *mode = &dp->mode;
+	u64 symbol, tmp;
+	u32 val, link_rate;
+	u8 bit_per_pix, tu_size_reg = TU_SIZE;
+	int ret;
+
+	bit_per_pix = (video->color_fmt == YCBCR_4_2_2) ?
+		      (video->color_depth * 2) : (video->color_depth * 3);
+
+	link_rate = drm_dp_bw_code_to_link_rate(dp->link.rate) / 1000;
+
+	val = VIF_BYPASS_INTERLACE;
+	ret = cdn_dp_reg_write(dp, BND_HSYNC2VSYNC, val);
+	if (ret)
+		goto err_config_video;
+
+	ret = cdn_dp_reg_write(dp, HSYNC2VSYNC_POL_CTRL, 0);
+	if (ret)
+		goto err_config_video;
+
+	/*
+	 * get a best tu_size and valid symbol:
+	 * 1. chose Lclk freq(162Mhz, 270Mhz, 540Mhz), set TU to 32
+	 * 2. calculate VS(valid symbol) = TU * Pclk * Bpp / (Lclk * Lanes)
+	 * 3. if VS > *.85 or VS < *.1 or VS < 2 or TU < VS + 4, then set
+	 *    TU += 2 and repeat 2nd step.
+	 */
+	do {
+		tu_size_reg += 2;
+		tmp = tu_size_reg * mode->clock * bit_per_pix;
+		tmp /= dp->link.num_lanes * link_rate * 8;
+		symbol = tmp / 1000;
+	} while ((symbol <= 1) || (tu_size_reg - symbol < 4) ||
+		 (tmp % 1000 > 850) || (tmp % 1000 < 100));
+
+	val = symbol + (tu_size_reg << 8);
+	ret = cdn_dp_reg_write(dp, DP_FRAMER_TU, val);
+	if (ret)
+		goto err_config_video;
+
+	/* set the FIFO Buffer size */
+	val = ((mode->clock * (symbol + 1) / 1000) + link_rate);
+	val /= (dp->link.num_lanes * link_rate);
+	val = 8 * (symbol + 1) / bit_per_pix - val;
+	val += 2;
+	ret = cdn_dp_reg_write(dp, DP_VC_TABLE(15), val);
+
+	switch (video->color_depth) {
+	case 6:
+		val = BCS_6;
+		break;
+	case 8:
+		val = BCS_8;
+		break;
+	case 10:
+		val = BCS_10;
+		break;
+	case 12:
+		val = BCS_12;
+		break;
+	case 16:
+		val = BCS_16;
+		break;
+	};
+
+	val += video->color_fmt << 8;
+	ret = cdn_dp_reg_write(dp, DP_FRAMER_PXL_REPR, val);
+	if (ret)
+		goto err_config_video;
+
+	val = video->h_sync_polarity ? DP_FRAMER_SP_HSP : 0;
+	val |= video->v_sync_polarity ? DP_FRAMER_SP_VSP : 0;
+	ret = cdn_dp_reg_write(dp, DP_FRAMER_SP, val);
+	if (ret)
+		goto err_config_video;
+
+	val = (mode->hsync_start - mode->hdisplay) << 16;
+	val |= mode->htotal - mode->hsync_end;
+	ret = cdn_dp_reg_write(dp, DP_FRONT_BACK_PORCH, val);
+	if (ret)
+		goto err_config_video;
+
+	val = mode->hdisplay * bit_per_pix / 8;
+	ret = cdn_dp_reg_write(dp, DP_BYTE_COUNT, val);
+	if (ret)
+		goto err_config_video;
+
+	val = mode->htotal | ((mode->htotal - mode->hsync_start) << 16);
+	ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_0, val);
+	if (ret)
+		goto err_config_video;
+
+	val = mode->hsync_end - mode->hsync_start;
+	val |= (mode->hdisplay << 16) | (video->h_sync_polarity << 15);
+	ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_1, val);
+	if (ret)
+		goto err_config_video;
+
+	val = mode->vtotal;
+	val |= ((mode->vtotal - mode->vsync_start) << 16);
+	ret = cdn_dp_reg_write(dp, MSA_VERTICAL_0, val);
+	if (ret)
+		goto err_config_video;
+
+	val = mode->vsync_end - mode->vsync_start;
+	val |= mode->vdisplay << 16 | (video->v_sync_polarity << 15);
+	ret = cdn_dp_reg_write(dp, MSA_VERTICAL_1, val);
+	if (ret)
+		goto err_config_video;
+
+	val = cdn_dp_get_msa_misc(video, mode);
+	ret = cdn_dp_reg_write(dp, MSA_MISC, val);
+	if (ret)
+		goto err_config_video;
+
+	ret = cdn_dp_reg_write(dp, STREAM_CONFIG, 1);
+	if (ret)
+		goto err_config_video;
+
+	val = mode->hsync_end - mode->hsync_start;
+	val |= (mode->hdisplay << 16);
+	ret = cdn_dp_reg_write(dp, DP_HORIZONTAL, val);
+	if (ret)
+		goto err_config_video;
+
+	val = mode->vtotal;
+	val -= (mode->vtotal - mode->vdisplay);
+	val |= (mode->vtotal - mode->vsync_start) << 16;
+	ret = cdn_dp_reg_write(dp, DP_VERTICAL_0, val);
+	if (ret)
+		goto err_config_video;
+
+	val = mode->vtotal;
+	ret = cdn_dp_reg_write(dp, DP_VERTICAL_1, val);
+	if (ret)
+		goto err_config_video;
+
+	val =  0;
+	ret = cdn_dp_reg_write_bit(dp, DP_VB_ID, 2, 1, val);
+
+err_config_video:
+	if (ret)
+		dev_err(dp->dev, "config video failed: %d\n", ret);
+	return ret;
+}
+
+int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio)
+{
+	u32 val;
+	int ret;
+
+	ret = cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, 0);
+	if (ret) {
+		dev_err(dp->dev, "audio stop failed: %d\n", ret);
+		return ret;
+	}
+
+	val = SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS;
+	val |= SPDIF_FIFO_MID_RANGE(0xe0);
+	val |= SPDIF_JITTER_THRSH(0xe0);
+	val |= SPDIF_JITTER_AVG_WIN(7);
+	writel(val, dp->regs + SPDIF_CTRL_ADDR);
+
+	/* clearn the audio config and reset */
+	writel(0, dp->regs + AUDIO_SRC_CNTL);
+	writel(0, dp->regs + AUDIO_SRC_CNFG);
+	writel(AUDIO_SW_RST, dp->regs + AUDIO_SRC_CNTL);
+	writel(0, dp->regs + AUDIO_SRC_CNTL);
+
+	/* reset smpl2pckt component  */
+	writel(0, dp->regs + SMPL2PKT_CNTL);
+	writel(AUDIO_SW_RST, dp->regs + SMPL2PKT_CNTL);
+	writel(0, dp->regs + SMPL2PKT_CNTL);
+
+	/* reset FIFO */
+	writel(AUDIO_SW_RST, dp->regs + FIFO_CNTL);
+	writel(0, dp->regs + FIFO_CNTL);
+
+	if (audio->format == AFMT_SPDIF)
+		clk_disable_unprepare(dp->spdif_clk);
+
+	return 0;
+}
+
+int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable)
+{
+	int ret;
+
+	ret = cdn_dp_reg_write_bit(dp, DP_VB_ID, 4, 1, enable);
+	if (ret)
+		dev_err(dp->dev, "audio mute failed: %d\n", ret);
+
+	return ret;
+}
+
+static void cdn_dp_audio_config_i2s(struct cdn_dp_device *dp,
+				    struct audio_info *audio)
+{
+	int sub_pckt_num = 1, i2s_port_en_val = 0xf, i;
+	u32 val;
+
+	if (audio->channels == 2) {
+		if (dp->link.num_lanes == 1)
+			sub_pckt_num = 2;
+		else
+			sub_pckt_num = 4;
+
+		i2s_port_en_val = 1;
+	} else if (audio->channels == 4) {
+		i2s_port_en_val = 3;
+	}
+
+	writel(0x0, dp->regs + SPDIF_CTRL_ADDR);
+
+	writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL);
+
+	val = MAX_NUM_CH(audio->channels);
+	val |= NUM_OF_I2S_PORTS(audio->channels);
+	val |= AUDIO_TYPE_LPCM;
+	val |= CFG_SUB_PCKT_NUM(sub_pckt_num);
+	writel(val, dp->regs + SMPL2PKT_CNFG);
+
+	if (audio->sample_width == 16)
+		val = 0;
+	else if (audio->sample_width == 24)
+		val = 1 << 9;
+	else
+		val = 2 << 9;
+
+	val |= AUDIO_CH_NUM(audio->channels);
+	val |= I2S_DEC_PORT_EN(i2s_port_en_val);
+	val |= TRANS_SMPL_WIDTH_32;
+	writel(val, dp->regs + AUDIO_SRC_CNFG);
+
+	for (i = 0; i < (audio->channels + 1) / 2; i++) {
+		if (audio->sample_width == 16)
+			val = (0x08 << 8) | (0x08 << 20);
+		else if (audio->sample_width == 24)
+			val = (0x0b << 8) | (0x0b << 20);
+
+		val |= ((2 * i) << 4) | ((2 * i + 1) << 16);
+		writel(val, dp->regs + STTS_BIT_CH(i));
+	}
+
+	switch (audio->sample_rate) {
+	case 32000:
+		val = SAMPLING_FREQ(3) |
+		      ORIGINAL_SAMP_FREQ(0xc);
+		break;
+	case 44100:
+		val = SAMPLING_FREQ(0) |
+		      ORIGINAL_SAMP_FREQ(0xf);
+		break;
+	case 48000:
+		val = SAMPLING_FREQ(2) |
+		      ORIGINAL_SAMP_FREQ(0xd);
+		break;
+	case 88200:
+		val = SAMPLING_FREQ(8) |
+		      ORIGINAL_SAMP_FREQ(0x7);
+		break;
+	case 96000:
+		val = SAMPLING_FREQ(0xa) |
+		      ORIGINAL_SAMP_FREQ(5);
+		break;
+	case 176400:
+		val = SAMPLING_FREQ(0xc) |
+		      ORIGINAL_SAMP_FREQ(3);
+		break;
+	case 192000:
+		val = SAMPLING_FREQ(0xe) |
+		      ORIGINAL_SAMP_FREQ(1);
+		break;
+	}
+	val |= 4;
+	writel(val, dp->regs + COM_CH_STTS_BITS);
+
+	writel(SMPL2PKT_EN, dp->regs + SMPL2PKT_CNTL);
+	writel(I2S_DEC_START, dp->regs + AUDIO_SRC_CNTL);
+}
+
+static void cdn_dp_audio_config_spdif(struct cdn_dp_device *dp)
+{
+	u32 val;
+
+	val = SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS;
+	val |= SPDIF_FIFO_MID_RANGE(0xe0);
+	val |= SPDIF_JITTER_THRSH(0xe0);
+	val |= SPDIF_JITTER_AVG_WIN(7);
+	writel(val, dp->regs + SPDIF_CTRL_ADDR);
+
+	writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL);
+
+	val = MAX_NUM_CH(2) | AUDIO_TYPE_LPCM | CFG_SUB_PCKT_NUM(4);
+	writel(val, dp->regs + SMPL2PKT_CNFG);
+	writel(SMPL2PKT_EN, dp->regs + SMPL2PKT_CNTL);
+
+	val = SPDIF_ENABLE | SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS;
+	val |= SPDIF_FIFO_MID_RANGE(0xe0);
+	val |= SPDIF_JITTER_THRSH(0xe0);
+	val |= SPDIF_JITTER_AVG_WIN(7);
+	writel(val, dp->regs + SPDIF_CTRL_ADDR);
+
+	clk_prepare_enable(dp->spdif_clk);
+	clk_set_rate(dp->spdif_clk, CDN_DP_SPDIF_CLK);
+}
+
+int cdn_dp_audio_config(struct cdn_dp_device *dp, struct audio_info *audio)
+{
+	int ret;
+
+	/* reset the spdif clk before config */
+	if (audio->format == AFMT_SPDIF) {
+		reset_control_assert(dp->spdif_rst);
+		reset_control_deassert(dp->spdif_rst);
+	}
+
+	ret = cdn_dp_reg_write(dp, CM_LANE_CTRL, LANE_REF_CYC);
+	if (ret)
+		goto err_audio_config;
+
+	ret = cdn_dp_reg_write(dp, CM_CTRL, 0);
+	if (ret)
+		goto err_audio_config;
+
+	if (audio->format == AFMT_I2S)
+		cdn_dp_audio_config_i2s(dp, audio);
+	else
+		cdn_dp_audio_config_spdif(dp);
+
+	ret = cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, AUDIO_PACK_EN);
+
+err_audio_config:
+	if (ret)
+		dev_err(dp->dev, "audio config failed: %d\n", ret);
+	return ret;
+}
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
new file mode 100644
index 0000000..da99e28
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
@@ -0,0 +1,482 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Chris Zhong <zyw at rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#ifndef _CDN_DP_REG_H
+#define _CDN_DP_REG_H
+
+#include <linux/bitops.h>
+
+#define ADDR_IMEM		0x10000
+#define ADDR_DMEM		0x20000
+
+/* APB CFG addr */
+#define APB_CTRL			0
+#define XT_INT_CTRL			0x04
+#define MAILBOX_FULL_ADDR		0x08
+#define MAILBOX_EMPTY_ADDR		0x0c
+#define MAILBOX0_WR_DATA		0x10
+#define MAILBOX0_RD_DATA		0x14
+#define KEEP_ALIVE			0x18
+#define VER_L				0x1c
+#define VER_H				0x20
+#define VER_LIB_L_ADDR			0x24
+#define VER_LIB_H_ADDR			0x28
+#define SW_DEBUG_L			0x2c
+#define SW_DEBUG_H			0x30
+#define MAILBOX_INT_MASK		0x34
+#define MAILBOX_INT_STATUS		0x38
+#define SW_CLK_L			0x3c
+#define SW_CLK_H			0x40
+#define SW_EVENTS0			0x44
+#define SW_EVENTS1			0x48
+#define SW_EVENTS2			0x4c
+#define SW_EVENTS3			0x50
+#define XT_OCD_CTRL			0x60
+#define APB_INT_MASK			0x6c
+#define APB_STATUS_MASK			0x70
+
+/* audio decoder addr */
+#define AUDIO_SRC_CNTL			0x30000
+#define AUDIO_SRC_CNFG			0x30004
+#define COM_CH_STTS_BITS		0x30008
+#define STTS_BIT_CH(x)			(0x3000c + ((x) << 2))
+#define SPDIF_CTRL_ADDR			0x3004c
+#define SPDIF_CH1_CS_3100_ADDR		0x30050
+#define SPDIF_CH1_CS_6332_ADDR		0x30054
+#define SPDIF_CH1_CS_9564_ADDR		0x30058
+#define SPDIF_CH1_CS_12796_ADDR		0x3005c
+#define SPDIF_CH1_CS_159128_ADDR	0x30060
+#define SPDIF_CH1_CS_191160_ADDR	0x30064
+#define SPDIF_CH2_CS_3100_ADDR		0x30068
+#define SPDIF_CH2_CS_6332_ADDR		0x3006c
+#define SPDIF_CH2_CS_9564_ADDR		0x30070
+#define SPDIF_CH2_CS_12796_ADDR		0x30074
+#define SPDIF_CH2_CS_159128_ADDR	0x30078
+#define SPDIF_CH2_CS_191160_ADDR	0x3007c
+#define SMPL2PKT_CNTL			0x30080
+#define SMPL2PKT_CNFG			0x30084
+#define FIFO_CNTL			0x30088
+#define FIFO_STTS			0x3008c
+
+/* source pif addr */
+#define SOURCE_PIF_WR_ADDR		0x30800
+#define SOURCE_PIF_WR_REQ		0x30804
+#define SOURCE_PIF_RD_ADDR		0x30808
+#define SOURCE_PIF_RD_REQ		0x3080c
+#define SOURCE_PIF_DATA_WR		0x30810
+#define SOURCE_PIF_DATA_RD		0x30814
+#define SOURCE_PIF_FIFO1_FLUSH		0x30818
+#define SOURCE_PIF_FIFO2_FLUSH		0x3081c
+#define SOURCE_PIF_STATUS		0x30820
+#define SOURCE_PIF_INTERRUPT_SOURCE	0x30824
+#define SOURCE_PIF_INTERRUPT_MASK	0x30828
+#define SOURCE_PIF_PKT_ALLOC_REG	0x3082c
+#define SOURCE_PIF_PKT_ALLOC_WR_EN	0x30830
+#define SOURCE_PIF_SW_RESET		0x30834
+
+/* bellow registers need access by mailbox */
+/* source car addr */
+#define SOURCE_HDTX_CAR			0x0900
+#define SOURCE_DPTX_CAR			0x0904
+#define SOURCE_PHY_CAR			0x0908
+#define SOURCE_CEC_CAR			0x090c
+#define SOURCE_CBUS_CAR			0x0910
+#define SOURCE_PKT_CAR			0x0918
+#define SOURCE_AIF_CAR			0x091c
+#define SOURCE_CIPHER_CAR		0x0920
+#define SOURCE_CRYPTO_CAR		0x0924
+
+/* clock meters addr */
+#define CM_CTRL				0x0a00
+#define CM_I2S_CTRL			0x0a04
+#define CM_SPDIF_CTRL			0x0a08
+#define CM_VID_CTRL			0x0a0c
+#define CM_LANE_CTRL			0x0a10
+#define I2S_NM_STABLE			0x0a14
+#define I2S_NCTS_STABLE			0x0a18
+#define SPDIF_NM_STABLE			0x0a1c
+#define SPDIF_NCTS_STABLE		0x0a20
+#define NMVID_MEAS_STABLE		0x0a24
+#define I2S_MEAS			0x0a40
+#define SPDIF_MEAS			0x0a80
+#define NMVID_MEAS			0x0ac0
+
+/* source vif addr */
+#define BND_HSYNC2VSYNC			0x0b00
+#define HSYNC2VSYNC_F1_L1		0x0b04
+#define HSYNC2VSYNC_F2_L1		0x0b08
+#define HSYNC2VSYNC_STATUS		0x0b0c
+#define HSYNC2VSYNC_POL_CTRL		0x0b10
+
+/* dptx phy addr */
+#define DP_TX_PHY_CONFIG_REG		0x2000
+#define DP_TX_PHY_STATUS_REG		0x2004
+#define DP_TX_PHY_SW_RESET		0x2008
+#define DP_TX_PHY_SCRAMBLER_SEED	0x200c
+#define DP_TX_PHY_TRAINING_01_04	0x2010
+#define DP_TX_PHY_TRAINING_05_08	0x2014
+#define DP_TX_PHY_TRAINING_09_10	0x2018
+#define TEST_COR			0x23fc
+
+/* dptx hpd addr */
+#define HPD_IRQ_DET_MIN_TIMER		0x2100
+#define HPD_IRQ_DET_MAX_TIMER		0x2104
+#define HPD_UNPLGED_DET_MIN_TIMER	0x2108
+#define HPD_STABLE_TIMER		0x210c
+#define HPD_FILTER_TIMER		0x2110
+#define HPD_EVENT_MASK			0x211c
+#define HPD_EVENT_DET			0x2120
+
+/* dpyx framer addr */
+#define DP_FRAMER_GLOBAL_CONFIG		0x2200
+#define DP_SW_RESET			0x2204
+#define DP_FRAMER_TU			0x2208
+#define DP_FRAMER_PXL_REPR		0x220c
+#define DP_FRAMER_SP			0x2210
+#define AUDIO_PACK_CONTROL		0x2214
+#define DP_VC_TABLE(x)			(0x2218 + ((x) << 2))
+#define DP_VB_ID			0x2258
+#define DP_MTPH_LVP_CONTROL		0x225c
+#define DP_MTPH_SYMBOL_VALUES		0x2260
+#define DP_MTPH_ECF_CONTROL		0x2264
+#define DP_MTPH_ACT_CONTROL		0x2268
+#define DP_MTPH_STATUS			0x226c
+#define DP_INTERRUPT_SOURCE		0x2270
+#define DP_INTERRUPT_MASK		0x2274
+#define DP_FRONT_BACK_PORCH		0x2278
+#define DP_BYTE_COUNT			0x227c
+
+/* dptx stream addr */
+#define MSA_HORIZONTAL_0		0x2280
+#define MSA_HORIZONTAL_1		0x2284
+#define MSA_VERTICAL_0			0x2288
+#define MSA_VERTICAL_1			0x228c
+#define MSA_MISC			0x2290
+#define STREAM_CONFIG			0x2294
+#define AUDIO_PACK_STATUS		0x2298
+#define VIF_STATUS			0x229c
+#define PCK_STUFF_STATUS_0		0x22a0
+#define PCK_STUFF_STATUS_1		0x22a4
+#define INFO_PACK_STATUS		0x22a8
+#define RATE_GOVERNOR_STATUS		0x22ac
+#define DP_HORIZONTAL			0x22b0
+#define DP_VERTICAL_0			0x22b4
+#define DP_VERTICAL_1			0x22b8
+#define DP_BLOCK_SDP			0x22bc
+
+/* dptx glbl addr */
+#define DPTX_LANE_EN			0x2300
+#define DPTX_ENHNCD			0x2304
+#define DPTX_INT_MASK			0x2308
+#define DPTX_INT_STATUS			0x230c
+
+/* dp aux addr */
+#define DP_AUX_HOST_CONTROL		0x2800
+#define DP_AUX_INTERRUPT_SOURCE		0x2804
+#define DP_AUX_INTERRUPT_MASK		0x2808
+#define DP_AUX_SWAP_INVERSION_CONTROL	0x280c
+#define DP_AUX_SEND_NACK_TRANSACTION	0x2810
+#define DP_AUX_CLEAR_RX			0x2814
+#define DP_AUX_CLEAR_TX			0x2818
+#define DP_AUX_TIMER_STOP		0x281c
+#define DP_AUX_TIMER_CLEAR		0x2820
+#define DP_AUX_RESET_SW			0x2824
+#define DP_AUX_DIVIDE_2M		0x2828
+#define DP_AUX_TX_PREACHARGE_LENGTH	0x282c
+#define DP_AUX_FREQUENCY_1M_MAX		0x2830
+#define DP_AUX_FREQUENCY_1M_MIN		0x2834
+#define DP_AUX_RX_PRE_MIN		0x2838
+#define DP_AUX_RX_PRE_MAX		0x283c
+#define DP_AUX_TIMER_PRESET		0x2840
+#define DP_AUX_NACK_FORMAT		0x2844
+#define DP_AUX_TX_DATA			0x2848
+#define DP_AUX_RX_DATA			0x284c
+#define DP_AUX_TX_STATUS		0x2850
+#define DP_AUX_RX_STATUS		0x2854
+#define DP_AUX_RX_CYCLE_COUNTER		0x2858
+#define DP_AUX_MAIN_STATES		0x285c
+#define DP_AUX_MAIN_TIMER		0x2860
+#define DP_AUX_AFE_OUT			0x2864
+
+/* crypto addr */
+#define CRYPTO_HDCP_REVISION		0x5800
+#define HDCP_CRYPTO_CONFIG		0x5804
+#define CRYPTO_INTERRUPT_SOURCE		0x5808
+#define CRYPTO_INTERRUPT_MASK		0x580c
+#define CRYPTO22_CONFIG			0x5818
+#define CRYPTO22_STATUS			0x581c
+#define SHA_256_DATA_IN			0x583c
+#define SHA_256_DATA_OUT_(x)		(0x5850 + ((x) << 2))
+#define AES_32_KEY_(x)			(0x5870 + ((x) << 2))
+#define AES_32_DATA_IN			0x5880
+#define AES_32_DATA_OUT_(x)		(0x5884 + ((x) << 2))
+#define CRYPTO14_CONFIG			0x58a0
+#define CRYPTO14_STATUS			0x58a4
+#define CRYPTO14_PRNM_OUT		0x58a8
+#define CRYPTO14_KM_0			0x58ac
+#define CRYPTO14_KM_1			0x58b0
+#define CRYPTO14_AN_0			0x58b4
+#define CRYPTO14_AN_1			0x58b8
+#define CRYPTO14_YOUR_KSV_0		0x58bc
+#define CRYPTO14_YOUR_KSV_1		0x58c0
+#define CRYPTO14_MI_0			0x58c4
+#define CRYPTO14_MI_1			0x58c8
+#define CRYPTO14_TI_0			0x58cc
+#define CRYPTO14_KI_0			0x58d0
+#define CRYPTO14_KI_1			0x58d4
+#define CRYPTO14_BLOCKS_NUM		0x58d8
+#define CRYPTO14_KEY_MEM_DATA_0		0x58dc
+#define CRYPTO14_KEY_MEM_DATA_1		0x58e0
+#define CRYPTO14_SHA1_MSG_DATA		0x58e4
+#define CRYPTO14_SHA1_V_VALUE_(x)	(0x58e8 + ((x) << 2))
+#define TRNG_CTRL			0x58fc
+#define TRNG_DATA_RDY			0x5900
+#define TRNG_DATA			0x5904
+
+/* cipher addr */
+#define HDCP_REVISION			0x60000
+#define INTERRUPT_SOURCE		0x60004
+#define INTERRUPT_MASK			0x60008
+#define HDCP_CIPHER_CONFIG		0x6000c
+#define AES_128_KEY_0			0x60010
+#define AES_128_KEY_1			0x60014
+#define AES_128_KEY_2			0x60018
+#define AES_128_KEY_3			0x6001c
+#define AES_128_RANDOM_0		0x60020
+#define AES_128_RANDOM_1		0x60024
+#define CIPHER14_KM_0			0x60028
+#define CIPHER14_KM_1			0x6002c
+#define CIPHER14_STATUS			0x60030
+#define CIPHER14_RI_PJ_STATUS		0x60034
+#define CIPHER_MODE			0x60038
+#define CIPHER14_AN_0			0x6003c
+#define CIPHER14_AN_1			0x60040
+#define CIPHER22_AUTH			0x60044
+#define CIPHER14_R0_DP_STATUS		0x60048
+#define CIPHER14_BOOTSTRAP		0x6004c
+
+#define DPTX_FRMR_DATA_CLK_RSTN_EN	BIT(11)
+#define DPTX_FRMR_DATA_CLK_EN		BIT(10)
+#define DPTX_PHY_DATA_RSTN_EN		BIT(9)
+#define DPTX_PHY_DATA_CLK_EN		BIT(8)
+#define DPTX_PHY_CHAR_RSTN_EN		BIT(7)
+#define DPTX_PHY_CHAR_CLK_EN		BIT(6)
+#define SOURCE_AUX_SYS_CLK_RSTN_EN	BIT(5)
+#define SOURCE_AUX_SYS_CLK_EN		BIT(4)
+#define DPTX_SYS_CLK_RSTN_EN		BIT(3)
+#define DPTX_SYS_CLK_EN			BIT(2)
+#define CFG_DPTX_VIF_CLK_RSTN_EN	BIT(1)
+#define CFG_DPTX_VIF_CLK_EN		BIT(0)
+
+#define SOURCE_PHY_RSTN_EN		BIT(1)
+#define SOURCE_PHY_CLK_EN		BIT(0)
+
+#define SOURCE_PKT_SYS_RSTN_EN		BIT(3)
+#define SOURCE_PKT_SYS_CLK_EN		BIT(2)
+#define SOURCE_PKT_DATA_RSTN_EN		BIT(1)
+#define SOURCE_PKT_DATA_CLK_EN		BIT(0)
+
+#define SPDIF_CDR_CLK_RSTN_EN		BIT(5)
+#define SPDIF_CDR_CLK_EN		BIT(4)
+#define SOURCE_AIF_SYS_RSTN_EN		BIT(3)
+#define SOURCE_AIF_SYS_CLK_EN		BIT(2)
+#define SOURCE_AIF_CLK_RSTN_EN		BIT(1)
+#define SOURCE_AIF_CLK_EN		BIT(0)
+
+#define SOURCE_CIPHER_SYSTEM_CLK_RSTN_EN	BIT(3)
+#define SOURCE_CIPHER_SYS_CLK_EN		BIT(2)
+#define SOURCE_CIPHER_CHAR_CLK_RSTN_EN		BIT(1)
+#define SOURCE_CIPHER_CHAR_CLK_EN		BIT(0)
+
+#define SOURCE_CRYPTO_SYS_CLK_RSTN_EN	BIT(1)
+#define SOURCE_CRYPTO_SYS_CLK_EN	BIT(0)
+
+#define APB_IRAM_PATH			BIT(2)
+#define APB_DRAM_PATH			BIT(1)
+#define APB_XT_RESET			BIT(0)
+
+#define MAILBOX_INT_MASK_BIT		BIT(1)
+#define PIF_INT_MASK_BIT		BIT(0)
+#define ALL_INT_MASK			3
+
+/* mailbox */
+#define MB_OPCODE_ID			0
+#define MB_MODULE_ID			1
+#define MB_SIZE_MSB_ID			2
+#define MB_SIZE_LSB_ID			3
+#define MB_DATA_ID			4
+
+#define MB_MODULE_ID_DP_TX		0x01
+#define MB_MODULE_ID_HDCP_TX		0x07
+#define MB_MODULE_ID_HDCP_RX		0x08
+#define MB_MODULE_ID_HDCP_GENERAL	0x09
+#define MB_MODULE_ID_GENERAL		0x0a
+
+/* general opcode */
+#define GENERAL_MAIN_CONTROL            0x01
+#define GENERAL_TEST_ECHO               0x02
+#define GENERAL_BUS_SETTINGS            0x03
+#define GENERAL_TEST_ACCESS             0x04
+
+#define DPTX_SET_POWER_MNG			0x00
+#define DPTX_SET_HOST_CAPABILITIES		0x01
+#define DPTX_GET_EDID				0x02
+#define DPTX_READ_DPCD				0x03
+#define DPTX_WRITE_DPCD				0x04
+#define DPTX_ENABLE_EVENT			0x05
+#define DPTX_WRITE_REGISTER			0x06
+#define DPTX_READ_REGISTER			0x07
+#define DPTX_WRITE_FIELD			0x08
+#define DPTX_TRAINING_CONTROL			0x09
+#define DPTX_READ_EVENT				0x0a
+#define DPTX_READ_LINK_STAT			0x0b
+#define DPTX_SET_VIDEO				0x0c
+#define DPTX_SET_AUDIO				0x0d
+#define DPTX_GET_LAST_AUX_STAUS			0x0e
+#define DPTX_SET_LINK_BREAK_POINT		0x0f
+#define DPTX_FORCE_LANES			0x10
+#define DPTX_HPD_STATE				0x11
+
+#define FW_STANDBY				0
+#define FW_ACTIVE				1
+
+#define DPTX_EVENT_ENABLE_HPD			BIT(0)
+#define DPTX_EVENT_ENABLE_TRAINING		BIT(1)
+
+#define LINK_TRAINING_NOT_ACTIVE		0
+#define LINK_TRAINING_RUN			1
+#define LINK_TRAINING_RESTART			2
+
+#define CONTROL_VIDEO_IDLE			0
+#define CONTROL_VIDEO_VALID			1
+
+#define VIF_BYPASS_INTERLACE			BIT(13)
+#define INTERLACE_FMT_DET			BIT(12)
+#define INTERLACE_DTCT_WIN			0x20
+
+#define DP_FRAMER_SP_INTERLACE_EN		BIT(2)
+#define DP_FRAMER_SP_HSP			BIT(1)
+#define DP_FRAMER_SP_VSP			BIT(0)
+
+/* capability */
+#define AUX_HOST_INVERT				3
+#define	FAST_LT_SUPPORT				1
+#define FAST_LT_NOT_SUPPORT			0
+#define LANE_MAPPING_NORMAL			0xe4
+#define LANE_MAPPING_FLIPPED			0x1b
+#define ENHANCED				1
+
+#define	FULL_LT_STARTED				BIT(0)
+#define FASE_LT_STARTED				BIT(1)
+#define CLK_RECOVERY_FINISHED			BIT(2)
+#define EQ_PHASE_FINISHED			BIT(3)
+#define FASE_LT_START_FINISHED			BIT(4)
+#define CLK_RECOVERY_FAILED			BIT(5)
+#define EQ_PHASE_FAILED				BIT(6)
+#define FASE_LT_FAILED				BIT(7)
+
+#define DPTX_HPD_EVENT				BIT(0)
+#define DPTX_TRAINING_EVENT			BIT(1)
+#define HDCP_TX_STATUS_EVENT			BIT(4)
+#define HDCP2_TX_IS_KM_STORED_EVENT		BIT(5)
+#define HDCP2_TX_STORE_KM_EVENT			BIT(6)
+#define HDCP_TX_IS_RECEIVER_ID_VALID_EVENT	BIT(7)
+
+#define TU_SIZE					30
+#define CDN_DP_MAX_LINK_RATE			DP_LINK_BW_5_4
+
+/* audio */
+#define AUDIO_PACK_EN				BIT(8)
+#define SAMPLING_FREQ(x)			(((x) & 0xf) << 16)
+#define ORIGINAL_SAMP_FREQ(x)			(((x) & 0xf) << 24)
+#define SYNC_WR_TO_CH_ZERO			BIT(1)
+#define I2S_DEC_START				BIT(1)
+#define AUDIO_SW_RST				BIT(0)
+#define SMPL2PKT_EN				BIT(1)
+#define MAX_NUM_CH(x)				(((x) & 0x1f) - 1)
+#define NUM_OF_I2S_PORTS(x)			((((x) / 2 - 1) & 0x3) << 5)
+#define AUDIO_TYPE_LPCM				(2 << 7)
+#define CFG_SUB_PCKT_NUM(x)			((((x) - 1) & 0x7) << 11)
+#define AUDIO_CH_NUM(x)				((((x) - 1) & 0x1f) << 2)
+#define TRANS_SMPL_WIDTH_16			0
+#define TRANS_SMPL_WIDTH_24			BIT(11)
+#define TRANS_SMPL_WIDTH_32			(2 << 11)
+#define I2S_DEC_PORT_EN(x)			(((x) & 0xf) << 17)
+#define SPDIF_ENABLE				BIT(21)
+#define SPDIF_AVG_SEL				BIT(20)
+#define SPDIF_JITTER_BYPASS			BIT(19)
+#define SPDIF_FIFO_MID_RANGE(x)			(((x) & 0xff) << 11)
+#define SPDIF_JITTER_THRSH(x)			(((x) & 0xff) << 3)
+#define SPDIF_JITTER_AVG_WIN(x)			((x) & 0x7)
+
+/* Refernce cycles when using lane clock as refernce */
+#define LANE_REF_CYC				0x8000
+
+enum voltage_swing_level {
+	VOLTAGE_LEVEL_0,
+	VOLTAGE_LEVEL_1,
+	VOLTAGE_LEVEL_2,
+	VOLTAGE_LEVEL_3,
+};
+
+enum pre_emphasis_level {
+	PRE_EMPHASIS_LEVEL_0,
+	PRE_EMPHASIS_LEVEL_1,
+	PRE_EMPHASIS_LEVEL_2,
+	PRE_EMPHASIS_LEVEL_3,
+};
+
+enum pattern_set {
+	PTS1		= BIT(0),
+	PTS2		= BIT(1),
+	PTS3		= BIT(2),
+	PTS4		= BIT(3),
+	DP_NONE		= BIT(4)
+};
+
+enum vic_color_depth {
+	BCS_6 = 0x1,
+	BCS_8 = 0x2,
+	BCS_10 = 0x4,
+	BCS_12 = 0x8,
+	BCS_16 = 0x10,
+};
+
+enum vic_bt_type {
+	BT_601 = 0x0,
+	BT_709 = 0x1,
+};
+
+void cdn_dp_clock_reset(struct cdn_dp_device *dp);
+
+void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, u32 clk);
+int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
+			 u32 i_size, const u32 *d_mem, u32 d_size);
+int cdn_dp_set_firmware_active(struct cdn_dp_device *dp, bool enable);
+int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes);
+int cdn_dp_event_config(struct cdn_dp_device *dp);
+u32 cdn_dp_get_event(struct cdn_dp_device *dp);
+int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
+int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value);
+int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len);
+int cdn_dp_get_edid_block(void *dp, u8 *edid,
+			  unsigned int block, size_t length);
+int cdn_dp_training_start(struct cdn_dp_device *dp);
+int cdn_dp_get_training_status(struct cdn_dp_device *dp);
+int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active);
+int cdn_dp_config_video(struct cdn_dp_device *dp);
+int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio);
+int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable);
+int cdn_dp_audio_config(struct cdn_dp_device *dp, struct audio_info *audio);
+#endif /* _CDN_DP_REG_H */
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index edd7ec2..d723d8e 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -969,9 +969,11 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
 		vop_dsp_hold_valid_irq_disable(vop);
 	}
 
-	pin_pol = 0x8;
-	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1;
-	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : (1 << 1);
+	pin_pol = BIT(DCLK_INVERT);
+	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ?
+		   0 : BIT(HSYNC_POSITIVE);
+	pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ?
+		   0 : BIT(VSYNC_POSITIVE);
 	VOP_CTRL_SET(vop, pin_pol, pin_pol);
 
 	switch (s->output_type) {
@@ -991,6 +993,11 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
 		VOP_CTRL_SET(vop, mipi_pin_pol, pin_pol);
 		VOP_CTRL_SET(vop, mipi_en, 1);
 		break;
+	case DRM_MODE_CONNECTOR_DisplayPort:
+		pin_pol &= ~BIT(DCLK_INVERT);
+		VOP_CTRL_SET(vop, dp_pin_pol, pin_pol);
+		VOP_CTRL_SET(vop, dp_en, 1);
+		break;
 	default:
 		DRM_ERROR("unsupport connector_type[%d]\n", s->output_type);
 	}
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index ff4f52e..4820a8b 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -45,6 +45,7 @@ struct vop_ctrl {
 	struct vop_reg edp_en;
 	struct vop_reg hdmi_en;
 	struct vop_reg mipi_en;
+	struct vop_reg dp_en;
 	struct vop_reg out_mode;
 	struct vop_reg dither_down;
 	struct vop_reg dither_up;
@@ -53,6 +54,7 @@ struct vop_ctrl {
 	struct vop_reg hdmi_pin_pol;
 	struct vop_reg edp_pin_pol;
 	struct vop_reg mipi_pin_pol;
+	struct vop_reg dp_pin_pol;
 
 	struct vop_reg htotal_pw;
 	struct vop_reg hact_st_end;
@@ -242,6 +244,13 @@ enum scale_down_mode {
 	SCALE_DOWN_AVG = 0x1
 };
 
+enum vop_pol {
+	HSYNC_POSITIVE = 0,
+	VSYNC_POSITIVE = 1,
+	DEN_NEGATIVE   = 2,
+	DCLK_INVERT    = 3
+};
+
 #define FRAC_16_16(mult, div)    (((mult) << 16) / (div))
 #define SCL_FT_DEFAULT_FIXPOINT_SHIFT	12
 #define SCL_MAX_VSKIPLINES		4
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index 5b1ae1f..dcf172e 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -281,6 +281,7 @@ static const struct vop_data rk3288_vop = {
 static const struct vop_ctrl rk3399_ctrl_data = {
 	.standby = VOP_REG(RK3399_SYS_CTRL, 0x1, 22),
 	.gate_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 23),
+	.dp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 11),
 	.rgb_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 12),
 	.hdmi_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 13),
 	.edp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 14),
@@ -290,6 +291,7 @@ static const struct vop_ctrl rk3399_ctrl_data = {
 	.data_blank = VOP_REG(RK3399_DSP_CTRL0, 0x1, 19),
 	.out_mode = VOP_REG(RK3399_DSP_CTRL0, 0xf, 0),
 	.rgb_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16),
+	.dp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16),
 	.hdmi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 20),
 	.edp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 24),
 	.mipi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 28),
-- 
2.6.3



From hl at rock-chips.com  Fri Jul 29 00:56:54 2016
From: hl at rock-chips.com (Lin Huang)
Date: Fri, 29 Jul 2016 15:56:54 +0800
Subject: [PATCH v4 0/7] rk3399 support ddr frequency scaling 
Message-ID: <1469779021-10426-1-git-send-email-hl@rock-chips.com>

rk3399 platform have dfi controller can monitor ddr load,
and dcf controller to handle ddr register so we can get the
right ddr frequency and make ddr controller happy work(which
will implement in bl31). So we do ddr frequency scaling with
following flow:

	     kernel                                bl31

	monitor ddr load
		|
		|
	get_target_rate
		|
		|           pass rate to bl31
	clk_set_rate(ddr) --------------------->run dcf flow
		|                                   |
		|                                   |
	wait dcf interrupt<-------------------trigger dcf interrupt  
		|
		|
	      return

Lin Huang (6):
  clk: rockchip: add new clock-type for the ddrclk
  clk: rockchip: rk3399: add SCLK_DDRCLK ID for ddrc
  clk: rockchip: rk3399: add ddrc clock support
  PM / devfreq: event: support rockchip dfi controller
  PM / devfreq: rockchip: add devfreq driver for rk3399 dmc
  drm/rockchip: Add dmc notifier in vop driver


Heiko St?bner (1):
  clk: rockchip: add clock flag parameter when register pll

Lin Huang (6):
  clk: rockchip: add new clock-type for the ddrclk
  clk: rockchip: rk3399: add SCLK_DDRCLK ID for ddrc
  clk: rockchip: rk3399: add ddrc clock support
  PM / devfreq: event: support rockchip dfi controller
  PM / devfreq: rockchip: add devfreq driver for rk3399 dmc
  drm/rockchip: Add dmc notifier in vop driver

 drivers/clk/rockchip/Makefile               |   1 +
 drivers/clk/rockchip/clk-ddr.c              | 146 +++++++++
 drivers/clk/rockchip/clk-pll.c              |   4 +-
 drivers/clk/rockchip/clk-rk3399.c           |  19 ++
 drivers/clk/rockchip/clk.c                  |  11 +-
 drivers/clk/rockchip/clk.h                  |  29 +-
 drivers/devfreq/Kconfig                     |   1 +
 drivers/devfreq/Makefile                    |   1 +
 drivers/devfreq/event/Kconfig               |   7 +
 drivers/devfreq/event/Makefile              |   1 +
 drivers/devfreq/event/rockchip-dfi.c        | 253 +++++++++++++++
 drivers/devfreq/rockchip/Kconfig            |   8 +
 drivers/devfreq/rockchip/Makefile           |   1 +
 drivers/devfreq/rockchip/rk3399_dmc.c       | 473 ++++++++++++++++++++++++++++
 drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 124 +++++++-
 include/dt-bindings/clock/rk3399-cru.h      |   1 +
 include/soc/rockchip/rockchip_sip.h         |  27 ++
 17 files changed, 1098 insertions(+), 9 deletions(-)
 create mode 100644 drivers/clk/rockchip/clk-ddr.c
 create mode 100644 drivers/devfreq/event/rockchip-dfi.c
 create mode 100644 drivers/devfreq/rockchip/Kconfig
 create mode 100644 drivers/devfreq/rockchip/Makefile
 create mode 100644 drivers/devfreq/rockchip/rk3399_dmc.c
 create mode 100644 include/soc/rockchip/rockchip_sip.h

-- 
1.9.1



From hl at rock-chips.com  Fri Jul 29 00:56:55 2016
From: hl at rock-chips.com (Lin Huang)
Date: Fri, 29 Jul 2016 15:56:55 +0800
Subject: [PATCH v4 1/7] clk: rockchip: add clock flag parameter when register
 pll
In-Reply-To: <1469779021-10426-1-git-send-email-hl@rock-chips.com>
References: <1469779021-10426-1-git-send-email-hl@rock-chips.com>
Message-ID: <1469779021-10426-2-git-send-email-hl@rock-chips.com>

From: Heiko St?bner <heiko at sntech.de>

add clock flag parameter so we can pass specific clock flag
(like CLK_GET_RATE_NOCACHE etc..)to pll driver.

Signed-off-by: Heiko St?bner <heiko at sntech.de>
Signed-off-by: Lin Huang <hl at rock-chips.com>
---
Changes in v4:
- None

Changes in v3:
- None

Changes in v2:
- None

Changes in v1:
- None

 drivers/clk/rockchip/clk-pll.c | 4 ++--
 drivers/clk/rockchip/clk.c     | 2 +-
 drivers/clk/rockchip/clk.h     | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c
index 8ac73bc..d824c36 100644
--- a/drivers/clk/rockchip/clk-pll.c
+++ b/drivers/clk/rockchip/clk-pll.c
@@ -864,7 +864,7 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
 		u8 num_parents, int con_offset, int grf_lock_offset,
 		int lock_shift, int mode_offset, int mode_shift,
 		struct rockchip_pll_rate_table *rate_table,
-		u8 clk_pll_flags)
+		unsigned long flags, u8 clk_pll_flags)
 {
 	const char *pll_parents[3];
 	struct clk_init_data init;
@@ -919,7 +919,7 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
 	init.name = pll_name;
 
 	/* keep all plls untouched for now */
-	init.flags = CLK_IGNORE_UNUSED;
+	init.flags = flags | CLK_IGNORE_UNUSED;
 
 	init.parent_names = &parent_names[0];
 	init.num_parents = 1;
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
index f0a8be1..9a046f1 100644
--- a/drivers/clk/rockchip/clk.c
+++ b/drivers/clk/rockchip/clk.c
@@ -390,7 +390,7 @@ void __init rockchip_clk_register_plls(struct rockchip_clk_provider *ctx,
 				list->con_offset, grf_lock_offset,
 				list->lock_shift, list->mode_offset,
 				list->mode_shift, list->rate_table,
-				list->pll_flags);
+				list->flags, list->pll_flags);
 		if (IS_ERR(clk)) {
 			pr_err("%s: failed to register clock %s\n", __func__,
 				list->name);
diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h
index 1abb7d0..bac775d 100644
--- a/drivers/clk/rockchip/clk.h
+++ b/drivers/clk/rockchip/clk.h
@@ -238,7 +238,7 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
 		u8 num_parents, int con_offset, int grf_lock_offset,
 		int lock_shift, int mode_offset, int mode_shift,
 		struct rockchip_pll_rate_table *rate_table,
-		u8 clk_pll_flags);
+		unsigned long flags, u8 clk_pll_flags);
 
 struct rockchip_cpuclk_clksel {
 	int reg;
-- 
1.9.1



From hl at rock-chips.com  Fri Jul 29 00:56:56 2016
From: hl at rock-chips.com (Lin Huang)
Date: Fri, 29 Jul 2016 15:56:56 +0800
Subject: [PATCH v4 2/7] clk: rockchip: add new clock-type for the ddrclk
In-Reply-To: <1469779021-10426-1-git-send-email-hl@rock-chips.com>
References: <1469779021-10426-1-git-send-email-hl@rock-chips.com>
Message-ID: <1469779021-10426-3-git-send-email-hl@rock-chips.com>

On new rockchip platform(rk3399 etc), there have dcf controller to
do ddr frequency scaling, and this controller will implement in
arm-trust-firmware. We add a special clock-type to handle that.

Signed-off-by: Lin Huang <hl at rock-chips.com>
---
Changes in v4:
- use arm_smccc_smc() to set/read ddr rate

Changes in v3:
- use sip call to set/read ddr rate

Changes in v2:
- use GENMASK instead val_mask
- use divider_recalc_rate() instead DIV_ROUND_UP_ULL
- cleanup code

Changes in v1:
- None

 drivers/clk/rockchip/Makefile       |   1 +
 drivers/clk/rockchip/clk-ddr.c      | 146 ++++++++++++++++++++++++++++++++++++
 drivers/clk/rockchip/clk.c          |   9 +++
 drivers/clk/rockchip/clk.h          |  27 +++++++
 include/soc/rockchip/rockchip_sip.h |  27 +++++++
 5 files changed, 210 insertions(+)
 create mode 100644 drivers/clk/rockchip/clk-ddr.c
 create mode 100644 include/soc/rockchip/rockchip_sip.h

diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile
index f47a2fa..b5f2c8e 100644
--- a/drivers/clk/rockchip/Makefile
+++ b/drivers/clk/rockchip/Makefile
@@ -8,6 +8,7 @@ obj-y	+= clk-pll.o
 obj-y	+= clk-cpu.o
 obj-y	+= clk-inverter.o
 obj-y	+= clk-mmc-phase.o
+obj-y	+= clk-ddr.o
 obj-$(CONFIG_RESET_CONTROLLER)	+= softrst.o
 
 obj-y	+= clk-rk3036.o
diff --git a/drivers/clk/rockchip/clk-ddr.c b/drivers/clk/rockchip/clk-ddr.c
new file mode 100644
index 0000000..dd657c6
--- /dev/null
+++ b/drivers/clk/rockchip/clk-ddr.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2016 Rockchip Electronics Co. Ltd.
+ * Author: Lin Huang <hl at rock-chips.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/arm-smccc.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <soc/rockchip/rockchip_sip.h>
+
+#include "clk.h"
+
+struct rockchip_ddrclk {
+	struct clk_hw	hw;
+	void __iomem	*reg_base;
+	int		mux_offset;
+	int		mux_shift;
+	int		mux_width;
+	int		mux_flag;
+	int		div_shift;
+	int		div_width;
+	int		div_flag;
+	spinlock_t	*lock;
+};
+
+#define to_rockchip_ddrclk_hw(hw) container_of(hw, struct rockchip_ddrclk, hw)
+
+static int rockchip_ddrclk_set_rate(struct clk_hw *hw, unsigned long drate,
+				    unsigned long prate)
+{
+	struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw);
+	unsigned long flags;
+	struct arm_smccc_res res;
+
+	spin_lock_irqsave(ddrclk->lock, flags);
+	arm_smccc_smc(SIP_DDR_FREQ, drate, 0, CONFIG_DRAM_SET_RATE,
+		      0, 0, 0, 0, &res);
+	spin_unlock_irqrestore(ddrclk->lock, flags);
+
+	return res.a0;
+}
+
+static unsigned long
+rockchip_ddrclk_recalc_rate(struct clk_hw *hw,
+			    unsigned long parent_rate)
+{
+	struct arm_smccc_res res;
+
+	arm_smccc_smc(SIP_DDR_FREQ, 0, 0, CONFIG_DRAM_GET_RATE,
+		      0, 0, 0, 0, &res);
+
+	return res.a0;
+}
+
+static long clk_ddrclk_round_rate(struct clk_hw *hw, unsigned long rate,
+				  unsigned long *prate)
+{
+	return rate;
+}
+
+static u8 rockchip_ddrclk_get_parent(struct clk_hw *hw)
+{
+	struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw);
+	int num_parents = clk_hw_get_num_parents(hw);
+	u32 val;
+
+	val = clk_readl(ddrclk->reg_base +
+			ddrclk->mux_offset) >> ddrclk->mux_shift;
+	val &= GENMASK(ddrclk->mux_width - 1, 0);
+
+	if (val >= num_parents)
+		return -EINVAL;
+
+	return val;
+}
+
+static const struct clk_ops rockchip_ddrclk_ops = {
+	.recalc_rate = rockchip_ddrclk_recalc_rate,
+	.set_rate = rockchip_ddrclk_set_rate,
+	.round_rate = clk_ddrclk_round_rate,
+	.get_parent = rockchip_ddrclk_get_parent,
+};
+
+struct clk *rockchip_clk_register_ddrclk(const char *name, int flags,
+					 const char *const *parent_names,
+					 u8 num_parents, int mux_offset,
+					 int mux_shift, int mux_width,
+					 int mux_flag, int div_shift,
+					 int div_width, int div_flag,
+					 void __iomem *reg_base,
+					 spinlock_t *lock)
+{
+	struct rockchip_ddrclk *ddrclk;
+	struct clk_init_data init;
+	struct clk *clk;
+
+	ddrclk = kzalloc(sizeof(*ddrclk), GFP_KERNEL);
+	if (!ddrclk)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	init.parent_names = parent_names;
+	init.num_parents = num_parents;
+	init.ops = &rockchip_ddrclk_ops;
+
+	init.flags = flags;
+	init.flags |= CLK_SET_RATE_NO_REPARENT;
+	init.flags |= CLK_GET_RATE_NOCACHE;
+
+	ddrclk->reg_base = reg_base;
+	ddrclk->lock = lock;
+	ddrclk->hw.init = &init;
+	ddrclk->mux_offset = mux_offset;
+	ddrclk->mux_shift = mux_shift;
+	ddrclk->mux_width = mux_width;
+	ddrclk->mux_flag = mux_flag;
+	ddrclk->div_shift = div_shift;
+	ddrclk->div_width = div_width;
+	ddrclk->div_flag = div_flag;
+
+	clk = clk_register(NULL, &ddrclk->hw);
+	if (IS_ERR(clk)) {
+		pr_err("%s: could not register ddrclk %s\n", __func__,	name);
+		goto free_ddrclk;
+	}
+
+	return clk;
+
+free_ddrclk:
+	kfree(ddrclk);
+
+	return NULL;
+}
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
index 9a046f1..0369645 100644
--- a/drivers/clk/rockchip/clk.c
+++ b/drivers/clk/rockchip/clk.c
@@ -489,6 +489,15 @@ void __init rockchip_clk_register_branches(
 				list->gate_offset, list->gate_shift,
 				list->gate_flags, flags, &ctx->lock);
 			break;
+		case branch_ddrc:
+			clk = rockchip_clk_register_ddrclk(
+				list->name, list->flags,
+				list->parent_names, list->num_parents,
+				list->muxdiv_offset, list->mux_shift,
+				list->mux_width, list->mux_flags,
+				list->div_shift, list->div_width,
+				list->div_flags, ctx->reg_base, &ctx->lock);
+			break;
 		}
 
 		/* none of the cases above matched */
diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h
index bac775d..2e3bccf 100644
--- a/drivers/clk/rockchip/clk.h
+++ b/drivers/clk/rockchip/clk.h
@@ -281,6 +281,13 @@ struct clk *rockchip_clk_register_mmc(const char *name,
 				const char *const *parent_names, u8 num_parents,
 				void __iomem *reg, int shift);
 
+struct clk *rockchip_clk_register_ddrclk(const char *name, int flags,
+			const char *const *parent_names, u8 num_parents,
+			int mux_offset, int mux_shift, int mux_width,
+			int mux_flag, int div_shift, int div_width,
+			int div_flag, void __iomem *reg_base,
+			spinlock_t *lock);
+
 #define ROCKCHIP_INVERTER_HIWORD_MASK	BIT(0)
 
 struct clk *rockchip_clk_register_inverter(const char *name,
@@ -299,6 +306,7 @@ enum rockchip_clk_branch_type {
 	branch_mmc,
 	branch_inverter,
 	branch_factor,
+	branch_ddrc,
 };
 
 struct rockchip_clk_branch {
@@ -488,6 +496,25 @@ struct rockchip_clk_branch {
 		.child		= ch,				\
 	}
 
+#define COMPOSITE_DDRC(_id, cname, pnames, f, mo, ms, mw, mf,	\
+			 ds, dw, df)				\
+	{							\
+		.id		= _id,				\
+		.branch_type	= branch_ddrc,			\
+		.name		= cname,			\
+		.parent_names	= pnames,			\
+		.num_parents	= ARRAY_SIZE(pnames),		\
+		.flags		= f,				\
+		.muxdiv_offset	= mo,				\
+		.mux_shift	= ms,				\
+		.mux_width	= mw,				\
+		.mux_flags	= mf,				\
+		.div_shift	= ds,				\
+		.div_width	= dw,				\
+		.div_flags	= df,				\
+		.gate_offset	= -1,				\
+	}
+
 #define MUX(_id, cname, pnames, f, o, s, w, mf)			\
 	{							\
 		.id		= _id,				\
diff --git a/include/soc/rockchip/rockchip_sip.h b/include/soc/rockchip/rockchip_sip.h
new file mode 100644
index 0000000..1fa7389
--- /dev/null
+++ b/include/soc/rockchip/rockchip_sip.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
+ * Author: Lin Huang <hl at rock-chips.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ */
+#ifndef __SOC_ROCKCHIP_SIP_H
+#define __SOC_ROCKCHIP_SIP_H
+
+#define SIP_DDR_FREQ		0xC2000008
+#define CONFIG_DRAM_INIT	0x00
+#define CONFIG_DRAM_SET_RATE	0x01
+#define CONFIG_DRAM_ROUND_RATE	0x02
+#define CONFIG_DRAM_SET_AT_SR	0x03
+#define CONFIG_DRAM_GET_BW	0x04
+#define CONFIG_DRAM_GET_RATE	0x05
+#define CONFIG_DRAM_CLR_IRQ	0x06
+#define CONFIG_DRAM_SET_PARAM	0x07
+
+#endif
-- 
1.9.1



From hl at rock-chips.com  Fri Jul 29 00:56:57 2016
From: hl at rock-chips.com (Lin Huang)
Date: Fri, 29 Jul 2016 15:56:57 +0800
Subject: [PATCH v4 3/7] clk: rockchip: rk3399: add SCLK_DDRCLK ID for ddrc
In-Reply-To: <1469779021-10426-1-git-send-email-hl@rock-chips.com>
References: <1469779021-10426-1-git-send-email-hl@rock-chips.com>
Message-ID: <1469779021-10426-4-git-send-email-hl@rock-chips.com>

Signed-off-by: Lin Huang <hl at rock-chips.com>
---
Changes in v4:
-None

Changes in v3:
-None

Changes in v2:
- None 
Changes in v1:
- None

 include/dt-bindings/clock/rk3399-cru.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/include/dt-bindings/clock/rk3399-cru.h b/include/dt-bindings/clock/rk3399-cru.h
index 50a44cf..8a0f0442 100644
--- a/include/dt-bindings/clock/rk3399-cru.h
+++ b/include/dt-bindings/clock/rk3399-cru.h
@@ -131,6 +131,7 @@
 #define SCLK_DPHY_RX0_CFG		165
 #define SCLK_RMII_SRC			166
 #define SCLK_PCIEPHY_REF100M		167
+#define SCLK_DDRCLK			168
 
 #define DCLK_VOP0			180
 #define DCLK_VOP1			181
-- 
1.9.1



From hl at rock-chips.com  Fri Jul 29 00:56:58 2016
From: hl at rock-chips.com (Lin Huang)
Date: Fri, 29 Jul 2016 15:56:58 +0800
Subject: [PATCH v4 4/7] clk: rockchip: rk3399: add ddrc clock support
In-Reply-To: <1469779021-10426-1-git-send-email-hl@rock-chips.com>
References: <1469779021-10426-1-git-send-email-hl@rock-chips.com>
Message-ID: <1469779021-10426-5-git-send-email-hl@rock-chips.com>

add ddrc clock setting, so we can do ddr frequency
scaling on rk3399 platform in future.

Signed-off-by: Lin Huang <hl at rock-chips.com>
---
Changes in v4:
- None

Changes in v3:
- None

Changes in v2:
- remove clk_ddrc_dpll_src from critical clock list

Changes in v1:
- remove ddrc source CLK_IGNORE_UNUSED flag
- move clk_ddrc and clk_ddrc_dpll_src to critical

 drivers/clk/rockchip/clk-rk3399.c | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c
index d4a1cf0..b7b42d9 100644
--- a/drivers/clk/rockchip/clk-rk3399.c
+++ b/drivers/clk/rockchip/clk-rk3399.c
@@ -118,6 +118,10 @@ PNAME(mux_armclkb_p)				= { "clk_core_b_lpll_src",
 						    "clk_core_b_bpll_src",
 						    "clk_core_b_dpll_src",
 						    "clk_core_b_gpll_src" };
+PNAME(mux_ddrclk_p)				= { "clk_ddrc_lpll_src",
+						    "clk_ddrc_bpll_src",
+						    "clk_ddrc_dpll_src",
+						    "clk_ddrc_gpll_src" };
 PNAME(mux_aclk_cci_p)				= { "cpll_aclk_cci_src",
 						    "gpll_aclk_cci_src",
 						    "npll_aclk_cci_src",
@@ -1377,6 +1381,18 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = {
 	COMPOSITE_NOMUX(0, "clk_test", "clk_test_pre", CLK_IGNORE_UNUSED,
 			RK3368_CLKSEL_CON(58), 0, 5, DFLAGS,
 			RK3368_CLKGATE_CON(13), 11, GFLAGS),
+
+	/* ddrc */
+	GATE(0, "clk_ddrc_lpll_src", "lpll", 0, RK3399_CLKGATE_CON(3),
+	     0, GFLAGS),
+	GATE(0, "clk_ddrc_bpll_src", "bpll", 0, RK3399_CLKGATE_CON(3),
+	     1, GFLAGS),
+	GATE(0, "clk_ddrc_dpll_src", "dpll", 0, RK3399_CLKGATE_CON(3),
+	     2, GFLAGS),
+	GATE(0, "clk_ddrc_gpll_src", "gpll", 0, RK3399_CLKGATE_CON(3),
+	     3, GFLAGS),
+	COMPOSITE_DDRC(SCLK_DDRCLK, "clk_ddrc", mux_ddrclk_p, 0,
+		       RK3399_CLKSEL_CON(6), 4, 2, MFLAGS, 0, 3, DFLAGS),
 };
 
 static struct rockchip_clk_branch rk3399_clk_pmu_branches[] __initdata = {
@@ -1487,6 +1503,9 @@ static const char *const rk3399_cru_critical_clocks[] __initconst = {
 	"gpll_hclk_perilp1_src",
 	"gpll_aclk_perilp0_src",
 	"gpll_aclk_perihp_src",
+
+	/* ddrc */
+	"clk_ddrc"
 };
 
 static const char *const rk3399_pmucru_critical_clocks[] __initconst = {
-- 
1.9.1



From hl at rock-chips.com  Fri Jul 29 00:56:59 2016
From: hl at rock-chips.com (Lin Huang)
Date: Fri, 29 Jul 2016 15:56:59 +0800
Subject: [PATCH v4 5/7] PM / devfreq: event: support rockchip dfi controller
In-Reply-To: <1469779021-10426-1-git-send-email-hl@rock-chips.com>
References: <1469779021-10426-1-git-send-email-hl@rock-chips.com>
Message-ID: <1469779021-10426-6-git-send-email-hl@rock-chips.com>

on rk3399 platform, there is dfi conroller can monitor
ddr load, base on this result, we can do ddr freqency
scaling.

Signed-off-by: Lin Huang <hl at rock-chips.com>
Acked-by: Chanwoo Choi <cw00.choi at samsung.com>
---
Changes in v4:
- None

Changes in v3:
- None

Changes in v2:
- use clk_disable_unprepare and clk_enable_prepare
- remove clk_enable_prepare in probe
- remove rockchip_dfi_remove function

Changes in v1:
- None

 drivers/devfreq/event/Kconfig        |   7 +
 drivers/devfreq/event/Makefile       |   1 +
 drivers/devfreq/event/rockchip-dfi.c | 253 +++++++++++++++++++++++++++++++++++
 3 files changed, 261 insertions(+)
 create mode 100644 drivers/devfreq/event/rockchip-dfi.c

diff --git a/drivers/devfreq/event/Kconfig b/drivers/devfreq/event/Kconfig
index a11720a..ff9279f 100644
--- a/drivers/devfreq/event/Kconfig
+++ b/drivers/devfreq/event/Kconfig
@@ -22,4 +22,11 @@ config DEVFREQ_EVENT_EXYNOS_PPMU
 	  (Platform Performance Monitoring Unit) counters to estimate the
 	  utilization of each module.
 
+config DEVFREQ_EVENT_ROCKCHIP_DFI
+	tristate "ROCKCHIP DFI DEVFREQ event Driver"
+	depends on ARCH_ROCKCHIP
+	help
+	  This add the devfreq-event driver for Rockchip SoC. It provides DFI
+	  (DDR Monitor Module) driver to count ddr load.
+
 endif # PM_DEVFREQ_EVENT
diff --git a/drivers/devfreq/event/Makefile b/drivers/devfreq/event/Makefile
index be146ea..e3f88fc 100644
--- a/drivers/devfreq/event/Makefile
+++ b/drivers/devfreq/event/Makefile
@@ -1,2 +1,3 @@
 # Exynos DEVFREQ Event Drivers
 obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_PPMU) += exynos-ppmu.o
+obj-$(CONFIG_DEVFREQ_EVENT_ROCKCHIP_DFI) += rockchip-dfi.o
diff --git a/drivers/devfreq/event/rockchip-dfi.c b/drivers/devfreq/event/rockchip-dfi.c
new file mode 100644
index 0000000..96a0307
--- /dev/null
+++ b/drivers/devfreq/event/rockchip-dfi.c
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
+ * Author: Lin Huang <hl at rock-chips.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/clk.h>
+#include <linux/devfreq-event.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/of.h>
+
+#define RK3399_DMC_NUM_CH	2
+
+/* DDRMON_CTRL */
+#define DDRMON_CTRL	0x04
+#define CLR_DDRMON_CTRL	(0x1f0000 << 0)
+#define LPDDR4_EN	(0x10001 << 4)
+#define HARDWARE_EN	(0x10001 << 3)
+#define LPDDR3_EN	(0x10001 << 2)
+#define SOFTWARE_EN	(0x10001 << 1)
+#define TIME_CNT_EN	(0x10001 << 0)
+
+#define DDRMON_CH0_COUNT_NUM		0x28
+#define DDRMON_CH0_DFI_ACCESS_NUM	0x2c
+#define DDRMON_CH1_COUNT_NUM		0x3c
+#define DDRMON_CH1_DFI_ACCESS_NUM	0x40
+
+/* pmu grf */
+#define PMUGRF_OS_REG2	0x308
+#define DDRTYPE_SHIFT	13
+#define DDRTYPE_MASK	7
+
+enum {
+	DDR3 = 3,
+	LPDDR3 = 6,
+	LPDDR4 = 7,
+	UNUSED = 0xFF
+};
+
+struct dmc_usage {
+	u32 access;
+	u32 total;
+};
+
+struct rockchip_dfi {
+	struct devfreq_event_dev *edev;
+	struct devfreq_event_desc *desc;
+	struct dmc_usage ch_usage[RK3399_DMC_NUM_CH];
+	struct device *dev;
+	void __iomem *regs;
+	struct regmap *regmap_pmu;
+	struct clk *clk;
+};
+
+static void rockchip_dfi_start_hardware_counter(struct devfreq_event_dev *edev)
+{
+	struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
+	void __iomem *dfi_regs = info->regs;
+	u32 val;
+	u32 ddr_type;
+
+	/* get ddr type */
+	regmap_read(info->regmap_pmu, PMUGRF_OS_REG2, &val);
+	ddr_type = (val >> DDRTYPE_SHIFT) & DDRTYPE_MASK;
+
+	/* clear DDRMON_CTRL setting */
+	writel_relaxed(CLR_DDRMON_CTRL, dfi_regs + DDRMON_CTRL);
+
+	/* set ddr type to dfi */
+	if (ddr_type == LPDDR3)
+		writel_relaxed(LPDDR3_EN, dfi_regs + DDRMON_CTRL);
+	else if (ddr_type == LPDDR4)
+		writel_relaxed(LPDDR4_EN, dfi_regs + DDRMON_CTRL);
+
+	/* enable count, use software mode */
+	writel_relaxed(SOFTWARE_EN, dfi_regs + DDRMON_CTRL);
+}
+
+static void rockchip_dfi_stop_hardware_counter(struct devfreq_event_dev *edev)
+{
+	struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
+	void __iomem *dfi_regs = info->regs;
+	u32 val;
+
+	val = readl_relaxed(dfi_regs + DDRMON_CTRL);
+	val &= ~SOFTWARE_EN;
+	writel_relaxed(val, dfi_regs + DDRMON_CTRL);
+}
+
+static int rockchip_dfi_get_busier_ch(struct devfreq_event_dev *edev)
+{
+	struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
+	u32 tmp, max = 0;
+	u32 i, busier_ch = 0;
+	void __iomem *dfi_regs = info->regs;
+
+	rockchip_dfi_stop_hardware_counter(edev);
+
+	/* Find out which channel is busier */
+	for (i = 0; i < RK3399_DMC_NUM_CH; i++) {
+		info->ch_usage[i].access = readl_relaxed(dfi_regs +
+				DDRMON_CH0_DFI_ACCESS_NUM + i * 20);
+		info->ch_usage[i].total = readl_relaxed(dfi_regs +
+				DDRMON_CH0_COUNT_NUM + i * 20);
+		tmp = info->ch_usage[i].access;
+		if (tmp > max) {
+			busier_ch = i;
+			max = tmp;
+		}
+	}
+	rockchip_dfi_start_hardware_counter(edev);
+
+	return busier_ch;
+}
+
+static int rockchip_dfi_disable(struct devfreq_event_dev *edev)
+{
+	struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
+
+	rockchip_dfi_stop_hardware_counter(edev);
+	clk_disable_unprepare(info->clk);
+
+	return 0;
+}
+
+static int rockchip_dfi_enable(struct devfreq_event_dev *edev)
+{
+	struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
+	int ret;
+
+	ret = clk_prepare_enable(info->clk);
+	if (ret) {
+		dev_err(&edev->dev, "failed to enable dfi clk: %d\n", ret);
+		return ret;
+	}
+
+	rockchip_dfi_start_hardware_counter(edev);
+	return 0;
+}
+
+static int rockchip_dfi_set_event(struct devfreq_event_dev *edev)
+{
+	return 0;
+}
+
+static int rockchip_dfi_get_event(struct devfreq_event_dev *edev,
+				  struct devfreq_event_data *edata)
+{
+	struct rockchip_dfi *info = devfreq_event_get_drvdata(edev);
+	int busier_ch;
+
+	busier_ch = rockchip_dfi_get_busier_ch(edev);
+
+	edata->load_count = info->ch_usage[busier_ch].access;
+	edata->total_count = info->ch_usage[busier_ch].total;
+
+	return 0;
+}
+
+static const struct devfreq_event_ops rockchip_dfi_ops = {
+	.disable = rockchip_dfi_disable,
+	.enable = rockchip_dfi_enable,
+	.get_event = rockchip_dfi_get_event,
+	.set_event = rockchip_dfi_set_event,
+};
+
+static const struct of_device_id rockchip_dfi_id_match[] = {
+	{ .compatible = "rockchip,rk3399-dfi" },
+	{ },
+};
+
+static int rockchip_dfi_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct rockchip_dfi *data;
+	struct resource *res;
+	struct devfreq_event_desc *desc;
+	struct device_node *np = pdev->dev.of_node, *node;
+
+	data = devm_kzalloc(dev, sizeof(struct rockchip_dfi), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	data->regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(data->regs))
+		return PTR_ERR(data->regs);
+
+	data->clk = devm_clk_get(dev, "pclk_ddr_mon");
+	if (IS_ERR(data->clk)) {
+		dev_err(dev, "Cannot get the clk dmc_clk\n");
+		return PTR_ERR(data->clk);
+	};
+
+	/* try to find the optional reference to the pmu syscon */
+	node = of_parse_phandle(np, "rockchip,pmu", 0);
+	if (node) {
+		data->regmap_pmu = syscon_node_to_regmap(node);
+		if (IS_ERR(data->regmap_pmu))
+			return PTR_ERR(data->regmap_pmu);
+	}
+	data->dev = dev;
+
+	desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
+	if (!desc)
+		return -ENOMEM;
+
+	desc->ops = &rockchip_dfi_ops;
+	desc->driver_data = data;
+	desc->name = np->name;
+	data->desc = desc;
+
+	data->edev = devm_devfreq_event_add_edev(&pdev->dev, desc);
+	if (IS_ERR(data->edev)) {
+		dev_err(&pdev->dev,
+			"failed to add devfreq-event device\n");
+		return PTR_ERR(data->edev);
+	}
+
+	platform_set_drvdata(pdev, data);
+
+	return 0;
+}
+
+static struct platform_driver rockchip_dfi_driver = {
+	.probe	= rockchip_dfi_probe,
+	.driver = {
+		.name	= "rockchip-dfi",
+		.of_match_table = rockchip_dfi_id_match,
+	},
+};
+module_platform_driver(rockchip_dfi_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Lin Huang <hl at rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip dfi driver");
-- 
1.9.1



From hl at rock-chips.com  Fri Jul 29 00:57:01 2016
From: hl at rock-chips.com (Lin Huang)
Date: Fri, 29 Jul 2016 15:57:01 +0800
Subject: [PATCH v4 7/7] drm/rockchip: Add dmc notifier in vop driver
In-Reply-To: <1469779021-10426-1-git-send-email-hl@rock-chips.com>
References: <1469779021-10426-1-git-send-email-hl@rock-chips.com>
Message-ID: <1469779021-10426-8-git-send-email-hl@rock-chips.com>

when in ddr frequency scaling process, vop can not do
enable or disable operation, since dcf will base on vop vblank
time to do frequency scaling and need to get vop irq if there
have vop enabled. So need register to devfreq notifier, and we can
get the dmc status. Also, when there have two vop enabled, we need
to disable dmc, since dcf only base on one vop vblank time, so the
other panel will flicker when do ddr frequency scaling.

Signed-off-by: Lin Huang <hl at rock-chips.com>
---
Changes in v4:
- register notifier to devfreq_register_notifier
- use DEVFREQ_PRECHANGE and DEVFREQ_POSTCHANGE to get dmc status
- when two vop enable, disable dmc
- when two vop back to one vop, enable dmc

Changes in v3:
- when do vop eanble/disable, dmc will wait until it finish

Changes in v2:
- None

Changes in v1:
- use wait_event instead usleep

 drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 124 ++++++++++++++++++++++++++--
 1 file changed, 119 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index c179933..3b251d1 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -12,6 +12,8 @@
  * GNU General Public License for more details.
  */
 
+#include <linux/devfreq.h>
+#include <linux/devfreq-event.h>
 #include <drm/drm.h>
 #include <drm/drmP.h>
 #include <drm/drm_atomic.h>
@@ -127,6 +129,13 @@ struct vop {
 
 	const struct vop_data *data;
 
+	struct devfreq *devfreq;
+	struct devfreq_event_dev *devfreq_event_dev;
+	struct notifier_block dmc_nb;
+	int dmc_in_process;
+	int vop_switch_status;
+	wait_queue_head_t wait_dmc_queue;
+	wait_queue_head_t wait_vop_switch_queue;
 	uint32_t *regsbak;
 	void __iomem *regs;
 
@@ -500,24 +509,60 @@ static void vop_line_flag_irq_disable(struct vop *vop)
 	spin_unlock_irqrestore(&vop->irq_lock, flags);
 }
 
+static int dmc_notify(struct notifier_block *nb, unsigned long event,
+		      void *data)
+{
+	struct vop *vop = container_of(nb, struct vop, dmc_nb);
+
+	if (event == DEVFREQ_PRECHANGE) {
+
+		/*
+		 * check if vop in enable or disable process,
+		 * if yes, wait until it finish, use 200ms as
+		 * timeout.
+		 */
+		if (!wait_event_timeout(vop->wait_vop_switch_queue,
+					!vop->vop_switch_status, HZ / 5))
+			dev_warn(vop->dev,
+				 "Timeout waiting for vop swtich status\n");
+		vop->dmc_in_process = 1;
+	} else if (event == DEVFREQ_POSTCHANGE) {
+		vop->dmc_in_process = 0;
+		wake_up(&vop->wait_dmc_queue);
+	}
+
+	return NOTIFY_OK;
+}
+
 static void vop_enable(struct drm_crtc *crtc)
 {
 	struct vop *vop = to_vop(crtc);
+	int num_enabled_crtc = 0;
 	int ret;
 
 	if (vop->is_enabled)
 		return;
 
+	/*
+	 * if in dmc scaling frequency process, wait until it finishes
+	 * use 100ms as timeout time.
+	 */
+	if (!wait_event_timeout(vop->wait_dmc_queue,
+				!vop->dmc_in_process, HZ / 5))
+		dev_warn(vop->dev,
+			 "Timeout waiting for dmc when vop enable\n");
+
+	vop->vop_switch_status = 1;
 	ret = pm_runtime_get_sync(vop->dev);
 	if (ret < 0) {
 		dev_err(vop->dev, "failed to get pm runtime: %d\n", ret);
-		return;
+		goto err;
 	}
 
 	ret = clk_enable(vop->hclk);
 	if (ret < 0) {
 		dev_err(vop->dev, "failed to enable hclk - %d\n", ret);
-		return;
+		goto err;
 	}
 
 	ret = clk_enable(vop->dclk);
@@ -531,7 +576,6 @@ static void vop_enable(struct drm_crtc *crtc)
 		dev_err(vop->dev, "failed to enable aclk - %d\n", ret);
 		goto err_disable_dclk;
 	}
-
 	/*
 	 * Slave iommu shares power, irq and clock with vop.  It was associated
 	 * automatically with this master device via common driver code.
@@ -560,6 +604,21 @@ static void vop_enable(struct drm_crtc *crtc)
 
 	drm_crtc_vblank_on(crtc);
 
+	vop->vop_switch_status = 0;
+	wake_up(&vop->wait_vop_switch_queue);
+
+	/* check how many vop we use now */
+	drm_for_each_crtc(crtc, vop->drm_dev) {
+		if (crtc->state->enable)
+			num_enabled_crtc++;
+	}
+
+	/* if enable two vop, need to disable dmc */
+	if ((num_enabled_crtc > 1) && vop->devfreq) {
+		if (vop->devfreq_event_dev)
+			devfreq_event_disable_edev(vop->devfreq_event_dev);
+		devfreq_suspend_device(vop->devfreq);
+	}
 	return;
 
 err_disable_aclk:
@@ -568,17 +627,33 @@ err_disable_dclk:
 	clk_disable(vop->dclk);
 err_disable_hclk:
 	clk_disable(vop->hclk);
+err:
+	vop->vop_switch_status = 0;
+	wake_up(&vop->wait_vop_switch_queue);
+	return;
 }
 
 static void vop_crtc_disable(struct drm_crtc *crtc)
 {
 	struct vop *vop = to_vop(crtc);
+	int num_enabled_crtc = 0;
 	int i;
 
 	if (!vop->is_enabled)
 		return;
 
 	/*
+	 * if in dmc scaling frequency process, wait until it finish
+	 * use 100ms as timeout time.
+	 */
+	if (!wait_event_timeout(vop->wait_dmc_queue,
+				!vop->dmc_in_process, HZ / 5))
+		dev_warn(vop->dev,
+			 "Timeout waiting for dmc when vop disable\n");
+
+	vop->vop_switch_status = 1;
+
+	/*
 	 * We need to make sure that all windows are disabled before we
 	 * disable that crtc. Otherwise we might try to scan from a destroyed
 	 * buffer later.
@@ -591,7 +666,6 @@ static void vop_crtc_disable(struct drm_crtc *crtc)
 		VOP_WIN_SET(vop, win, enable, 0);
 		spin_unlock(&vop->reg_lock);
 	}
-
 	drm_crtc_vblank_off(crtc);
 
 	/*
@@ -622,7 +696,6 @@ static void vop_crtc_disable(struct drm_crtc *crtc)
 	 * vop standby complete, so iommu detach is safe.
 	 */
 	rockchip_drm_dma_detach_device(vop->drm_dev, vop->dev);
-
 	clk_disable(vop->dclk);
 	clk_disable(vop->aclk);
 	clk_disable(vop->hclk);
@@ -632,6 +705,25 @@ static void vop_crtc_disable(struct drm_crtc *crtc)
 		vop->psr_enabled = false;
 		schedule_delayed_work(&vop->psr_work, VOP_PSR_SET_DELAY_TIME);
 	}
+
+	vop->vop_switch_status = 0;
+	wake_up(&vop->wait_vop_switch_queue);
+
+	/* check how many vop use now */
+	drm_for_each_crtc(crtc, vop->drm_dev) {
+		if (crtc->state->enable)
+			num_enabled_crtc++;
+	}
+
+	/*
+	 * if num_enabled_crtc = 1 now, it means 2 vop enabled
+	 * change to 1 vop enabled  need to enable dmc again.
+	 */
+	if ((num_enabled_crtc == 1) && vop->devfreq) {
+		if (vop->devfreq_event_dev)
+			devfreq_event_enable_edev(vop->devfreq_event_dev);
+		devfreq_resume_device(vop->devfreq);
+	}
 }
 
 static void vop_plane_destroy(struct drm_plane *plane)
@@ -1583,6 +1675,8 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
 	struct drm_device *drm_dev = data;
 	struct vop *vop;
 	struct resource *res;
+	struct devfreq *devfreq;
+	struct devfreq_event_dev *event_dev;
 	size_t alloc_size;
 	int ret, irq;
 
@@ -1648,6 +1742,26 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
 	vop->psr_enabled = false;
 	INIT_DELAYED_WORK(&vop->psr_work, vop_psr_work);
 
+	init_waitqueue_head(&vop->wait_vop_switch_queue);
+	vop->vop_switch_status = 0;
+	init_waitqueue_head(&vop->wait_dmc_queue);
+	vop->dmc_in_process = 0;
+
+	devfreq = devfreq_get_devfreq_by_phandle(dev, 0);
+	if (IS_ERR(devfreq))
+		goto out;
+
+	vop->devfreq = devfreq;
+	vop->dmc_nb.notifier_call = dmc_notify;
+	devfreq_register_notifier(vop->devfreq, &vop->dmc_nb,
+				  DEVFREQ_TRANSITION_NOTIFIER);
+
+	event_dev = devfreq_event_get_edev_by_phandle(vop->devfreq->dev.parent,
+						      0);
+	if (IS_ERR(event_dev))
+		goto out;
+	vop->devfreq_event_dev = event_dev;
+out:
 	return 0;
 }
 
-- 
1.9.1



From hl at rock-chips.com  Fri Jul 29 00:57:00 2016
From: hl at rock-chips.com (Lin Huang)
Date: Fri, 29 Jul 2016 15:57:00 +0800
Subject: [PATCH v4 6/7] PM / devfreq: rockchip: add devfreq driver for rk3399
 dmc
In-Reply-To: <1469779021-10426-1-git-send-email-hl@rock-chips.com>
References: <1469779021-10426-1-git-send-email-hl@rock-chips.com>
Message-ID: <1469779021-10426-7-git-send-email-hl@rock-chips.com>

base on dfi result, we do ddr frequency scaling, register
dmc driver to devfreq framework, and use simple-ondemand
policy.

Signed-off-by: Lin Huang <hl at rock-chips.com>
---
Changes in v4:
- use arm_smccc_smc() function talk to bl31
- delete rockchip_dmc.c file and config
- delete dmc_notify
- adjust probe order
 
Changes in v3:
- operate dram setting through sip call
- imporve set rate flow

Changes in v2:
- None
 
Changes in v1:
- move dfi controller to event
- fix set voltage sequence when set rate fail
- change Kconfig type from tristate to bool
- move unuse EXPORT_SYMBOL_GPL()

 drivers/devfreq/Kconfig               |   1 +
 drivers/devfreq/Makefile              |   1 +
 drivers/devfreq/rockchip/Kconfig      |   8 +
 drivers/devfreq/rockchip/Makefile     |   1 +
 drivers/devfreq/rockchip/rk3399_dmc.c | 473 ++++++++++++++++++++++++++++++++++
 5 files changed, 484 insertions(+)
 create mode 100644 drivers/devfreq/rockchip/Kconfig
 create mode 100644 drivers/devfreq/rockchip/Makefile
 create mode 100644 drivers/devfreq/rockchip/rk3399_dmc.c

diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig
index 64281bb..acb2a57 100644
--- a/drivers/devfreq/Kconfig
+++ b/drivers/devfreq/Kconfig
@@ -99,5 +99,6 @@ config ARM_TEGRA_DEVFREQ
          operating frequencies and voltages with OPP support.
 
 source "drivers/devfreq/event/Kconfig"
+source "drivers/devfreq/rockchip/Kconfig"
 
 endif # PM_DEVFREQ
diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile
index 5134f9e..d844e23 100644
--- a/drivers/devfreq/Makefile
+++ b/drivers/devfreq/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_DEVFREQ_GOV_USERSPACE)	+= governor_userspace.o
 obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ)	+= exynos/
 obj-$(CONFIG_ARM_EXYNOS5_BUS_DEVFREQ)	+= exynos/
 obj-$(CONFIG_ARM_TEGRA_DEVFREQ)		+= tegra-devfreq.o
+obj-$(CONFIG_ARCH_ROCKCHIP)		+= rockchip/
 
 # DEVFREQ Event Drivers
 obj-$(CONFIG_PM_DEVFREQ_EVENT)		+= event/
diff --git a/drivers/devfreq/rockchip/Kconfig b/drivers/devfreq/rockchip/Kconfig
new file mode 100644
index 0000000..d8f9e66
--- /dev/null
+++ b/drivers/devfreq/rockchip/Kconfig
@@ -0,0 +1,8 @@
+config ARM_RK3399_DMC_DEVFREQ
+	tristate "ARM RK3399 DMC DEVFREQ Driver"
+	select PM_OPP
+	select DEVFREQ_GOV_SIMPLE_ONDEMAND
+	help
+          This adds the DEVFREQ driver for the RK3399 dmc. It sets the frequency
+          for the memory controller and reads the usage counts from hardware.
+
diff --git a/drivers/devfreq/rockchip/Makefile b/drivers/devfreq/rockchip/Makefile
new file mode 100644
index 0000000..c62c105
--- /dev/null
+++ b/drivers/devfreq/rockchip/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ)	+= rk3399_dmc.o
diff --git a/drivers/devfreq/rockchip/rk3399_dmc.c b/drivers/devfreq/rockchip/rk3399_dmc.c
new file mode 100644
index 0000000..527aa11
--- /dev/null
+++ b/drivers/devfreq/rockchip/rk3399_dmc.c
@@ -0,0 +1,473 @@
+/*
+ * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
+ * Author: Lin Huang <hl at rock-chips.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/arm-smccc.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/devfreq.h>
+#include <linux/devfreq-event.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
+#include <linux/regulator/consumer.h>
+#include <linux/rwsem.h>
+#include <linux/suspend.h>
+#include <linux/syscore_ops.h>
+
+#include <soc/rockchip/rockchip_sip.h>
+
+struct dram_timing {
+	unsigned int ddr3_speed_bin;
+	unsigned int pd_idle;
+	unsigned int sr_idle;
+	unsigned int sr_mc_gate_idle;
+	unsigned int srpd_lite_idle;
+	unsigned int standby_idle;
+	unsigned int dram_dll_dis_freq;
+	unsigned int phy_dll_dis_freq;
+	unsigned int ddr3_odt_dis_freq;
+	unsigned int ddr3_drv;
+	unsigned int ddr3_odt;
+	unsigned int phy_ddr3_ca_drv;
+	unsigned int phy_ddr3_dq_drv;
+	unsigned int phy_ddr3_odt;
+	unsigned int lpddr3_odt_dis_freq;
+	unsigned int lpddr3_drv;
+	unsigned int lpddr3_odt;
+	unsigned int phy_lpddr3_ca_drv;
+	unsigned int phy_lpddr3_dq_drv;
+	unsigned int phy_lpddr3_odt;
+	unsigned int lpddr4_odt_dis_freq;
+	unsigned int lpddr4_drv;
+	unsigned int lpddr4_dq_odt;
+	unsigned int lpddr4_ca_odt;
+	unsigned int phy_lpddr4_ca_drv;
+	unsigned int phy_lpddr4_ck_cs_drv;
+	unsigned int phy_lpddr4_dq_drv;
+	unsigned int phy_lpddr4_odt;
+};
+
+struct rk3399_dmcfreq {
+	struct device *dev;
+	struct devfreq *devfreq;
+	struct devfreq_simple_ondemand_data ondemand_data;
+	struct clk *dmc_clk;
+	struct devfreq_event_dev *edev;
+	struct mutex lock;
+	struct dram_timing *timing;
+	wait_queue_head_t	wait_dcf_queue;
+	int irq;
+	int wait_dcf_flag;
+	struct regulator *vdd_center;
+	unsigned long rate, target_rate;
+	unsigned long volt, target_volt;
+};
+
+static int rk3399_dmcfreq_target(struct device *dev, unsigned long *freq,
+				 u32 flags)
+{
+	struct platform_device *pdev = container_of(dev, struct platform_device,
+						    dev);
+	struct rk3399_dmcfreq *dmcfreq = platform_get_drvdata(pdev);
+	struct dev_pm_opp *opp;
+	unsigned long old_clk_rate = dmcfreq->rate;
+	unsigned long target_volt, target_rate;
+	int err;
+
+	rcu_read_lock();
+	opp = devfreq_recommended_opp(dev, freq, flags);
+	if (IS_ERR(opp)) {
+		rcu_read_unlock();
+		return PTR_ERR(opp);
+	}
+
+	target_rate = dev_pm_opp_get_freq(opp);
+	target_volt = dev_pm_opp_get_voltage(opp);
+	opp = devfreq_recommended_opp(dev, &dmcfreq->rate, flags);
+	if (IS_ERR(opp)) {
+		rcu_read_unlock();
+		return PTR_ERR(opp);
+	}
+	dmcfreq->volt = dev_pm_opp_get_voltage(opp);
+	rcu_read_unlock();
+
+	if (dmcfreq->rate == target_rate)
+		return 0;
+
+	mutex_lock(&dmcfreq->lock);
+
+	/*
+	 * if frequency scaling from low to high, adjust voltage first;
+	 * if frequency scaling from high to low, adjuset frequency first;
+	 */
+	if (old_clk_rate < target_rate) {
+		err = regulator_set_voltage(dmcfreq->vdd_center, target_volt,
+					    target_volt);
+		if (err) {
+			dev_err(dev, "Unable to set vol %lu\n", target_volt);
+			goto out;
+		}
+	}
+	dmcfreq->wait_dcf_flag = 1;
+	err = clk_set_rate(dmcfreq->dmc_clk, target_rate);
+	if (err) {
+		dev_err(dev,
+			"Unable to set freq %lu. Current freq %lu. Error %d\n",
+			target_rate, old_clk_rate, err);
+		regulator_set_voltage(dmcfreq->vdd_center, dmcfreq->volt,
+				      dmcfreq->volt);
+		goto out;
+	}
+
+	/*
+	 * wait until bcf irq happen, it means freq scaling finish in bl31,
+	 * use 100ms as timeout time
+	 */
+	if (!wait_event_timeout(dmcfreq->wait_dcf_queue,
+				!dmcfreq->wait_dcf_flag, HZ / 10))
+		dev_warn(dev, "Timeout waiting for dcf irq\n");
+	/*
+	 * check the dpll rate
+	 * there only two result we will get,
+	 * 1. ddr frequency scaling fail, we still get the old rate
+	 * 2, ddr frequency scaling sucessful, we get the rate we set
+	 */
+	dmcfreq->rate = clk_get_rate(dmcfreq->dmc_clk);
+
+	/* if get the incorrect rate, set voltage to old value */
+	if (dmcfreq->rate != target_rate) {
+		dev_err(dev, "get wrong ddr frequency, Request freq %lu,\
+			Current freq %lu\n", target_rate, dmcfreq->rate);
+		regulator_set_voltage(dmcfreq->vdd_center, dmcfreq->volt,
+				      dmcfreq->volt);
+	} else if (old_clk_rate > target_rate)
+		err = regulator_set_voltage(dmcfreq->vdd_center, target_volt,
+					    target_volt);
+	if (err)
+		dev_err(dev, "Unable to set vol %lu\n", target_volt);
+
+out:
+	mutex_unlock(&dmcfreq->lock);
+	return err;
+}
+
+static int rk3399_dmcfreq_get_dev_status(struct device *dev,
+					 struct devfreq_dev_status *stat)
+{
+	struct platform_device *pdev = container_of(dev, struct platform_device,
+						    dev);
+	struct rk3399_dmcfreq *dmcfreq = platform_get_drvdata(pdev);
+	struct devfreq_event_data edata;
+
+	devfreq_event_get_event(dmcfreq->edev, &edata);
+
+	stat->current_frequency = dmcfreq->rate;
+	stat->busy_time = edata.load_count;
+	stat->total_time = edata.total_count;
+
+	return 0;
+}
+
+static int rk3399_dmcfreq_get_cur_freq(struct device *dev, unsigned long *freq)
+{
+	struct platform_device *pdev = container_of(dev, struct platform_device,
+						    dev);
+	struct rk3399_dmcfreq *dmcfreq = platform_get_drvdata(pdev);
+
+	*freq = dmcfreq->rate;
+
+	return 0;
+}
+
+static void rk3399_dmcfreq_exit(struct device *dev)
+{
+	struct platform_device *pdev = container_of(dev,
+						    struct platform_device,
+						    dev);
+	struct rk3399_dmcfreq *dmcfreq = platform_get_drvdata(pdev);
+
+	devfreq_unregister_opp_notifier(dev, dmcfreq->devfreq);
+}
+
+static struct devfreq_dev_profile rk3399_devfreq_dmc_profile = {
+	.polling_ms	= 200,
+	.target		= rk3399_dmcfreq_target,
+	.get_dev_status	= rk3399_dmcfreq_get_dev_status,
+	.get_cur_freq	= rk3399_dmcfreq_get_cur_freq,
+	.exit		= rk3399_dmcfreq_exit,
+};
+
+static __maybe_unused int rk3399_dmcfreq_suspend(struct device *dev)
+{
+	struct platform_device *pdev = container_of(dev,
+						    struct platform_device,
+						    dev);
+	struct rk3399_dmcfreq *dmcfreq = platform_get_drvdata(pdev);
+
+	devfreq_event_disable_edev(dmcfreq->edev);
+	devfreq_suspend_device(dmcfreq->devfreq);
+
+	return 0;
+}
+
+static __maybe_unused int rk3399_dmcfreq_resume(struct device *dev)
+{
+	struct platform_device *pdev = container_of(dev,
+						    struct platform_device,
+						    dev);
+	struct rk3399_dmcfreq *dmcfreq = platform_get_drvdata(pdev);
+
+	devfreq_event_enable_edev(dmcfreq->edev);
+	devfreq_resume_device(dmcfreq->devfreq);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(rk3399_dmcfreq_pm, rk3399_dmcfreq_suspend,
+			 rk3399_dmcfreq_resume);
+
+static irqreturn_t rk3399_dmc_irq(int irq, void *dev_id)
+{
+	struct rk3399_dmcfreq *dmcfreq = dev_id;
+	struct arm_smccc_res res;
+
+	dmcfreq->wait_dcf_flag = 0;
+	wake_up(&dmcfreq->wait_dcf_queue);
+
+	/* clr dcf irq */
+	arm_smccc_smc(SIP_DDR_FREQ, 0, 0, CONFIG_DRAM_CLR_IRQ,
+		      0, 0, 0, 0, &res);
+
+	return IRQ_HANDLED;
+}
+
+static int of_do_get_timing(struct device_node *np,
+		struct dram_timing *timing)
+{
+	int ret;
+
+	ret = of_property_read_u32(np, "ddr3_speed_bin",
+				   &timing->ddr3_speed_bin);
+	ret |= of_property_read_u32(np, "pd_idle", &timing->pd_idle);
+	ret |= of_property_read_u32(np, "sr_idle", &timing->sr_idle);
+	ret |= of_property_read_u32(np, "sr_mc_gate_idle",
+				    &timing->sr_mc_gate_idle);
+	ret |= of_property_read_u32(np, "srpd_lite_idle",
+				    &timing->srpd_lite_idle);
+	ret |= of_property_read_u32(np, "standby_idle", &timing->standby_idle);
+	ret |= of_property_read_u32(np, "dram_dll_dis_freq",
+				    &timing->dram_dll_dis_freq);
+	ret |= of_property_read_u32(np, "phy_dll_dis_freq",
+				    &timing->phy_dll_dis_freq);
+	ret |= of_property_read_u32(np, "ddr3_odt_dis_freq",
+				    &timing->ddr3_odt_dis_freq);
+	ret |= of_property_read_u32(np, "ddr3_drv", &timing->ddr3_drv);
+	ret |= of_property_read_u32(np, "ddr3_odt", &timing->ddr3_odt);
+	ret |= of_property_read_u32(np, "phy_ddr3_ca_drv",
+				    &timing->phy_ddr3_ca_drv);
+	ret |= of_property_read_u32(np, "phy_ddr3_dq_drv",
+				    &timing->phy_ddr3_dq_drv);
+	ret |= of_property_read_u32(np, "phy_ddr3_odt", &timing->phy_ddr3_odt);
+	ret |= of_property_read_u32(np, "lpddr3_odt_dis_freq",
+				    &timing->lpddr3_odt_dis_freq);
+	ret |= of_property_read_u32(np, "lpddr3_drv", &timing->lpddr3_drv);
+	ret |= of_property_read_u32(np, "lpddr3_odt", &timing->lpddr3_odt);
+	ret |= of_property_read_u32(np, "phy_lpddr3_ca_drv",
+				    &timing->phy_lpddr3_ca_drv);
+	ret |= of_property_read_u32(np, "phy_lpddr3_dq_drv",
+				    &timing->phy_lpddr3_dq_drv);
+	ret |= of_property_read_u32(np, "phy_lpddr3_odt",
+				    &timing->phy_lpddr3_odt);
+	ret |= of_property_read_u32(np, "lpddr4_odt_dis_freq",
+				    &timing->lpddr4_odt_dis_freq);
+	ret |= of_property_read_u32(np, "lpddr4_drv",
+				    &timing->lpddr4_drv);
+	ret |= of_property_read_u32(np, "lpddr4_dq_odt",
+				    &timing->lpddr4_dq_odt);
+	ret |= of_property_read_u32(np, "lpddr4_ca_odt",
+				    &timing->lpddr4_ca_odt);
+	ret |= of_property_read_u32(np, "phy_lpddr4_ca_drv",
+				    &timing->phy_lpddr4_ca_drv);
+	ret |= of_property_read_u32(np, "phy_lpddr4_ck_cs_drv",
+				    &timing->phy_lpddr4_ck_cs_drv);
+	ret |= of_property_read_u32(np, "phy_lpddr4_dq_drv",
+				    &timing->phy_lpddr4_dq_drv);
+	ret |= of_property_read_u32(np, "phy_lpddr4_odt",
+				    &timing->phy_lpddr4_odt);
+
+	return ret;
+}
+
+static struct dram_timing *of_get_ddr_timings(struct device *dev,
+					      struct device_node *np)
+{
+	struct dram_timing	*timing = NULL;
+	struct device_node	*np_tim;
+
+	np_tim = of_parse_phandle(np, "ddr_timing", 0);
+	if (np_tim) {
+		timing = devm_kzalloc(dev, sizeof(*timing), GFP_KERNEL);
+		if (!timing)
+			goto err;
+		if (of_do_get_timing(np_tim, timing)) {
+			devm_kfree(dev, timing);
+			goto err;
+		}
+		return timing;
+	}
+
+err:
+	if (timing) {
+		devm_kfree(dev, timing);
+		timing = NULL;
+	}
+
+	return timing;
+}
+
+static int rk3399_dmcfreq_probe(struct platform_device *pdev)
+{
+	struct arm_smccc_res res;
+	struct device *dev = &pdev->dev;
+	struct device_node *np = pdev->dev.of_node;
+	uint64_t param = 0;
+	struct rk3399_dmcfreq *data;
+	int ret, irq, index;
+	uint32_t *timing;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "no dmc irq resource\n");
+		return -EINVAL;
+	}
+	data = devm_kzalloc(dev, sizeof(struct rk3399_dmcfreq), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	mutex_init(&data->lock);
+
+	data->vdd_center = devm_regulator_get(dev, "center");
+	if (IS_ERR(data->vdd_center)) {
+		dev_err(dev, "Cannot get the regulator \"center\"\n");
+		return PTR_ERR(data->vdd_center);
+	}
+
+	data->dmc_clk = devm_clk_get(dev, "dmc_clk");
+	if (IS_ERR(data->dmc_clk)) {
+		dev_err(dev, "Cannot get the clk dmc_clk\n");
+		return PTR_ERR(data->dmc_clk);
+	};
+
+	data->irq = irq;
+	ret = devm_request_irq(dev, irq, rk3399_dmc_irq, 0,
+			       dev_name(dev), data);
+	if (ret) {
+		dev_err(dev, "failed to request dmc irq: %d\n", ret);
+		return ret;
+	}
+
+	/* get dram timing and pass it to bl31 */
+	data->timing = of_get_ddr_timings(dev, np);
+	if (data->timing) {
+		timing = (uint32_t *)data->timing;
+		for (index = 0; index < (sizeof(struct dram_timing) / 4);
+		     index++) {
+			param = index;
+			param = param << 32 | *timing++;
+			arm_smccc_smc(SIP_DDR_FREQ, param, 0,
+				      CONFIG_DRAM_SET_PARAM, 0, 0, 0, 0, &res);
+			if (res.a0) {
+				dev_err(dev, "failed to set dram param: %ld\n",
+					res.a0);
+				return -EINVAL;
+			}
+			param = 0;
+		}
+	}
+
+	arm_smccc_smc(SIP_DDR_FREQ, 0, 0, CONFIG_DRAM_INIT,
+		      0, 0, 0, 0, &res);
+
+	init_waitqueue_head(&data->wait_dcf_queue);
+	data->wait_dcf_flag = 0;
+
+	data->edev = devfreq_event_get_edev_by_phandle(dev, 0);
+	if (IS_ERR(data->edev))
+		return -EPROBE_DEFER;
+
+	ret = devfreq_event_enable_edev(data->edev);
+	if (ret < 0) {
+		dev_err(dev, "failed to enable devfreq-event devices\n");
+		return ret;
+	}
+
+	/*
+	 * We add a devfreq driver to our parent since it has a device tree node
+	 * with operating points.
+	 */
+	if (dev_pm_opp_of_add_table(dev)) {
+		dev_err(dev, "Invalid operating-points in device tree.\n");
+		return -EINVAL;
+	}
+	of_property_read_u32(np, "upthreshold",
+			     &data->ondemand_data.upthreshold);
+	of_property_read_u32(np, "downdifferential",
+			     &data->ondemand_data.downdifferential);
+	data->rate = clk_get_rate(data->dmc_clk);
+	rk3399_devfreq_dmc_profile.initial_freq = data->rate;
+	data->devfreq = devfreq_add_device(dev,
+					   &rk3399_devfreq_dmc_profile,
+					   "simple_ondemand",
+					   &data->ondemand_data);
+	if (IS_ERR(data->devfreq))
+		return PTR_ERR(data->devfreq);
+	devfreq_register_opp_notifier(dev, data->devfreq);
+
+	data->dev = dev;
+	platform_set_drvdata(pdev, data);
+
+	return 0;
+}
+
+static int rk3399_dmcfreq_remove(struct platform_device *pdev)
+{
+	struct rk3399_dmcfreq *dmcfreq = platform_get_drvdata(pdev);
+
+	devfreq_remove_device(dmcfreq->devfreq);
+	regulator_put(dmcfreq->vdd_center);
+
+	return 0;
+}
+
+static const struct of_device_id rk3399dmc_devfreq_of_match[] = {
+	{ .compatible = "rockchip,rk3399-dmc" },
+	{ },
+};
+
+static struct platform_driver rk3399_dmcfreq_driver = {
+	.probe	= rk3399_dmcfreq_probe,
+	.remove	= rk3399_dmcfreq_remove,
+	.driver = {
+		.name	= "rk3399-dmc-freq",
+		.pm	= &rk3399_dmcfreq_pm,
+		.of_match_table = rk3399dmc_devfreq_of_match,
+	},
+};
+module_platform_driver(rk3399_dmcfreq_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("RK3399 dmcfreq driver with devfreq framework");
-- 
1.9.1



From tomeu at tomeuvizoso.net  Fri Jul 29 01:38:34 2016
From: tomeu at tomeuvizoso.net (Tomeu Vizoso)
Date: Fri, 29 Jul 2016 10:38:34 +0200
Subject: [PATCH v14 0/17] Add Analogix Core Display Port Driver
In-Reply-To: <57031D8E.1020903@rock-chips.com>
References: <1455534485-1154-1-git-send-email-ykk@rock-chips.com>
 <20160331101535.GZ2510@phenom.ffwll.local> <57031D8E.1020903@rock-chips.com>
Message-ID: <CAAObsKAkVd1KC5+MJ4=RyZVsEC1kwsz6V946T1wTkpr8sPeRhA@mail.gmail.com>

On 5 April 2016 at 04:06, Yakir Yang <ykk at rock-chips.com> wrote:
> Hi Daniel,
>
>
> On 03/31/2016 06:15 PM, Daniel Vetter wrote:
>>
>> On Mon, Feb 15, 2016 at 07:08:05PM +0800, Yakir Yang wrote:
>>>
>>> Hi all,
>>>
>>>    The Samsung Exynos eDP controller and Rockchip RK3288 eDP controller
>>> share the same IP, so a lot of parts can be re-used. I split the common
>>> code into bridge directory, then rk3288 and exynos only need to keep
>>> some platform code. Cause I can't find the exact IP name of exynos dp
>>> controller, so I decide to name dp core driver with "analogix" which I
>>> find in rk3288 eDP TRM
>>>
>>> But there are still three light registers setting different between
>>> exynos and rk3288.
>>> 1. RK3288 have five special pll registers which not indicate in exynos
>>>     dp controller.
>>> 2. The address of DP_PHY_PD(dp phy power manager register) are different
>>>     between rk3288 and exynos.
>>> 3. Rk3288 and exynos have different setting with AUX_HW_RETRY_CTL(dp
>>> debug
>>>     register).
>>>
>>> Due to Mark Yao have introduced the ATOMIC support to Rockchip drm, so
>>> it's
>>> okay to use the ATOMIC helpers functions in connector_funcs. No need to
>>> splict
>>> the connector init to platform driver anymore, and this is the biggest
>>> change
>>> since version 11.
>>>
>>> This v14 didn't have lots of new changes which seems not the correct time
>>> to
>>> upgrade the version number, but I have changed ordering of patches
>>> (adding 2
>>> more, and removing 2 out). Especially to prevent confusing people, so I
>>> updated
>>> the whole series.
>>
>> So I'm jumping into this part way late, but just noticed (well Thierry
>> pointed this out to me) that the exynos dp driver reinvents all the dp and
>> dp-aux helpers we already. That's somewhat okish for a private driver (and
>> exynos has a reputation for that kind of stuff), but imo not ok for a
>> shared driver.
>>
>> Not saying this should block merging this patch, but it really needs to be
>> addressed. All the dp aux and edid read code in the current
>> exynos_dp_core/reg.c files needs to be replaced with dp helpers and the
>> core i2c edid reading code.
>>
>> Who's going to sign up to do this?
>
>
> Volunteer to that, after finish this thread, I would send new series to
> switch to take use of dp helper.

Hi Yakir,

do you have plans to do this work in the short term? If not, I can tackle it.

Regards,

Tomeu

> :-D
> - Yakir
>
>
>> -Daniel
>>
>>> Thanks,
>>> - Yakir
>>>
>>>
>>> Changes in v14:
>>> - Rebase the new changes in imx-dp driver
>>> - Split up this patch into 3 parts, make this easy to review (Heiko)
>>> - Remove the Rockchip DP PHY to an separate thread (Heiko)
>>>      https://patchwork.kernel.org/patch/8312701/
>>>
>>> Changes in v13:
>>> - Use .enable instead of preprare/commit in encoder_helper_funcs (Heiko)
>>> - Fix the missing parameters with drm_encoder_init() helper function.
>>> (Heiko)
>>>
>>> Changes in v12:
>>> - Move the connector init to analogix_dp driver, and using ATOMIC helper
>>> (Heiko)
>>> - Add the ack from Jingoo
>>> - Remove the enum link_rate_type struct, using the marcos in
>>> drm_dp_helper.h (Jingoo)
>>>
>>> Changes in v11:
>>> - Uses tabs to fix the indentation issues in analogix_dp_core.h (Heiko)
>>> - Rename the "analogix,need-force-hpd" to common 'force-hpd' (Rob)
>>> - Add the ack from Rob Herring
>>> - Revert parts of Gustavo Padovan's changes in commit:
>>>         drm/exynos: do not start enabling DP at bind() phase
>>>    Add dp phy poweron function in bind time.
>>> - Move the panel prepare from get_modes time to bind time, and move
>>>    the panel unprepare from bridge->disable to unbind time. (Heiko)
>>>
>>> Changes in v10:
>>> - Add the ack from Rob Herring
>>> - Correct the ROCKCHIP_ANALOGIX_DP indentation in Kconfig to tabs here
>>> (Heiko)
>>> - Add the ack from Rob Herring
>>> - Remove the surplus "plat_data" check. (Heiko)
>>> -       switch (dp->plat_data && dp->plat_data->dev_type) {
>>> +       switch (dp->plat_data->dev_type) {
>>>
>>> Changes in v9:
>>> - Document more details for 'ports' property.
>>>
>>> Changes in v8:
>>> - Correct the right document path of display-timing.txt (Heiko)
>>> - Correct the misspell of 'from' to 'frm'. (Heiko)
>>> - Modify the commit subject name. (Heiko)
>>>
>>> Changes in v7:
>>> - Back to use the of_property_read_bool() interfacs to provoid backward
>>>    compatibility of "hsync-active-high" "vsync-active-high" "interlaced"
>>>    to avoid -EOVERFLOW error (Krzysztof)
>>>
>>> Changes in v6:
>>> - Fix the Kconfig recursive dependency (Javier)
>>> - Fix Peach Pit hpd property name error:
>>> -       hpd-gpio = <&gpx2 6 0>;
>>> +       hpd-gpios = <&gpx2 6 0>;
>>>
>>> Changes in v5:
>>> - Correct the check condition of gpio_is_valid when driver try to get
>>>    the "hpd-gpios" DT propery. (Heiko)
>>> - Move the platform attach callback in the front of core driver bridge
>>>    attch function. Cause once platform failed at attach, core driver
>>> should
>>>    still failed, so no need to init connector before platform attached
>>> (Krzysztof)
>>> - Keep code style no changes with the previous exynos_dp_code.c in this
>>>    patch, and update commit message about the new export symbol
>>> (Krzysztof)
>>> - Gather the device type patch (v4 11/16) into this one. (Krzysztof)
>>> - leave out the connector registration to analogix platform driver.
>>> (Thierry)
>>> - Resequence this patch after analogix_dp driver have been split
>>>    from exynos_dp code, and rephrase reasonable commit message, and
>>>    remove some controversial style (Krzysztof)
>>>      -          analogix_dp_write_byte_to_dpcd(
>>>      -                          dp, DP_TEST_RESPONSE,
>>>      +          analogix_dp_write_byte_to_dpcd(dp,
>>>      +                          DP_TEST_RESPONSE,
>>>                                 DP_TEST_EDID_CHECKSUM_WRITE);
>>> - Switch video timing type to "u32", so driver could use
>>> "of_property_read_u32"
>>>    to get the backword timing values. Krzysztof suggest me that driver
>>> could use
>>>    the "of_property_read_bool" to get backword timing values, but that
>>> interfacs
>>>    would modify the original drm_display_mode timing directly (whether
>>> those
>>>    properties exists or not).
>>> - Correct the misspell in commit message. (Krzysztof)
>>> - Remove the empty line at the end of document, and correct the endpoint
>>>    numbers in the example DT node, and remove the regulator iomux setting
>>>    in driver code while using the pinctl in devicetree instead. (Heiko)
>>> - Add device type declared, cause the previous "platform device type
>>>    support (v4 11/16)" already merge into (v5 02/14).
>>> - Implement connector registration code. (Thierry)
>>> - Split binding doc's from driver changes. (Rob)
>>> - Add eDP hotplug pinctrl property. (Heiko)
>>>
>>> Changes in v4:
>>> - Update "analogix,hpd-gpios" to "hpd-gpios" DT propery. (Rob)
>>> - Rename "analogix_dp-exynos.c" file name to "exynos_dp.c" (Jingoo)
>>> - Create a separate folder for analogix code in bridge/ (Archit)
>>> - Update commit message more readable. (Jingoo)
>>> - Adjust the order from 05 to 04
>>> - Provide backword compatibility with samsung. (Krzysztof)
>>> - Split all DTS changes, and provide backward compatibility. Mark old
>>>    properties as deprecated but still support them. (Krzysztof)
>>> - Update "analogix,hpd-gpio" to "hpd-gpios" prop name. (Rob)
>>> - Deprecated some properties which could parsed from Edid/Mode/DPCD.
>>> (Thierry)
>>>      "analogix,color-space" & "analogix,color-depth"   &
>>>      "analogix,link-rate"   & "analogix,lane-count"    &
>>>      "analogix,ycbcr-coeff" & "analogix,dynamic-range" &
>>>      "vsync-active-high"    & "hsync-active-high"      & "interlaces"
>>> - Separate all DTS changes to a separate patch. (Krzysztof)
>>> - Remove some deprecated DT properties in rockchip dp document.
>>> - Seprate the link-rate and lane-count limit out with the device_type
>>>    flag. (Thierry)
>>> - Take Jingoo suggest, add commit messages.
>>> - Call drm_panel_prepare() in .get_modes function, ensure panel should
>>>    power on before driver try to read edid message.
>>>
>>> Changes in v3:
>>> - Move exynos's video_timing code to analogix_dp-exynos platform driver,
>>>    add get_modes method to struct analogix_dp_plat_data. (Thierry)
>>> - Rename some "samsung*" dts propery to "analogix*". (Heiko)
>>> - The link_rate and lane_count shouldn't config to the DT property value
>>>    directly, but we can take those as hardware limite. For example,
>>> RK3288
>>>    only support 4 physical lanes of 2.7/1.62 Gbps/lane, so DT property
>>> would
>>>    like "link-rate = 0x0a" "lane-count = 4". (Thierry)
>>> - Dynamic parse video timing info from struct drm_display_mode and
>>>    struct drm_display_info. (Thierry)
>>> - Add devicetree binding documents. (Heiko)
>>> - Remove sync pol & colorimetry properies from the new analogix dp driver
>>>    devicetree binding. (Thierry)
>>> - Update the exist exynos dtsi file with the latest DP DT properies.
>>> - Leave "sclk_edp_24m" to rockchip dp phy driver which name to "24m",
>>>    and leave "sclk_edp" to analogix dp core driver which name to "dp",
>>>    and leave "pclk_edp" to rockchip dp platform driver which name to
>>>    "pclk". (Thierry & Heiko)
>>> - Add devicetree binding document. (Heiko)
>>> - Remove "rockchip,panel" DT property, take use of remote point to get
>>> panel
>>>    node. (Heiko)
>>> - Add the new function point dp_platdata->get_modes() init.
>>> - Add "analogix,need-force-hpd" to indicate whether driver need foce
>>>    hpd when hpd detect failed.
>>> - move dp hpd detect to connector detect function.
>>> - Add edid modes parse support
>>>
>>> Changes in v2:
>>> - Remove new copyright (Jingoo)
>>> - Fix compiled failed due to analogix_dp_device misspell
>>> - Improved commit message more readable, and avoid using some
>>>    uncommon style like bellow: (Joe Preches)
>>>      -  retval = exynos_dp_read_bytes_from_i2c(...
>>>                                   ...);
>>>      +  retval =
>>>      +  exynos_dp_read_bytes_from_i2c(......);
>>> - Get panel node with remote-endpoint method, and create devicetree
>>> binding
>>>    for driver. (Heiko)
>>> - Remove the clock enable/disbale with "sclk_edp" & "sclk_edp_24m",
>>>    leave those clock to rockchip dp phy driver.
>>> - Fix compile failed dut to phy_pd_addr variable misspell error
>>>
>>> Heiko Stuebner (2):
>>>    drm/exynos: dp: rename implementation specific driver part
>>>    drm: bridge: analogix/dp: rename register constants
>>>
>>> Yakir Yang (15):
>>>    drm: bridge: analogix/dp: split exynos dp driver to bridge directory
>>>    drm: bridge: analogix/dp: fix some obvious code style
>>>    drm: bridge: analogix/dp: remove duplicate configuration of link rate
>>>      and link count
>>>    drm: bridge: analogix/dp: dynamic parse sync_pol & interlace &
>>>      dynamic_range
>>>    dt-bindings: add document for analogix display port driver
>>>    ARM: dts: exynos/dp: remove some properties that deprecated by
>>>      analogix_dp driver
>>>    drm: rockchip: dp: add rockchip platform dp driver
>>>    dt-bindings: add document for rockchip variant of analogix_dp
>>>    drm: bridge: analogix/dp: add some rk3288 special registers setting
>>>    drm: bridge: analogix/dp: add max link rate and lane count limit for
>>>      RK3288
>>>    drm: bridge: analogix/dp: try force hpd after plug in lookup failed
>>>    drm: bridge: analogix/dp: move hpd detect to connector detect function
>>>    drm: bridge: analogix/dp: add edid modes parse in get_modes method
>>>    drm: bridge: analogix/dp: add panel prepare/unprepare in
>>>      suspend/resume time
>>>    drm: bridge: analogix/dp: Fix the possible dead lock in bridge disable
>>>      time
>>>
>>>   .../bindings/display/bridge/analogix_dp.txt        |   52 +
>>>   .../bindings/display/exynos/exynos_dp.txt          |   93 +-
>>>   .../display/rockchip/analogix_dp-rockchip.txt      |   92 ++
>>>   arch/arm/boot/dts/exynos5250-arndale.dts           |    2 -
>>>   arch/arm/boot/dts/exynos5250-smdk5250.dts          |    2 -
>>>   arch/arm/boot/dts/exynos5250-snow-common.dtsi      |    4 +-
>>>   arch/arm/boot/dts/exynos5250-spring.dts            |    4 +-
>>>   arch/arm/boot/dts/exynos5420-peach-pit.dts         |    4 +-
>>>   arch/arm/boot/dts/exynos5420-smdk5420.dts          |    2 -
>>>   arch/arm/boot/dts/exynos5800-peach-pi.dts          |    2 -
>>>   drivers/gpu/drm/bridge/Kconfig                     |    2 +
>>>   drivers/gpu/drm/bridge/Makefile                    |    1 +
>>>   drivers/gpu/drm/bridge/analogix/Kconfig            |    3 +
>>>   drivers/gpu/drm/bridge/analogix/Makefile           |    1 +
>>>   drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 1430
>>> ++++++++++++++++++
>>>   drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |  281 ++++
>>>   drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  | 1320
>>> +++++++++++++++++
>>>   .../analogix/analogix_dp_reg.h}                    |  270 ++--
>>>   drivers/gpu/drm/exynos/Kconfig                     |    3 +-
>>>   drivers/gpu/drm/exynos/Makefile                    |    2 +-
>>>   drivers/gpu/drm/exynos/exynos_dp.c                 |  324 +++++
>>>   drivers/gpu/drm/exynos/exynos_dp_core.c            | 1510
>>> --------------------
>>>   drivers/gpu/drm/exynos/exynos_dp_core.h            |  282 ----
>>>   drivers/gpu/drm/exynos/exynos_dp_reg.c             | 1263
>>> ----------------
>>>   drivers/gpu/drm/rockchip/Kconfig                   |    9 +
>>>   drivers/gpu/drm/rockchip/Makefile                  |    1 +
>>>   drivers/gpu/drm/rockchip/analogix_dp-rockchip.c    |  384 +++++
>>>   include/drm/bridge/analogix_dp.h                   |   41 +
>>>   28 files changed, 4115 insertions(+), 3269 deletions(-)
>>>   create mode 100644
>>> Documentation/devicetree/bindings/display/bridge/analogix_dp.txt
>>>   create mode 100644
>>> Documentation/devicetree/bindings/display/rockchip/analogix_dp-rockchip.txt
>>>   create mode 100644 drivers/gpu/drm/bridge/analogix/Kconfig
>>>   create mode 100644 drivers/gpu/drm/bridge/analogix/Makefile
>>>   create mode 100644 drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>>>   create mode 100644 drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
>>>   create mode 100644 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
>>>   rename drivers/gpu/drm/{exynos/exynos_dp_reg.h =>
>>> bridge/analogix/analogix_dp_reg.h} (62%)
>>>   create mode 100644 drivers/gpu/drm/exynos/exynos_dp.c
>>>   delete mode 100644 drivers/gpu/drm/exynos/exynos_dp_core.c
>>>   delete mode 100644 drivers/gpu/drm/exynos/exynos_dp_core.h
>>>   delete mode 100644 drivers/gpu/drm/exynos/exynos_dp_reg.c
>>>   create mode 100644 drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
>>>   create mode 100644 include/drm/bridge/analogix_dp.h
>>>
>>> --
>>> 1.9.1
>>>
>>>
>>> _______________________________________________
>>> dri-devel mailing list
>>> dri-devel at lists.freedesktop.org
>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>
>
>
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel


From ulf.hansson at linaro.org  Fri Jul 29 02:21:01 2016
From: ulf.hansson at linaro.org (Ulf Hansson)
Date: Fri, 29 Jul 2016 11:21:01 +0200
Subject: [PATCH 0/5] Add power domain support for dw_mmc/sdhci-of-arasan
In-Reply-To: <1469765703-30999-1-git-send-email-shawn.lin@rock-chips.com>
References: <1469765703-30999-1-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <CAPDyKFq6itpANwnLqgOqKHT0fJH4SvZi2FWrafa=HSBfiZE52w@mail.gmail.com>

On 29 July 2016 at 06:14, Shawn Lin <shawn.lin at rock-chips.com> wrote:
>
> We need to enable the power domain manually while probing as the
> power policy will turn off the pd which is not referenced. Otherwise

I assume you are going to use the generic PM domain (genpd)?
If so, this statement isn't entirely correct.

As a matter of fact, the PM domain will be "powered on" once you have
registered your PM domain with genpd and when the device (dw_mmc etc)
gets attached to the domain. The attach occurs when the platform bus'
->probe() function get invoked for the device.

Genpd monitors each device within the domain. To allow it to "power
off", all devices in same domain needs to be runtime PM enabled and
runtime PM suspended.

Due to this, you should be able to deploy genpd support+an updated
DTB, without first having to deploying runtime PM support in the
related drivers. In other words, you should be able to work on these
things almost independently of each other.

> we may take risk of breaking kernel once Feng Xiao land the rockchip pd
> relate stuff. So, let's do it in advance.
>
> This pachset is based on the latest linux-next[1] which should contain
> the next branch of linux-mmc and dt stuff. It's ideal to pack these all
> into Ulf' tree with Heiko's ack for dts, Rob's ack for Doc and Jaehoon's
> ack for dw_mmc. But it's okay to be merged seperately.
>
> [1] git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
>
> Thanks.
>
>
>
> Shawn Lin (5):
>   Documentation: mmc: synopsys-dw-mshc: add power domain description
>   Documentation: mmc: sdhci-of-arasan: add power domain description

To me this isn't necessary to describe for each device. I think
Documentation/devicetree/bindings/power/power_domain.txt describes all
that is needed.

>   arm64: dts: rockchip: Provide power-domains for sd/sdio/emmc
>     controllers
>   mmc: dw_mmc: add power domain support
>   mmc: sdhci-of-arasan: add power domain support

I would rephrase these commit message headers, to something like
"Deploy runtime PM support", as that is actually what you intend to
do.

>
>  Documentation/devicetree/bindings/mmc/arasan,sdhci.txt     |  1 +
>  Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt |  2 ++
>  arch/arm64/boot/dts/rockchip/rk3399.dtsi                   |  3 +++
>  drivers/mmc/host/dw_mmc.c                                  | 11 +++++++++++
>  drivers/mmc/host/sdhci-of-arasan.c                         | 10 ++++++++++
>  5 files changed, 27 insertions(+)
>
> --
> 2.3.7
>
>

Kind regards
Uffe


From thierry.reding at gmail.com  Fri Jul 29 07:24:27 2016
From: thierry.reding at gmail.com (Thierry Reding)
Date: Fri, 29 Jul 2016 16:24:27 +0200
Subject: [PATCH 2/2] dt-bindings: add simple-panel-dsi and simple-panel
In-Reply-To: <CAL_JsqL8D3OK_JQcY+dLTcqcvmgB2Vuc0Tknf1zsR=8Pa3F4jw@mail.gmail.com>
References: <1468984730-23186-1-git-send-email-mark.yao@rock-chips.com>
 <1468984730-23186-2-git-send-email-mark.yao@rock-chips.com>
 <20160725152125.GS21170@ulmo.ba.sec>
 <5796C47C.5040208@rock-chips.com>
 <20160726090226.GB2433@ulmo.ba.sec>
 <CAL_JsqL8D3OK_JQcY+dLTcqcvmgB2Vuc0Tknf1zsR=8Pa3F4jw@mail.gmail.com>
Message-ID: <20160729142427.GB3864@ulmo.ba.sec>

On Thu, Jul 28, 2016 at 02:00:21PM -0500, Rob Herring wrote:
> On Tue, Jul 26, 2016 at 4:02 AM, Thierry Reding
> <thierry.reding at gmail.com> wrote:
> > On Tue, Jul 26, 2016 at 10:01:32AM +0800, Mark yao wrote:
> >> On 2016?07?25? 23:21, Thierry Reding wrote:
> >>
> >>     On Wed, Jul 20, 2016 at 11:18:50AM +0800, Mark Yao wrote:
> >>
> >>         Allow user add display timing on device tree with simple-panel-dsi
> >>         or simple-panel.
> >>
> >>         Cc: Thierry Reding <thierry.reding at gmail.com>
> >>         Cc: Rob Herring <robh+dt at kernel.org>
> >>         Cc: Mark Rutland <mark.rutland at arm.com>
> >>
> >>         Signed-off-by: Mark Yao <mark.yao at rock-chips.com>
> >>         ---
> >>          .../bindings/display/panel/simple-panel.txt        | 48 ++++++++++++++++++++++
> >>          1 file changed, 48 insertions(+)
> >>
> >>     Sorry, not going to happen. Read this for an explanation of why not:
> >>
> >>             https://sietch-tagr.blogspot.de/2016/04/display-panels-are-not-special.html
> >>
> >>     Thierry
> >>
> >>
> >> Hi Thierry
> >>
> >> The blog actually not persuade me why can't use display timing on
> >> device tree.
> >
> > Okay, perhaps read it again, it addresses most of your points below.
> >
> >> 1, Binding panel as a simple string on device tree seems simple on device tree,
> >> but it's complex on kernel code, and kernel code would became bigger and
> >> bigger.
> >
> > I don't think the video timings in the simple-panel driver are very
> > complex. They also don't use very much space. And if you're really
> > concerned about space you can always use conditional compilation and
> > Kconfig symbols to remove timings for panels that you don't use.
> >
> > Also, panels are characterized by much more than just video timings.
> > There were attempts, way back, to fully describe panels in device tree
> > and that failed. What you propose here is a partial solution to a much
> > more complex problem.
> >
> > This is all explained in the blog post.
> 
> While I agree with everything Thierry has said in this thread, there
> is an argument to be made for timing information in DT.
> 
> Maybe most vendors have now learned that flexible clock frequencies
> are needed to generate the correct timings, but I have worked on parts
> without fine resolution (i.e. a dedicated pll). Even HiKey with HDMI
> suffers from this. In that case you can be tuning the timings
> (increasing the h and/or v blanks) to get the desired refresh rates.
> So in that case, the panel compatible would not determine the display
> timings and you could have the same panel with different timings on
> different boards. That's maybe rare enough that putting timing info in
> DT still wouldn't make sense.

There's another mechanism, though not very popular (possibly because it
is indeed a very rare usecase), that we introduced a while ago. The idea
is that we don't specify the mode per panel, as we do now, but rather a
range of values that are valid for the various video timing parameters.
These set can usually be copied from some datasheet and are represented
by (minimum, typical, maximum) triplets. So drivers for display hardware
would query the timings in this way, try to establish a working mode
from the typical values and tweak as necessary by adjusting the values
within the limits given by (minimum, maximum).

One disadvantage of this is that it becomes somewhat more complicated to
write the display driver, but I think that's an easy problem to solve by
adding some common helpers (I'm thinking one that takes as input the
target clock frequency and the timing ranges and outputs a mode with the
porches adjusted to match the target clock frequency as closely as
possible).

The big advantage is, of course, that device trees become trivial to
write and people don't have to manually stitch together timings to get
to the desired result.

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160729/fd6558a0/attachment-0001.sig>

From helgaas at kernel.org  Fri Jul 29 09:48:47 2016
From: helgaas at kernel.org (Bjorn Helgaas)
Date: Fri, 29 Jul 2016 11:48:47 -0500
Subject: [PATCH v8 2/2] PCI: Rockchip: Add Rockchip PCIe controller support
In-Reply-To: <1469760493-25821-2-git-send-email-shawn.lin@rock-chips.com>
References: <1469760493-25821-1-git-send-email-shawn.lin@rock-chips.com>
 <1469760493-25821-2-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <20160729164847.GB23928@localhost>

Hi Shawn,

You fixed some of the things I mentioned, but not all, and you
over-fixed some things, leading to issues.

On Fri, Jul 29, 2016 at 10:48:13AM +0800, Shawn Lin wrote:
> This patch adds Rockchip PCIe controller support found
> on RK3399 Soc platform.
> 
> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
> 
> Reviewed-by: Brian Norris <briannorris at chromium.org>
> ---
> 
> Changes in v8:
> - fix all the comments suggested by Bjorn Helgaas in v7[1]
>   [1] https://patchwork.kernel.org/patch/9233887/
> - enable bandwith interrupt for debug
> 
> Changes in v7:
> - make it as a build-in driver
> - improve gen1/2 training timeout checking
> - only clear known interrupt
> - fix INTx for 0-base index
> 
> Changes in v6:
> - use "depends on PCI_MSI_IRQ_DOMAIN" suggested by Arnd
> 
> Changes in v5:
> - handle multiple pending INTx at the same time
>   suggested by Marc
> 
> Changes in v4:
> - address the comments from Brain
> 
> Changes in v3:
> - remove header file
> - remove struct msi_controller and move most of variables
>   of rockchip_pcie_port to become the local ones.
> - allow legacy int even if enabling MSI
> - drop regulator set voltage operation suggested by Doug
> 
> Changes in v2:
> - remove phy related stuff and call phy API
> - add new head file and define lots of macro to make
>   the code more readable
> - remove lots msi related code suggested by Marc
> - add IO window address translation
> - init_port and parse_dt reconstruction suggested by Bharat
> - improve wr_own_conf suggested by Arnd
> - make pcie as an interrupt controller and fix wrong int handler
>   suggested by Marc
> - remove PCI_PROBE_ONLY suggested by Lorenzo
> 
>  drivers/pci/host/Kconfig         |   11 +
>  drivers/pci/host/Makefile        |    1 +
>  drivers/pci/host/pcie-rockchip.c | 1257 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 1269 insertions(+)
>  create mode 100644 drivers/pci/host/pcie-rockchip.c
> 
> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
> index c917e2f..4d714d2 100644
> --- a/drivers/pci/host/Kconfig
> +++ b/drivers/pci/host/Kconfig
> @@ -264,4 +264,15 @@ config PCIE_ARTPEC6
>  	  Say Y here to enable PCIe controller support on Axis ARTPEC-6
>  	  SoCs.  This PCIe controller uses the DesignWare core.
>  
> +config PCIE_ROCKCHIP
> +	bool "Rockchip PCIe controller"
> +	depends on ARM64 && ARCH_ROCKCHIP
> +	depends on OF
> +	depends on PCI_MSI_IRQ_DOMAIN
> +	select MFD_SYSCON
> +	help
> +	  Say Y here if you want internal PCI support on Rockchip SoC.
> +	  There is 1 internal PCIe port available to support GEN2 with
> +	  4 slots.
> +
>  endmenu
> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> index 5bc0af2..695f716 100644
> --- a/drivers/pci/host/Makefile
> +++ b/drivers/pci/host/Makefile
> @@ -30,3 +30,4 @@ obj-$(CONFIG_PCI_HOST_THUNDER_ECAM) += pci-thunder-ecam.o
>  obj-$(CONFIG_PCI_HOST_THUNDER_PEM) += pci-thunder-pem.o
>  obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o
>  obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o
> +obj-$(CONFIG_PCIE_ROCKCHIP) += pcie-rockchip.o
> diff --git a/drivers/pci/host/pcie-rockchip.c b/drivers/pci/host/pcie-rockchip.c
> new file mode 100644
> index 0000000..13ac199
> --- /dev/null
> +++ b/drivers/pci/host/pcie-rockchip.c
> @@ -0,0 +1,1257 @@
> +/*
> + * Rockchip AXI PCIe host controller driver
> + *
> + * Copyright (c) 2016 Rockchip, Inc.
> + *
> + * Author: Shawn Lin <shawn.lin at rock-chips.com>
> + *         Wenrui Li <wenrui.li at rock-chips.com>
> + *
> + * Bits taken from Synopsys Designware Host controller driver and
> + * ARM PCI Host generic driver.
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/irqchip/chained_irq.h>
> +#include <linux/irqdomain.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/of_pci.h>
> +#include <linux/of_platform.h>
> +#include <linux/of_irq.h>
> +#include <linux/pci.h>
> +#include <linux/pci_ids.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/reset.h>
> +#include <linux/regmap.h>
> +

Indent all the following constants with tabs, not spaces.

> +#define PCIE_CLIENT_BASE                   0x0
> +#define PCIE_RC_CONFIG_NORMAL_BASE         0x800000
> +#define PCIE_RC_CONFIG_BASE                0xa00000

> +#define PCIE_RC_CONFIG_LCSR_OFFSET         0xd0
> +#define PCIE_RC_CONFIG_LCSR_BW_ENA         0x1
> +#define PCIE_RC_CONFIG_LCSR_LBMIE          BIT(10)
> +#define PCIE_RC_CONFIG_LCSR_LABIE          BIT(11)
> +#define PCIE_RC_CONFIG_LCSR_LBMS           BIT(30)
> +#define PCIE_RC_CONFIG_LCSR_LAMS           BIT(31)

This section looks like the offset and fields in one register (LCSR).

1) Remove "_OFFSET".  You don't use it on similar registers below,
e.g., IE_CORE_OB_REGION_ADDR1.

2) Indent the field definitions a space or two to show the association
with the register name, e.g.,

  #define PCIE_RC_CONFIG_LCSR                0xd0
  #define   PCIE_RC_CONFIG_LCSR_BW_ENA       0x1

3) Make the field definition the same width as the register.  In this
case, you use readl, so it would be:

  #define   PCIE_RC_CONFIG_LCSR_BW_ENA       0x00000001

4) Use the same type of definition (plain hex value vs. BIT()) for all
fields in a register.  BIT(x) likely maps to your documentation, but
my personal preference is the plain hex value because it's simpler to
debug, it shows the register width, and it works nicely for multi-bit
fields.  I guess GENMASK() is one way to handle multi-bit fields, too.
Whichever you choose, use it consistently.

> +#define PCIE_CORE_LINK_CTRL_STATUS         0x8000d0

Unused, remove.

> +#define PCIE_CORE_CTRL_MGMT_BASE           0x900000
> +#define PCIE_CORE_AXI_CONF_BASE            0xc00000
> +#define PCIE_CORE_AXI_INBOUND_BASE         0xc00800
> +#define PCIE_CLIENT_BASIC_STATUS0          0x44

Unused, remove.

> +#define PCIE_CLIENT_BASIC_STATUS1          0x48
> +#define PCIE_CLIENT_INT_MASK               0x4c
> +#define PCIE_CLIENT_INT_STATUS             0x50
> +#define PCIE_CLIENT_INT_LEGACY_DONE        BIT(15)
> +#define PCIE_CLIENT_INT_MSG                BIT(14)
> +#define PCIE_CLIENT_INT_HOT_RST            BIT(13)
> +#define PCIE_CLIENT_INT_DPA                BIT(12)
> +#define PCIE_CLIENT_INT_FATAL_ERR          BIT(11)
> +#define PCIE_CLIENT_INT_NFATAL_ERR         BIT(10)
> +#define PCIE_CLIENT_INT_CORR_ERR           BIT(9)
> +#define PCIE_CLIENT_INT_INTD               BIT(8)
> +#define PCIE_CLIENT_INT_INTC               BIT(7)
> +#define PCIE_CLIENT_INT_INTB               BIT(6)
> +#define PCIE_CLIENT_INT_INTA               BIT(5)
> +#define PCIE_CLIENT_INT_LOCAL              BIT(4)
> +#define PCIE_CLIENT_INT_UDMA               BIT(3)
> +#define PCIE_CLIENT_INT_PHY                BIT(2)
> +#define PCIE_CLIENT_INT_HOT_PLUG           BIT(1)
> +#define PCIE_CLIENT_INT_PWR_STCG           BIT(0)
> +#define PCIE_RC_CONFIG_RID_CCR             0x8
> +#define PCIE_RC_CONFIG_LCS                 0xd0
> +#define PCIE_RC_BAR_CONF                   0x300
> +#define PCIE_CORE_OB_REGION_ADDR1          0x4
> +#define PCIE_CORE_OB_REGION_DESC0          0x8
> +#define PCIE_CORE_OB_REGION_DESC1          0xc
> +#define PCIE_CORE_OB_REGION_ADDR0_NUM_BITS 0x3fU

Why do some of these constants have "U" and others not?  I don't think
they're needed.

> +#define PCIE_CORE_OB_REGION_ADDR0_LO_ADDR  0xffffff00U
> +#define PCIE_CORE_IB_REGION_ADDR0_NUM_BITS 0x3fU
> +#define PCIE_CORE_IB_REGION_ADDR0_LO_ADDR  0xffffff00U
> +#define PCIE_RP_IB_ADDR_TRANS              0x4
> +#define PCIE_CORE_INT_MASK                 0x900210
> +#define PCIE_CORE_INT_STATUS               0x90020c
> +#define PCIE_CORE_INT_PRFPE                BIT(0)
> +#define PCIE_CORE_INT_CRFPE                BIT(1)
> +#define PCIE_CORE_INT_RRPE                 BIT(2)
> +#define PCIE_CORE_INT_PRFO                 BIT(3)
> +#define PCIE_CORE_INT_CRFO                 BIT(4)
> +#define PCIE_CORE_INT_RT                   BIT(5)
> +#define PCIE_CORE_INT_RTR                  BIT(6)
> +#define PCIE_CORE_INT_PE                   BIT(7)
> +#define PCIE_CORE_INT_MTR                  BIT(8)
> +#define PCIE_CORE_INT_UCR                  BIT(9)
> +#define PCIE_CORE_INT_FCE                  BIT(10)
> +#define PCIE_CORE_INT_CT                   BIT(11)
> +#define PCIE_CORE_INT_UTC                  BIT(18)
> +#define PCIE_CORE_INT_MMVC                 BIT(19)
> +
> +/* Size of one AXI Region (not Region 0) */
> +#define AXI_REGION_SIZE                    BIT(20)
> +/* Size of Region 0, equal to sum of sizes of other regions */
> +#define AXI_REGION_0_SIZE                 (32 * (0x1 << 20))
> +#define OB_REG_SIZE_SHIFT                  5
> +#define IB_ROOT_PORT_REG_SIZE_SHIFT        3
> +#define AXI_WRAPPER_IO_WRITE               0x6
> +#define AXI_WRAPPER_MEM_WRITE              0x2

This could do with a blank line.  The below are not directly
associated with the registers above.

> +#define MAX_AXI_IB_ROOTPORT_REGION_NUM     3
> +#define MIN_AXI_ADDR_BITS_PASSED           8
> +#define ROCKCHIP_VENDOR_ID                 0x1d87
> +#define PCIE_ECAM_BUS(x)		  (((x) & 0xff) << 20)
> +#define PCIE_ECAM_DEV(x)		  (((x) & 0x1f) << 15)
> +#define PCIE_ECAM_FUNC(x)		  (((x) & 0x7) << 12)
> +#define PCIE_ECAM_REG(x)		  (((x) & 0xfff) << 0)
> +#define PCIE_ECAM_ADDR(bus, dev, func, reg) \
> +	  (PCIE_ECAM_BUS(bus) | PCIE_ECAM_DEV(dev) | \
> +	   PCIE_ECAM_FUNC(func) | PCIE_ECAM_REG(reg))
> +
> +/*
> +  * The higher 16-bit of this register is used for write protection
> +  * only if BIT(x + 16) set to 1 the BIT(x) can be written.
> +  */
> +#define HIWORD_UPDATE(val, mask, shift) \
> +	((val) << (shift) | (mask) << ((shift) + 16))
> +
> +#define RC_REGION_0_ADDR_TRANS_H          0x00000000
> +#define RC_REGION_0_ADDR_TRANS_L          0x00000000
> +#define RC_REGION_0_PASS_BITS             (25 - 1)
> +#define RC_REGION_1_ADDR_TRANS_H          0x00000000
> +#define RC_REGION_1_ADDR_TRANS_L          0x00400000
> +#define RC_REGION_1_PASS_BITS             (20 - 1)
> +#define MAX_AXI_WRAPPER_REGION_NUM        33
> +#define PCIE_CORE_LCSR_RETRAIN_LINK       BIT(5)
> +#define PCIE_CLIENT_CONF_ENABLE           1
> +#define PCIE_CLIENT_CONF_ENABLE_SHIFT     0
> +#define PCIE_CLIENT_CONF_ENABLE_MASK      0x1
> +#define PCIE_CLIENT_LINK_TRAIN_ENABLE     1
> +#define PCIE_CLIENT_LINK_TRAIN_SHIFT      1
> +#define PCIE_CLIENT_LINK_TRAIN_MASK       0x1
> +#define PCIE_CLIENT_ARI_ENABLE            1
> +#define PCIE_CLIENT_ARI_ENABLE_SHIFT      3
> +#define PCIE_CLIENT_ARI_ENABLE_MASK       0x1
> +#define PCIE_CLIENT_CONF_LANE_NUM(x)      (x / 2)
> +#define PCIE_CLIENT_CONF_LANE_NUM_SHIFT   4
> +#define PCIE_CLIENT_CONF_LANE_NUM_MASK    0x3
> +#define PCIE_CLIENT_MODE_RC               1
> +#define PCIE_CLIENT_MODE_SHIFT            6
> +#define PCIE_CLIENT_MODE_MASK             0x1
> +#define PCIE_CLIENT_GEN_SEL_2             1
> +#define PCIE_CLIENT_GEN_SEL_1             0
> +#define PCIE_CLIENT_GEN_SEL_SHIFT         7
> +#define PCIE_CLIENT_GEN_SEL_MASK          0x1
> +#define PCIE_CLIENT_LINK_STATUS_UP        0x3
> +#define PCIE_CLIENT_LINK_STATUS_SHIFT     20
> +#define PCIE_CLIENT_LINK_STATUS_MASK      0x3
> +#define PCIE_CORE_PL_CONF_SPEED_2_5G      0x0
> +#define PCIE_CORE_PL_CONF_SPEED_5G        0x1
> +#define PCIE_CORE_PL_CONF_SPEED_8G        0x2
> +#define PCIE_CORE_PL_CONF_SPEED_SHIFT     3
> +#define PCIE_CORE_PL_CONF_SPEED_MASK      0x3
> +#define PCIE_CORE_PL_CONF_LANE_SHIFT      1
> +#define PCIE_CORE_PL_CONF_LANE_MASK       0x3
> +#define PCIE_CORE_RC_CONF_SCC_SHIFT       16
> +
> +#define ROCKCHIP_PCIE_RPIFR1_INTR_MASK    GENMASK(8, 5)
> +#define ROCKCHIP_PCIE_RPIFR1_INTR_SHIFT   5
> +
> +#define PCIE_CORE_INT \
> +		(PCIE_CORE_INT_PRFPE | PCIE_CORE_INT_CRFPE | \
> +		 PCIE_CORE_INT_RRPE | PCIE_CORE_INT_CRFO | \
> +		 PCIE_CORE_INT_RT | PCIE_CORE_INT_RTR | \
> +		 PCIE_CORE_INT_PE | PCIE_CORE_INT_MTR | \
> +		 PCIE_CORE_INT_UCR | PCIE_CORE_INT_FCE | \
> +		 PCIE_CORE_INT_CT | PCIE_CORE_INT_UTC | \
> +		 PCIE_CORE_INT_MMVC)
> +
> +#define PCIE_CLIENT_INT_SUBSYSTEM \
> +	(PCIE_CLIENT_INT_PWR_STCG | PCIE_CLIENT_INT_HOT_PLUG | \
> +	PCIE_CLIENT_INT_PHY | PCIE_CLIENT_INT_UDMA | \
> +	PCIE_CLIENT_INT_LOCAL)
> +
> +#define PCIE_CLIENT_INT_LEGACY \
> +	(PCIE_CLIENT_INT_INTA | PCIE_CLIENT_INT_INTB | \
> +	PCIE_CLIENT_INT_INTC | PCIE_CLIENT_INT_INTD)
> +
> +#define PCIE_CLIENT_INT_CLI \
> +	(PCIE_CLIENT_INT_CORR_ERR | PCIE_CLIENT_INT_NFATAL_ERR | \
> +	PCIE_CLIENT_INT_FATAL_ERR | PCIE_CLIENT_INT_DPA | \
> +	PCIE_CLIENT_INT_HOT_RST | PCIE_CLIENT_INT_MSG | \
> +	PCIE_CLIENT_INT_LEGACY_DONE | PCIE_CLIENT_INT_LEGACY | \
> +	PCIE_CLIENT_INT_PHY)
> +
> +struct rockchip_pcie_port {
> +	void	__iomem *reg_base;
> +	void	__iomem *apb_base;
> +	struct	phy *phy;
> +	struct	reset_control *core_rst;
> +	struct	reset_control *mgmt_rst;
> +	struct	reset_control *mgmt_sticky_rst;
> +	struct	reset_control *pipe_rst;
> +	struct	clk *aclk_pcie;
> +	struct	clk *aclk_perf_pcie;
> +	struct	clk *hclk_pcie;
> +	struct	clk *clk_pcie_pm;
> +	struct	regulator *vpcie3v3; /* 3.3V power supply */
> +	struct	regulator *vpcie1v8; /* 1.8V power supply */
> +	struct	regulator *vpcie0v9; /* 0.9V power supply */
> +	struct	gpio_desc *ep_gpio;
> +	u32	lanes;
> +	u8	root_bus_nr;
> +	struct	device *dev;
> +	struct	irq_domain *irq_domain;
> +};
> +
> +static inline u32 pcie_read(struct rockchip_pcie_port *port, u32 reg)
> +{
> +	return readl(port->apb_base + reg);
> +}
> +
> +static inline void pcie_write(struct rockchip_pcie_port *port,
> +			      u32 val, u32 reg)
> +{
> +	writel(val, port->apb_base + reg);
> +}
> +
> +static void rockchip_pcie_enable_bw_int(struct rockchip_pcie_port *port)
> +{
> +	u32 status;
> +
> +	status = pcie_read(port, PCIE_RC_CONFIG_BASE +
> +				 PCIE_RC_CONFIG_LCSR_OFFSET);
> +	status |= (PCIE_RC_CONFIG_LCSR_LBMIE | PCIE_RC_CONFIG_LCSR_LABIE);
> +	pcie_write(port, status, PCIE_RC_CONFIG_BASE +
> +				 PCIE_RC_CONFIG_LCSR_OFFSET);
> +}
> +
> +static void rockchip_pcie_clr_bw_int(struct rockchip_pcie_port *port)
> +{
> +	u32 status;
> +
> +	status = pcie_read(port, PCIE_RC_CONFIG_BASE +
> +				 PCIE_RC_CONFIG_LCSR_OFFSET);
> +	status |= (PCIE_RC_CONFIG_LCSR_LBMS | PCIE_RC_CONFIG_LCSR_LAMS);
> +	pcie_write(port, status, PCIE_RC_CONFIG_BASE +
> +				 PCIE_RC_CONFIG_LCSR_OFFSET);
> +}
> +
> +static int rockchip_pcie_valid_config(struct rockchip_pcie_port *pp,
> +				      struct pci_bus *bus, int dev)
> +{
> +	/* access only one slot on each root port */
> +	if (bus->number == pp->root_bus_nr && dev > 0)
> +		return 0;
> +
> +	/*
> +	 * do not read more than one device on the bus directly attached
> +	 * to RC's downstream side.
> +	 */
> +	if (bus->primary == pp->root_bus_nr && dev > 0)
> +		return 0;
> +
> +	return 1;
> +}
> +
> +static int rockchip_pcie_rd_own_conf(struct rockchip_pcie_port *pp,
> +				     int where, int size,
> +				     u32 *val)
> +{
> +	void __iomem *addr = pp->apb_base + PCIE_RC_CONFIG_BASE + where;
> +
> +	if (!IS_ALIGNED((uintptr_t)addr, size)) {
> +		*val = 0;
> +		return PCIBIOS_BAD_REGISTER_NUMBER;
> +	}
> +
> +	if (size == 4) {
> +		*val = readl(addr);
> +	} else if (size == 2) {
> +		*val = readw(addr);
> +	} else if (size == 1) {
> +		*val = readb(addr);
> +	} else {
> +		*val = 0;
> +		return PCIBIOS_BAD_REGISTER_NUMBER;
> +	}
> +	return PCIBIOS_SUCCESSFUL;
> +}
> +
> +static int rockchip_pcie_wr_own_conf(struct rockchip_pcie_port *pp,
> +				     int where, int size, u32 val)
> +{
> +	u32 mask, tmp, offset;
> +
> +	offset = (where & (~0x3));
> +
> +	if (size == 4) {
> +		writel(val, pp->apb_base + PCIE_RC_CONFIG_BASE + offset);
> +		return PCIBIOS_SUCCESSFUL;
> +	}
> +
> +	mask = ~(((1 << (size * 8)) - 1) << ((where & 0x3) * 8));
> +
> +	tmp = readl(pp->apb_base + PCIE_RC_CONFIG_BASE + offset) & mask;
> +	tmp |= val << ((where & 0x3) * 8);
> +	writel(tmp, pp->apb_base + PCIE_RC_CONFIG_BASE + offset);
> +
> +	return PCIBIOS_SUCCESSFUL;
> +}
> +
> +static int rockchip_pcie_rd_other_conf(struct rockchip_pcie_port *pp,
> +				       struct pci_bus *bus, u32 devfn,
> +				       int where, int size, u32 *val)
> +{
> +	u32 busdev;
> +
> +	busdev = PCIE_ECAM_ADDR(bus->number, PCI_SLOT(devfn),
> +				PCI_FUNC(devfn), where);
> +
> +	if (!IS_ALIGNED(busdev, size)) {
> +		*val = 0;
> +		return PCIBIOS_BAD_REGISTER_NUMBER;
> +	}
> +
> +	if (size == 4) {
> +		*val = readl(pp->reg_base + busdev);
> +	} else if (size == 2) {
> +		*val = readw(pp->reg_base + busdev);
> +	} else if (size == 1) {
> +		*val = readb(pp->reg_base + busdev);
> +	} else {
> +		*val = 0;
> +		return PCIBIOS_BAD_REGISTER_NUMBER;
> +	}
> +	return PCIBIOS_SUCCESSFUL;
> +}
> +
> +static int rockchip_pcie_wr_other_conf(struct rockchip_pcie_port *pp,
> +				       struct pci_bus *bus, u32 devfn,
> +				       int where, int size, u32 val)
> +{
> +	u32 busdev;
> +
> +	busdev = PCIE_ECAM_ADDR(bus->number, PCI_SLOT(devfn),
> +				PCI_FUNC(devfn), where);
> +	if (!IS_ALIGNED(busdev, size))
> +		return PCIBIOS_BAD_REGISTER_NUMBER;
> +
> +	if (size == 4)
> +		writel(val, pp->reg_base + busdev);
> +	else if (size == 2)
> +		writew(val, pp->reg_base + busdev);
> +	else if (size == 1)
> +		writeb(val, pp->reg_base + busdev);
> +	else
> +		return PCIBIOS_BAD_REGISTER_NUMBER;
> +
> +	return PCIBIOS_SUCCESSFUL;
> +}
> +
> +static int rockchip_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
> +				 int size, u32 *val)
> +{
> +	struct rockchip_pcie_port *pp = bus->sysdata;
> +
> +	if (rockchip_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) {
> +		*val = 0xffffffff;
> +		return PCIBIOS_DEVICE_NOT_FOUND;
> +	}
> +
> +	if (bus->number != pp->root_bus_nr)
> +		return rockchip_pcie_rd_other_conf(pp, bus, devfn,
> +						  where, size, val);
> +
> +	return rockchip_pcie_rd_own_conf(pp, where, size, val);

You only did this half-way:

1) Do this instead to avoid the negation:

  if (bus->number == pp->root_bus_nr)
    return rockchip_pcie_rd_own_conf(pp, where, size, val);

  return rockchip_pcie_rd_other_conf(pp, bus, devfn, ...);

2) Apply the same change to rockchip_pcie_wr_conf() below.

> +}
> +
> +static int rockchip_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
> +				 int where, int size, u32 val)
> +{
> +	struct rockchip_pcie_port *pp = bus->sysdata;
> +	int ret;
> +
> +	if (rockchip_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0)
> +		return PCIBIOS_DEVICE_NOT_FOUND;
> +
> +	if (bus->number != pp->root_bus_nr)
> +		ret = rockchip_pcie_wr_other_conf(pp, bus, devfn,
> +						  where, size, val);
> +	else
> +		ret = rockchip_pcie_wr_own_conf(pp, where, size, val);
> +
> +	return ret;
> +}
> +
> +static struct pci_ops rockchip_pcie_ops = {
> +	.read = rockchip_pcie_rd_conf,
> +	.write = rockchip_pcie_wr_conf,
> +};
> +
> +/**
> + * rockchip_pcie_init_port - Initialize hardware
> + * @port: PCIe port information
> + */
> +static int rockchip_pcie_init_port(struct rockchip_pcie_port *port)
> +{
> +	int err;
> +	u32 status;
> +	unsigned long timeout;
> +
> +	gpiod_set_value(port->ep_gpio, 0);
> +
> +	err = phy_init(port->phy);
> +	if (err < 0) {
> +		dev_err(port->dev, "fail to init phy, err %d\n", err);
> +		return err;
> +	}
> +
> +	err = reset_control_assert(port->core_rst);
> +	if (err) {
> +		dev_err(port->dev, "assert core_rst err %d\n", err);
> +		return err;
> +	}
> +
> +	err = reset_control_assert(port->mgmt_rst);
> +	if (err) {
> +		dev_err(port->dev, "assert mgmt_rst err %d\n", err);
> +		return err;
> +	}
> +
> +	err = reset_control_assert(port->mgmt_sticky_rst);
> +	if (err) {
> +		dev_err(port->dev, "assert mgmt_sticky_rst err %d\n", err);
> +		return err;
> +	}
> +
> +	err = reset_control_assert(port->pipe_rst);
> +	if (err) {
> +		dev_err(port->dev, "assert pipe_rst err %d\n", err);
> +		return err;
> +	}
> +
> +	pcie_write(port,
> +		   HIWORD_UPDATE(PCIE_CLIENT_CONF_ENABLE,
> +				 PCIE_CLIENT_CONF_ENABLE_MASK,
> +				 PCIE_CLIENT_CONF_ENABLE_SHIFT) |
> +		   HIWORD_UPDATE(PCIE_CLIENT_CONF_LANE_NUM(port->lanes),
> +				 PCIE_CLIENT_CONF_LANE_NUM_MASK,
> +				 PCIE_CLIENT_CONF_LANE_NUM_SHIFT) |
> +		   HIWORD_UPDATE(PCIE_CLIENT_MODE_RC,
> +				 PCIE_CLIENT_MODE_MASK,
> +				 PCIE_CLIENT_MODE_SHIFT) |
> +		   HIWORD_UPDATE(PCIE_CLIENT_ARI_ENABLE,
> +				 PCIE_CLIENT_ARI_ENABLE_MASK,
> +				 PCIE_CLIENT_ARI_ENABLE_SHIFT) |
> +		   HIWORD_UPDATE(PCIE_CLIENT_GEN_SEL_2,
> +				 PCIE_CLIENT_GEN_SEL_MASK,
> +				 PCIE_CLIENT_GEN_SEL_SHIFT),
> +		   PCIE_CLIENT_BASE);
> +
> +	err = phy_power_on(port->phy);
> +	if (err) {
> +		dev_err(port->dev, "fail to power on phy, err %d\n", err);
> +		return err;
> +	}
> +
> +	err = reset_control_deassert(port->core_rst);
> +	if (err) {
> +		dev_err(port->dev, "deassert core_rst err %d\n", err);
> +		return err;
> +	}
> +
> +	err = reset_control_deassert(port->mgmt_rst);
> +	if (err) {
> +		dev_err(port->dev, "deassert mgmt_rst err %d\n", err);
> +		return err;
> +	}
> +
> +	err = reset_control_deassert(port->mgmt_sticky_rst);
> +	if (err) {
> +		dev_err(port->dev, "deassert mgmt_sticky_rst err %d\n", err);
> +		return err;
> +	}
> +
> +	err = reset_control_deassert(port->pipe_rst);
> +	if (err) {
> +		dev_err(port->dev, "deassert pipe_rst err %d\n", err);
> +		return err;
> +	}
> +
> +	/* Enable Gen1 training */
> +	pcie_write(port,
> +		   HIWORD_UPDATE(PCIE_CLIENT_LINK_TRAIN_ENABLE,
> +				 PCIE_CLIENT_LINK_TRAIN_MASK,
> +				 PCIE_CLIENT_LINK_TRAIN_SHIFT),
> +		   PCIE_CLIENT_BASE);
> +
> +	gpiod_set_value(port->ep_gpio, 1);
> +
> +	/* 500ms timeout value should be enough for Gen1/2 training */
> +	timeout = jiffies + msecs_to_jiffies(500);
> +
> +	for (;;) {
> +		status = pcie_read(port, PCIE_CLIENT_BASIC_STATUS1);
> +		if (((status >> PCIE_CLIENT_LINK_STATUS_SHIFT) &
> +		      PCIE_CLIENT_LINK_STATUS_MASK) ==
> +		      PCIE_CLIENT_LINK_STATUS_UP) {
> +			dev_dbg(port->dev, "PCIe link training gen1 pass!\n");
> +			break;
> +		}
> +
> +		msleep(20);
> +
> +		if (!time_before(jiffies, timeout)) {
> +			err = -ETIMEDOUT;
> +			break;
> +		}
> +
> +	}
> +
> +	/* Double check gen1 training */
> +	if (err) {
> +		status = pcie_read(port, PCIE_CLIENT_BASIC_STATUS1);
> +		err = (((status >> PCIE_CLIENT_LINK_STATUS_SHIFT) &
> +			PCIE_CLIENT_LINK_STATUS_MASK) ==
> +			PCIE_CLIENT_LINK_STATUS_UP) ? 0 : -ETIMEDOUT;
> +		if (err) {
> +			dev_err(port->dev, "PCIe link training gen1 timeout!\n");
> +			return err;
> +		}
> +	}
> +
> +	/*
> +	 * Enable retrain for gen2. This should be configured only after
> +	 * gen1 finished.
> +	 */
> +	status = pcie_read(port,
> +			   PCIE_RC_CONFIG_LCS + PCIE_RC_CONFIG_BASE);
> +	status |= PCIE_CORE_LCSR_RETRAIN_LINK;
> +	pcie_write(port, status,
> +		   PCIE_RC_CONFIG_LCS + PCIE_RC_CONFIG_BASE);
> +
> +	timeout = jiffies + msecs_to_jiffies(500);
> +	for (;;) {
> +		status = pcie_read(port, PCIE_CORE_CTRL_MGMT_BASE);
> +		if (((status >> PCIE_CORE_PL_CONF_SPEED_SHIFT) &
> +		     PCIE_CORE_PL_CONF_SPEED_MASK) ==
> +		     PCIE_CORE_PL_CONF_SPEED_5G) {
> +			dev_dbg(port->dev, "PCIe link training gen2 pass!\n");
> +			break;
> +		}
> +
> +		msleep(20);
> +
> +		if (!time_before(jiffies, timeout)) {
> +			err = -ETIMEDOUT;
> +			break;
> +		}
> +	}
> +
> +	/* Double check gen2 training */
> +	if (err) {
> +		status = pcie_read(port, PCIE_CORE_CTRL_MGMT_BASE);
> +		err = (((status >> PCIE_CORE_PL_CONF_SPEED_SHIFT) &
> +			PCIE_CORE_PL_CONF_SPEED_MASK) ==
> +			PCIE_CORE_PL_CONF_SPEED_5G) ? 0 : -ETIMEDOUT;
> +		if (err)
> +			dev_dbg(port->dev, "PCIe link training gen2 timeout, fall back to gen1!\n");
> +	}
> +
> +	/* Check the final link width from negotiated lane counter from MGMT */
> +	status = pcie_read(port, PCIE_CORE_CTRL_MGMT_BASE);
> +	status =  0x1 << ((status >> PCIE_CORE_PL_CONF_LANE_SHIFT) &
> +			   PCIE_CORE_PL_CONF_LANE_MASK);
> +	dev_dbg(port->dev, "current link width is x%d\n", status);
> +
> +	pcie_write(port, ROCKCHIP_VENDOR_ID, PCIE_RC_CONFIG_BASE);
> +	pcie_write(port, PCI_CLASS_BRIDGE_PCI << PCIE_CORE_RC_CONF_SCC_SHIFT,
> +		   PCIE_RC_CONFIG_BASE + PCIE_RC_CONFIG_RID_CCR);
> +	pcie_write(port, 0x0, PCIE_CORE_CTRL_MGMT_BASE + PCIE_RC_BAR_CONF);
> +
> +	pcie_write(port, (RC_REGION_0_ADDR_TRANS_L + RC_REGION_0_PASS_BITS),
> +		   PCIE_CORE_AXI_CONF_BASE);
> +	pcie_write(port, RC_REGION_0_ADDR_TRANS_H,
> +		   PCIE_CORE_AXI_CONF_BASE + PCIE_CORE_OB_REGION_ADDR1);
> +	pcie_write(port, 0x0080000a,
> +		   PCIE_CORE_AXI_CONF_BASE + PCIE_CORE_OB_REGION_DESC0);
> +	pcie_write(port, 0x0,
> +		   PCIE_CORE_AXI_CONF_BASE + PCIE_CORE_OB_REGION_DESC1);
> +
> +	return 0;
> +}
> +
> +static irqreturn_t rockchip_pcie_subsys_irq_handler(int irq, void *arg)
> +{
> +	struct rockchip_pcie_port *pp = arg;
> +	u32 reg;
> +	u32 sub_reg;
> +
> +	reg = pcie_read(pp, PCIE_CLIENT_INT_STATUS);
> +	if (reg & PCIE_CLIENT_INT_LOCAL) {
> +		dev_dbg(pp->dev, "local interrupt received\n");
> +		sub_reg = pcie_read(pp, PCIE_CORE_INT_STATUS);
> +		if (sub_reg & PCIE_CORE_INT_PRFPE)
> +			dev_dbg(pp->dev, "parity error detected while reading from the PNP receive FIFO RAM\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_CRFPE)
> +			dev_dbg(pp->dev, "parity error detected while reading from the Completion Receive FIFO RAM\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_RRPE)
> +			dev_dbg(pp->dev, "parity error detected while reading from replay buffer RAM\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_PRFO)
> +			dev_dbg(pp->dev, "overflow occurred in the PNP receive FIFO\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_CRFO)
> +			dev_dbg(pp->dev, "overflow occurred in the completion receive FIFO\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_RT)
> +			dev_dbg(pp->dev, "replay timer timed out\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_RTR)
> +			dev_dbg(pp->dev, "replay timer rolled over after 4 transmissions of the same TLP\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_PE)
> +			dev_dbg(pp->dev, "phy error detected on receive side\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_MTR)
> +			dev_dbg(pp->dev, "malformed TLP received from the link\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_UCR)
> +			dev_dbg(pp->dev, "malformed TLP received from the link\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_FCE)
> +			dev_dbg(pp->dev, "an error was observed in the flow control advertisements from the other side\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_CT)
> +			dev_dbg(pp->dev, "a request timed out waiting for completion\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_UTC)
> +			dev_dbg(pp->dev, "unmapped TC error\n");
> +
> +		if (sub_reg & PCIE_CORE_INT_MMVC)
> +			dev_dbg(pp->dev, "MSI mask register changes\n");
> +
> +		pcie_write(pp, sub_reg, PCIE_CORE_INT_STATUS);
> +	} else if (reg & PCIE_CLIENT_INT_PHY) {
> +		dev_dbg(pp->dev, "phy link changes\n");
> +		rockchip_pcie_clr_bw_int(pp);
> +	}
> +
> +	pcie_write(pp, reg & PCIE_CLIENT_INT_LOCAL, PCIE_CLIENT_INT_STATUS);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t rockchip_pcie_client_irq_handler(int irq, void *arg)
> +{
> +	struct rockchip_pcie_port *pp = arg;
> +	u32 reg;
> +
> +	reg = pcie_read(pp, PCIE_CLIENT_INT_STATUS);
> +	if (reg & PCIE_CLIENT_INT_LEGACY_DONE)
> +		dev_dbg(pp->dev, "legacy done interrupt received\n");
> +
> +	if (reg & PCIE_CLIENT_INT_MSG)
> +		dev_dbg(pp->dev, "message done interrupt received\n");
> +
> +	if (reg & PCIE_CLIENT_INT_HOT_RST)
> +		dev_dbg(pp->dev, "hot reset interrupt received\n");
> +
> +	if (reg & PCIE_CLIENT_INT_DPA)
> +		dev_dbg(pp->dev, "dpa interrupt received\n");
> +
> +	if (reg & PCIE_CLIENT_INT_FATAL_ERR)
> +		dev_dbg(pp->dev, "fatal error interrupt received\n");
> +
> +	if (reg & PCIE_CLIENT_INT_NFATAL_ERR)
> +		dev_dbg(pp->dev, "no fatal error interrupt received\n");
> +
> +	if (reg & PCIE_CLIENT_INT_CORR_ERR)
> +		dev_dbg(pp->dev, "correctable error interrupt received\n");
> +
> +	if (reg & PCIE_CLIENT_INT_PHY)
> +		dev_dbg(pp->dev, "phy interrupt received\n");
> +
> +	pcie_write(pp, reg & (PCIE_CLIENT_INT_LEGACY_DONE |
> +			      PCIE_CLIENT_INT_MSG | PCIE_CLIENT_INT_HOT_RST |
> +			      PCIE_CLIENT_INT_DPA | PCIE_CLIENT_INT_FATAL_ERR |
> +			      PCIE_CLIENT_INT_NFATAL_ERR |
> +			      PCIE_CLIENT_INT_CORR_ERR),
> +			      PCIE_CLIENT_INT_STATUS |
> +			      PCIE_CLIENT_INT_PHY);
> +	return IRQ_HANDLED;
> +}
> +
> +static void rockchip_pcie_legacy_int_handler(struct irq_desc *desc)
> +{
> +	struct irq_chip *chip = irq_desc_get_chip(desc);
> +	struct rockchip_pcie_port *port;
> +	u32 reg;
> +	u32 hwirq;
> +	u32 virq;
> +
> +	chained_irq_enter(chip, desc);
> +	port = irq_desc_get_handler_data(desc);
> +
> +	reg = pcie_read(port, PCIE_CLIENT_INT_STATUS);
> +	reg = (reg & ROCKCHIP_PCIE_RPIFR1_INTR_MASK) >>
> +	       ROCKCHIP_PCIE_RPIFR1_INTR_SHIFT;
> +
> +	while (reg) {
> +		hwirq = ffs(reg) - 1;
> +		reg &= ~BIT(hwirq);
> +
> +		virq = irq_find_mapping(port->irq_domain, hwirq);
> +		if (virq)
> +			generic_handle_irq(virq);
> +		else
> +			dev_err(port->dev, "unexpected IRQ, INT%d\n", hwirq);
> +	}
> +
> +	chained_irq_exit(chip, desc);
> +}
> +
> +
> +/**
> + * rockchip_pcie_parse_dt - Parse Device tree

s/Device tree/Device Tree/

> + * @port: PCIe port information
> + *
> + * Return: '0' on success and error value on failure
> + */
> +static int rockchip_pcie_parse_dt(struct rockchip_pcie_port *port)
> +{
> +	struct device *dev = port->dev;
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct device_node *node = dev->of_node;
> +	struct resource *regs;
> +	int irq;
> +	int err;
> +
> +	regs = platform_get_resource_byname(pdev,
> +					    IORESOURCE_MEM,
> +					    "axi-base");
> +	if (!regs) {
> +		dev_err(dev, "missing axi-base property\n");
> +		return -ENODEV;
> +	}
> +
> +	port->reg_base = devm_ioremap_resource(dev, regs);
> +	if (IS_ERR(port->reg_base))
> +		return PTR_ERR(port->reg_base);
> +
> +	regs = platform_get_resource_byname(pdev,
> +					    IORESOURCE_MEM,
> +					    "apb-base");
> +	if (!regs) {
> +		dev_err(dev, "missing apb-base property\n");
> +		return -ENODEV;
> +	}
> +
> +	port->apb_base = devm_ioremap_resource(dev, regs);
> +	if (IS_ERR(port->apb_base))
> +		return PTR_ERR(port->apb_base);
> +
> +	port->phy = devm_phy_get(dev, "pcie-phy");
> +	if (IS_ERR(port->phy)) {
> +		if (PTR_ERR(port->phy) != -EPROBE_DEFER)
> +			dev_err(dev, "Missing phy\n");
> +		return PTR_ERR(port->phy);
> +	}
> +
> +	port->lanes = 1;
> +	err = of_property_read_u32(node, "num-lanes", &port->lanes);
> +	if (!err && ((port->lanes == 0) ||
> +		     (port->lanes == 3) ||
> +		     (port->lanes > 4))) {
> +		dev_warn(dev, "invalid num-lanes, default use one lane\n");
> +		port->lanes = 1;
> +	}
> +
> +	port->core_rst = devm_reset_control_get(dev, "core");
> +	if (IS_ERR(port->core_rst)) {
> +		if (PTR_ERR(port->core_rst) != -EPROBE_DEFER)
> +			dev_err(dev, "missing core rst property in node\n");
> +		return PTR_ERR(port->core_rst);
> +	}
> +
> +	port->mgmt_rst = devm_reset_control_get(dev, "mgmt");
> +	if (IS_ERR(port->mgmt_rst)) {
> +		if (PTR_ERR(port->mgmt_rst) != -EPROBE_DEFER)
> +			dev_err(dev, "missing mgmt rst property in node\n");
> +		return PTR_ERR(port->mgmt_rst);
> +	}
> +
> +	port->mgmt_sticky_rst = devm_reset_control_get(dev, "mgmt-sticky");
> +	if (IS_ERR(port->mgmt_sticky_rst)) {
> +		if (PTR_ERR(port->mgmt_sticky_rst) != -EPROBE_DEFER)
> +			dev_err(dev, "missing mgmt-sticky rst property in node\n");
> +		return PTR_ERR(port->mgmt_sticky_rst);
> +	}
> +
> +	port->pipe_rst = devm_reset_control_get(dev, "pipe");
> +	if (IS_ERR(port->pipe_rst)) {
> +		if (PTR_ERR(port->pipe_rst) != -EPROBE_DEFER)
> +			dev_err(dev, "missing pipe rst property in node\n");
> +		return PTR_ERR(port->pipe_rst);
> +	}
> +
> +	port->ep_gpio = devm_gpiod_get(dev, "ep", GPIOD_OUT_HIGH);
> +	if (IS_ERR(port->ep_gpio)) {
> +		dev_err(dev, "missing ep-gpios property in node\n");
> +		return PTR_ERR(port->ep_gpio);
> +	}
> +
> +	port->aclk_pcie = devm_clk_get(dev, "aclk");
> +	if (IS_ERR(port->aclk_pcie)) {
> +		dev_err(dev, "aclk clock not found\n");
> +		return PTR_ERR(port->aclk_pcie);
> +	}
> +
> +	port->aclk_perf_pcie = devm_clk_get(dev, "aclk-perf");
> +	if (IS_ERR(port->aclk_perf_pcie)) {
> +		dev_err(dev, "aclk_perf clock not found\n");
> +		return PTR_ERR(port->aclk_perf_pcie);
> +	}
> +
> +	port->hclk_pcie = devm_clk_get(dev, "hclk");
> +	if (IS_ERR(port->hclk_pcie)) {
> +		dev_err(dev, "hclk clock not found\n");
> +		return PTR_ERR(port->hclk_pcie);
> +	}
> +
> +	port->clk_pcie_pm = devm_clk_get(dev, "pm");
> +	if (IS_ERR(port->clk_pcie_pm)) {
> +		dev_err(dev, "pm clock not found\n");
> +		return PTR_ERR(port->clk_pcie_pm);
> +	}
> +
> +	irq = platform_get_irq_byname(pdev, "sys");
> +	if (irq < 0) {
> +		dev_err(dev, "missing sys IRQ resource\n");
> +		return -EINVAL;
> +	}
> +
> +	err = devm_request_irq(dev, irq, rockchip_pcie_subsys_irq_handler,
> +			       IRQF_SHARED, "pcie-sys", port);
> +	if (err) {
> +		dev_err(dev, "failed to request pcie subsystem IRQ\n");
> +		return err;
> +	}
> +
> +	irq = platform_get_irq_byname(pdev, "legacy");
> +	if (irq < 0) {
> +		dev_err(dev, "missing legacy IRQ resource\n");
> +		return -EINVAL;
> +	}
> +
> +	irq_set_chained_handler_and_data(irq,
> +					 rockchip_pcie_legacy_int_handler,
> +					 port);
> +
> +	irq = platform_get_irq_byname(pdev, "client");
> +	if (irq < 0) {
> +		dev_err(dev, "missing client IRQ resource\n");
> +		return -EINVAL;
> +	}
> +
> +	err = devm_request_irq(dev, irq, rockchip_pcie_client_irq_handler,
> +			       IRQF_SHARED, "pcie-client", port);
> +	if (err) {
> +		dev_err(dev, "failed to request pcie client IRQ\n");

"PCIe"

> +		return err;
> +	}
> +
> +	port->vpcie3v3 = devm_regulator_get_optional(dev, "vpcie3v3");
> +	if (IS_ERR(port->vpcie3v3)) {
> +		if (PTR_ERR(port->vpcie3v3) == -EPROBE_DEFER)
> +			return -EPROBE_DEFER;
> +		dev_info(dev, "no vpcie3v3 regulator found\n");
> +	}
> +
> +	port->vpcie1v8 = devm_regulator_get_optional(dev, "vpcie1v8");
> +	if (IS_ERR(port->vpcie1v8)) {
> +		if (PTR_ERR(port->vpcie1v8) == -EPROBE_DEFER)
> +			return -EPROBE_DEFER;
> +		dev_info(dev, "no vpcie1v8 regulator found\n");
> +	}
> +
> +	port->vpcie0v9 = devm_regulator_get_optional(dev, "vpcie0v9");
> +	if (IS_ERR(port->vpcie0v9)) {
> +		if (PTR_ERR(port->vpcie0v9) == -EPROBE_DEFER)
> +			return -EPROBE_DEFER;
> +		dev_info(dev, "no vpcie0v9 regulator found\n");
> +	}
> +
> +	return 0;
> +}
> +
> +static int rockchip_pcie_set_vpcie(struct rockchip_pcie_port *port)
> +{
> +	int err;
> +
> +	if (!IS_ERR(port->vpcie3v3)) {
> +		err = regulator_enable(port->vpcie3v3);
> +		if (err) {
> +			dev_err(port->dev, "Fail to enable vpcie3v3 regulator.\n");

s/Fail/fail/ here and similar cases below.

> +			goto err_out;
> +		}
> +	}
> +
> +	if (!IS_ERR(port->vpcie1v8)) {
> +		err = regulator_enable(port->vpcie1v8);
> +		if (err) {
> +			dev_err(port->dev, "Fail to enable vpcie1v8 regulator.\n");
> +			goto err_disable_3v3;
> +		}
> +	}
> +
> +	if (!IS_ERR(port->vpcie0v9)) {
> +		err = regulator_enable(port->vpcie0v9);
> +		if (err) {
> +			dev_err(port->dev, "Fail to enable vpcie0v9 regulator.\n");
> +			goto err_disable_1v8;
> +		}
> +	}
> +
> +	return 0;
> +
> +err_disable_1v8:
> +	if (!IS_ERR(port->vpcie1v8))
> +		regulator_disable(port->vpcie1v8);
> +err_disable_3v3:
> +	if (!IS_ERR(port->vpcie3v3))
> +		regulator_disable(port->vpcie3v3);
> +err_out:
> +	return err;
> +}
> +
> +static void rockchip_pcie_enable_interrupts(struct rockchip_pcie_port *port)
> +{
> +	pcie_write(port, (PCIE_CLIENT_INT_CLI << 16) &
> +		   (~PCIE_CLIENT_INT_CLI), PCIE_CLIENT_INT_MASK);
> +	pcie_write(port, (u32)(~PCIE_CORE_INT), PCIE_CORE_INT_MASK);
> +
> +	rockchip_pcie_enable_bw_int(port);
> +}
> +
> +static int rockchip_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
> +				  irq_hw_number_t hwirq)
> +{
> +	irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq);
> +	irq_set_chip_data(irq, domain->host_data);
> +
> +	return 0;
> +}
> +
> +static const struct irq_domain_ops intx_domain_ops = {
> +	.map = rockchip_pcie_intx_map,
> +};
> +
> +static int rockchip_pcie_init_irq_domain(struct rockchip_pcie_port *pp)
> +{
> +	struct device *dev = pp->dev;
> +	struct device_node *intc = of_get_next_child(dev->of_node, NULL);
> +
> +	if (!intc) {
> +		dev_err(dev, "Missing child interrupt-controller node\n");
> +		return -EINVAL;
> +	}
> +
> +	pp->irq_domain = irq_domain_add_linear(intc, 4, &intx_domain_ops, pp);
> +	if (!pp->irq_domain) {
> +		dev_err(dev, "Failed to get a INTx IRQ domain\n");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int rockchip_pcie_prog_ob_atu(struct rockchip_pcie_port *pp,
> +				     int region_no,
> +				     int type, u8 num_pass_bits,
> +				     u32 lower_addr, u32 upper_addr)
> +{
> +	u32 ob_addr_0;
> +	u32 ob_addr_1;
> +	u32 ob_desc_0;
> +	void __iomem *aw_base;
> +
> +	if (region_no >= MAX_AXI_WRAPPER_REGION_NUM)
> +		return -EINVAL;
> +	if ((num_pass_bits + 1) < 8)
> +		return -EINVAL;
> +	if (num_pass_bits > 63)
> +		return -EINVAL;
> +	if (region_no == 0) {
> +		if (AXI_REGION_0_SIZE < (2ULL << num_pass_bits))
> +		return -EINVAL;
> +	}
> +	if (region_no != 0) {
> +		if (AXI_REGION_SIZE < (2ULL << num_pass_bits))
> +		return -EINVAL;

Misleading indentation.

> +	}

Add a blank line between all the error checking and the meat of the
function.  Also below in the corresponding ib_atu code.

> +	aw_base = pp->apb_base + PCIE_CORE_AXI_CONF_BASE;
> +	aw_base += (region_no << OB_REG_SIZE_SHIFT);
> +
> +	ob_addr_0 = num_pass_bits & PCIE_CORE_OB_REGION_ADDR0_NUM_BITS;
> +	ob_addr_0 |= lower_addr & PCIE_CORE_OB_REGION_ADDR0_LO_ADDR;
> +	ob_addr_1 = upper_addr;
> +	ob_desc_0 = (1 << 23 | type);
> +
> +	writel(ob_addr_0, aw_base);
> +	writel(ob_addr_1, aw_base + PCIE_CORE_OB_REGION_ADDR1);
> +	writel(ob_desc_0, aw_base + PCIE_CORE_OB_REGION_DESC0);
> +	writel(0, aw_base + PCIE_CORE_OB_REGION_DESC1);
> +
> +	return 0;
> +}
> +
> +static int rockchip_pcie_prog_ib_atu(struct rockchip_pcie_port *pp,
> +				     int region_no,
> +				     u8 num_pass_bits,
> +				     u32 lower_addr,
> +				     u32 upper_addr)
> +{
> +	u32 ib_addr_0;
> +	u32 ib_addr_1;
> +	void __iomem *aw_base;
> +
> +	if (!pp)
> +		return -EINVAL;

Remove this test here, too, same as in the ob_atu path.

> +	if (region_no > MAX_AXI_IB_ROOTPORT_REGION_NUM)
> +		return -EINVAL;
> +	if ((num_pass_bits + 1) < MIN_AXI_ADDR_BITS_PASSED)
> +		return -EINVAL;
> +	if (num_pass_bits > 63)
> +		return -EINVAL;
> +	aw_base = pp->apb_base + PCIE_CORE_AXI_INBOUND_BASE;
> +	aw_base += (region_no << IB_ROOT_PORT_REG_SIZE_SHIFT);
> +
> +	ib_addr_0 = num_pass_bits & PCIE_CORE_IB_REGION_ADDR0_NUM_BITS;
> +	ib_addr_0 |= (lower_addr << 8) & PCIE_CORE_IB_REGION_ADDR0_LO_ADDR;
> +	ib_addr_1 = upper_addr;
> +
> +	writel(ib_addr_0, aw_base);
> +	writel(ib_addr_1, aw_base + PCIE_RP_IB_ADDR_TRANS);
> +
> +	return 0;
> +}
> +
> +static int rockchip_pcie_probe(struct platform_device *pdev)
> +{
> +	struct rockchip_pcie_port *port;
> +	struct device *dev = &pdev->dev;
> +	struct pci_bus *bus, *child;
> +	struct resource_entry *win;
> +	resource_size_t io_base;
> +	struct resource	*busn;

Removing the initialization here means "busn" may be used
uninitialized below if the DT doesn't include an IORESOURCE_BUS item.
That's why I didn't suggest removing the initialization from "busn".

I didn't check all these variables again, so there may be others that
actually need the initialization.  Please check them all yourself.

> +	struct resource	*mem;
> +	struct resource	*io;
> +	phys_addr_t io_bus_addr;
> +	u32 io_size;
> +	phys_addr_t mem_bus_addr;
> +	u32 mem_size;
> +	int reg_no;
> +	int err;
> +	int offset;
> +
> +	LIST_HEAD(res);
> +
> +	if (!dev->of_node)
> +		return -ENODEV;
> +
> +	port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
> +	if (!port)
> +		return -ENOMEM;
> +
> +	port->dev = dev;
> +
> +	err = rockchip_pcie_parse_dt(port);
> +	if (err)
> +		return err;
> +
> +	err = clk_prepare_enable(port->aclk_pcie);
> +	if (err) {
> +		dev_err(dev, "Unable to enable aclk_pcie clock.\n");
> +		goto err_aclk_pcie;
> +	}
> +
> +	err = clk_prepare_enable(port->aclk_perf_pcie);
> +	if (err) {
> +		dev_err(dev, "Unable to enable aclk_perf_pcie clock.\n");
> +		goto err_aclk_perf_pcie;
> +	}
> +
> +	err = clk_prepare_enable(port->hclk_pcie);
> +	if (err) {
> +		dev_err(dev, "Unable to enable hclk_pcie clock.\n");
> +		goto err_hclk_pcie;
> +	}
> +
> +	err = clk_prepare_enable(port->clk_pcie_pm);
> +	if (err) {
> +		dev_err(dev, "Unable to enable hclk_pcie clock.\n");
> +		goto err_pcie_pm;
> +	}
> +
> +	err = rockchip_pcie_set_vpcie(port);
> +	if (err) {
> +		dev_err(port->dev, "Fail to set vpcie regulator.\n");
> +		goto err_set_vpcie;
> +	}
> +
> +	err = rockchip_pcie_init_port(port);
> +	if (err)
> +		goto err_vpcie;
> +
> +	platform_set_drvdata(pdev, port);
> +
> +	rockchip_pcie_enable_interrupts(port);
> +
> +	err = rockchip_pcie_init_irq_domain(port);
> +	if (err < 0)
> +		goto err_vpcie;
> +
> +	err = of_pci_get_host_bridge_resources(dev->of_node, 0, 0xff,
> +					       &res, &io_base);
> +	if (err)
> +		goto err_vpcie;
> +
> +	err = devm_request_pci_bus_resources(dev, &res);
> +	if (err)
> +		goto err_vpcie;
> +
> +	/* Get the I/O and memory ranges from DT */
> +	resource_list_for_each_entry(win, &res) {
> +		switch (resource_type(win->res)) {
> +		case IORESOURCE_IO:
> +			io = win->res;
> +			io->name = "I/O";
> +			io_size = resource_size(io);
> +			io_bus_addr = io->start - win->offset;
> +			err = pci_remap_iospace(io, io_base);
> +			if (err) {
> +				dev_warn(port->dev, "error %d: failed to map resource %pR\n",
> +					 err, io);
> +				continue;
> +			}
> +			break;
> +		case IORESOURCE_MEM:
> +			mem = win->res;
> +			mem->name = "MEM";
> +			mem_size = resource_size(mem);
> +			mem_bus_addr = mem->start - win->offset;
> +			break;
> +		case IORESOURCE_BUS:
> +			busn = win->res;
> +			break;
> +		default:
> +			continue;
> +		}
> +	}
> +
> +	if (mem_size)
> +		for (reg_no = 0; reg_no < (mem_size >> 20); reg_no++) {
> +			err = rockchip_pcie_prog_ob_atu(port, reg_no + 1,
> +							AXI_WRAPPER_MEM_WRITE,
> +							20 - 1,
> +							mem_bus_addr +
> +							(reg_no << 20),
> +							0);
> +			if (err) {
> +				dev_err(dev, "Program RC mem outbound ATU failed\n");
> +				goto err_vpcie;
> +			}
> +		}
> +
> +	err = rockchip_pcie_prog_ib_atu(port, 2, 32 - 1, 0x0, 0);
> +	if (err) {
> +		dev_err(dev, "Program RC mem inbound ATU failed\n");
> +		goto err_vpcie;
> +	}
> +
> +	offset = mem_size >> 20;
> +
> +	if (io_size)
> +		for (reg_no = 0; reg_no < (io_size >> 20); reg_no++) {
> +			err = rockchip_pcie_prog_ob_atu(port,
> +							reg_no + 1 + offset,
> +							AXI_WRAPPER_IO_WRITE,
> +							20 - 1,
> +							io_bus_addr +
> +							(reg_no << 20),
> +							0);
> +			if (err) {
> +				dev_err(dev, "Program RC io outbound ATU failed\n");
> +				goto err_vpcie;
> +			}
> +		}
> +
> +	if (busn)
> +		port->root_bus_nr = busn->start;
> +
> +	bus = pci_scan_root_bus(&pdev->dev, 0,
> +				&rockchip_pcie_ops, port, &res);

General practice is to fill the line with as much as will fit in 80
columns, then wrap.  It looks like "&rockchip_pcie_ops" would fit on
the previous line.  Actually, I think *everything* fits.

> +
> +	if (!bus) {
> +		err = -ENOMEM;
> +		goto err_vpcie;
> +	}
> +
> +	pci_bus_size_bridges(bus);
> +	pci_bus_assign_resources(bus);
> +	list_for_each_entry(child, &bus->children, node)
> +		pcie_bus_configure_settings(child);
> +
> +	pci_bus_add_devices(bus);
> +
> +	dev_warn(dev, "only 32-bit config accesses supported; smaller writes may corrupt adjacent RW1C fields\n");
> +
> +	return err;
> +
> +err_vpcie:
> +	if (!IS_ERR(port->vpcie3v3))
> +		regulator_disable(port->vpcie3v3);
> +	if (!IS_ERR(port->vpcie1v8))
> +		regulator_disable(port->vpcie1v8);
> +	if (!IS_ERR(port->vpcie0v9))
> +		regulator_disable(port->vpcie0v9);
> +err_set_vpcie:
> +	clk_disable_unprepare(port->clk_pcie_pm);
> +err_pcie_pm:
> +	clk_disable_unprepare(port->hclk_pcie);
> +err_hclk_pcie:
> +	clk_disable_unprepare(port->aclk_perf_pcie);
> +err_aclk_perf_pcie:
> +	clk_disable_unprepare(port->aclk_pcie);
> +err_aclk_pcie:
> +	return err;
> +}
> +
> +static const struct of_device_id rockchip_pcie_of_match[] = {
> +	{ .compatible = "rockchip,rk3399-pcie", },
> +	{}
> +};
> +
> +static struct platform_driver rockchip_pcie_driver = {
> +	.driver = {
> +		.name = "rockchip-pcie",
> +		.of_match_table = rockchip_pcie_of_match,
> +	},
> +	.probe = rockchip_pcie_probe,
> +
> +};
> +builtin_platform_driver(rockchip_pcie_driver);
> +

Spurious blank line here.


From seanpaul at chromium.org  Fri Jul 29 12:16:31 2016
From: seanpaul at chromium.org (Sean Paul)
Date: Fri, 29 Jul 2016 15:16:31 -0400
Subject: [PATCH] drm/analogix_dp: Ensure the panel is properly
 prepared/unprepared
In-Reply-To: <CAOw6vbKbLzm4mPtp2LsG=7=KJkS_4Oe7rA4Of0uT5vM_Xoiphw@mail.gmail.com>
References: <CAOw6vbKbLzm4mPtp2LsG=7=KJkS_4Oe7rA4Of0uT5vM_Xoiphw@mail.gmail.com>
Message-ID: <1469819791-31860-1-git-send-email-seanpaul@chromium.org>

Instead of just preparing the panel on bind, actually prepare/unprepare
during modeset/disable. The panel must be prepared in order to read hpd
status, so we need to refcount the prepares in order to ensure we don't
accidentally turn the panel off at the wrong time.

Signed-off-by: Sean Paul <seanpaul at chromium.org>
---


Hi Yakir,
This is what I was talking about upthread. I've tested it and it seems to be working.

What do you think?

Sean



drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 48 +++++++++++++++++-----
 1 file changed, 37 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index 32715da..7b764a4 100644
--- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -960,11 +960,27 @@ enum drm_connector_status
 analogix_dp_detect(struct drm_connector *connector, bool force)
 {
 	struct analogix_dp_device *dp = to_dp(connector);
+	enum drm_connector_status status = connector_status_disconnected;
+	int ret;
 
-	if (analogix_dp_detect_hpd(dp))
-		return connector_status_disconnected;
+	if (dp->plat_data->panel && dp->dpms_mode != DRM_MODE_DPMS_ON) {
+		ret = drm_panel_prepare(dp->plat_data->panel);
+		if (ret) {
+			DRM_ERROR("failed to setup panel (%d)\n", ret);
+			return connector_status_disconnected;
+		}
+	}
+
+	if (!analogix_dp_detect_hpd(dp))
+		status = connector_status_connected;
+
+	if (dp->plat_data->panel && dp->dpms_mode != DRM_MODE_DPMS_ON) {
+		ret = drm_panel_unprepare(dp->plat_data->panel);
+		if (ret)
+			DRM_ERROR("failed to setup the panel ret = %d\n", ret);
+	}
 
-	return connector_status_connected;
+	return status;
 }
 
 static void analogix_dp_connector_destroy(struct drm_connector *connector)
@@ -1035,6 +1051,18 @@ static int analogix_dp_bridge_attach(struct drm_bridge *bridge)
 	return 0;
 }
 
+static void analogix_dp_bridge_pre_enable(struct drm_bridge *bridge)
+{
+	struct analogix_dp_device *dp = bridge->driver_private;
+	int ret;
+
+	if (dp->plat_data->panel) {
+		ret = drm_panel_prepare(dp->plat_data->panel);
+		if (ret)
+			DRM_ERROR("failed to setup the panel ret = %d\n", ret);
+	}
+}
+
 static void analogix_dp_bridge_enable(struct drm_bridge *bridge)
 {
 	struct analogix_dp_device *dp = bridge->driver_private;
@@ -1058,6 +1086,7 @@ static void analogix_dp_bridge_enable(struct drm_bridge *bridge)
 static void analogix_dp_bridge_disable(struct drm_bridge *bridge)
 {
 	struct analogix_dp_device *dp = bridge->driver_private;
+	int ret;
 
 	if (dp->dpms_mode != DRM_MODE_DPMS_ON)
 		return;
@@ -1077,6 +1106,10 @@ static void analogix_dp_bridge_disable(struct drm_bridge *bridge)
 
 	pm_runtime_put_sync(dp->dev);
 
+	ret = drm_panel_unprepare(dp->plat_data->panel);
+	if (ret)
+		DRM_ERROR("failed to setup the panel ret = %d\n", ret);
+
 	dp->dpms_mode = DRM_MODE_DPMS_OFF;
 }
 
@@ -1165,9 +1198,9 @@ static void analogix_dp_bridge_nop(struct drm_bridge *bridge)
 }
 
 static const struct drm_bridge_funcs analogix_dp_bridge_funcs = {
+	.pre_enable = analogix_dp_bridge_pre_enable,
 	.enable = analogix_dp_bridge_enable,
 	.disable = analogix_dp_bridge_disable,
-	.pre_enable = analogix_dp_bridge_nop,
 	.post_disable = analogix_dp_bridge_nop,
 	.mode_set = analogix_dp_bridge_mode_set,
 	.attach = analogix_dp_bridge_attach,
@@ -1333,13 +1366,6 @@ int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev,
 
 	phy_power_on(dp->phy);
 
-	if (dp->plat_data->panel) {
-		if (drm_panel_prepare(dp->plat_data->panel)) {
-			DRM_ERROR("failed to setup the panel\n");
-			return -EBUSY;
-		}
-	}
-
 	analogix_dp_init_dp(dp);
 
 	ret = devm_request_threaded_irq(&pdev->dev, dp->irq,
-- 
2.8.0.rc3.226.g39d4020



From robh at kernel.org  Fri Jul 29 14:28:33 2016
From: robh at kernel.org (Rob Herring)
Date: Fri, 29 Jul 2016 16:28:33 -0500
Subject: [PATCH v3 1/4] iio: adc: rockchip_saradc: reset saradc
 controller before programming it
In-Reply-To: <1469629447-544-1-git-send-email-wxt@rock-chips.com>
References: <1469629447-544-1-git-send-email-wxt@rock-chips.com>
Message-ID: <20160729212833.GA12688@rob-hp-laptop>

On Wed, Jul 27, 2016 at 10:24:04PM +0800, Caesar Wang wrote:
> SARADC controller needs to be reset before programming it, otherwise
> it will not function properly.
> 
> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
> Cc: Jonathan Cameron <jic23 at kernel.org>
> Cc: Heiko Stuebner <heiko at sntech.de>
> Cc: Rob Herring <robh+dt at kernel.org>
> Cc: linux-iio at vger.kernel.org
> Cc: linux-rockchip at lists.infradead.org
> Tested-by: Guenter Roeck <linux at roeck-us.net>
> 
> ---
> 
> Changes in v3:
> - %s/devm_reset_control_get_optional()/devm_reset_control_get()
> - add Guente's test tag.
> 
> Changes in v2:
> - Make the reset as an optional property, since it should work
> with old devicetrees as well.
> 
>  .../bindings/iio/adc/rockchip-saradc.txt           |  7 +++++
>  drivers/iio/adc/Kconfig                            |  1 +
>  drivers/iio/adc/rockchip_saradc.c                  | 30 ++++++++++++++++++++++
>  3 files changed, 38 insertions(+)

Please add acks when posting new versions.

Rob


From robh at kernel.org  Fri Jul 29 14:34:35 2016
From: robh at kernel.org (Rob Herring)
Date: Fri, 29 Jul 2016 16:34:35 -0500
Subject: [PATCH v4 1/2] Documentation: bindings: add dt documentation for
 Rockchip PCIe PHY
In-Reply-To: <1469755326-10263-1-git-send-email-shawn.lin@rock-chips.com>
References: <1469755326-10263-1-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <20160729213435.GA15549@rob-hp-laptop>

On Fri, Jul 29, 2016 at 09:22:05AM +0800, Shawn Lin wrote:
> This patch adds a binding that describes the Rockchip PCIe PHY
> found on Rockchip SoCs PCIe interface.
> 
> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
> 
> ---
> 
> Changes in v4: None
> Changes in v3:
> - rename the node to pcie_phy: pcie-phy suggested by Doug
> 
> Changes in v2:
> - add clk and reset description
> - remove unit-address
> 
>  .../devicetree/bindings/phy/rockchip-pcie-phy.txt  | 32 ++++++++++++++++++++++
>  1 file changed, 32 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/phy/rockchip-pcie-phy.txt

Please add acks when posting new versions.

Rob


From robh at kernel.org  Fri Jul 29 14:43:28 2016
From: robh at kernel.org (Rob Herring)
Date: Fri, 29 Jul 2016 16:43:28 -0500
Subject: [PATCH 1/5] Documentation: mmc: synopsys-dw-mshc: add power
 domain description
In-Reply-To: <1469765703-30999-2-git-send-email-shawn.lin@rock-chips.com>
References: <1469765703-30999-1-git-send-email-shawn.lin@rock-chips.com>
 <1469765703-30999-2-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <20160729214328.GA20468@rob-hp-laptop>

On Fri, Jul 29, 2016 at 12:14:59PM +0800, Shawn Lin wrote:
> This patch adds power domain as a optional property for dw_mmc.
> We should handle the power domain if available as it's in off
> state dut to the policy of power-saving after booting up. Otherwise
> dw_mmc can not be accessed.
> 
> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
> 
> ---
> 
>  Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt | 2 ++
>  1 file changed, 2 insertions(+)

Acked-by: Rob Herring <robh at kernel.org>


From robh at kernel.org  Fri Jul 29 14:43:51 2016
From: robh at kernel.org (Rob Herring)
Date: Fri, 29 Jul 2016 16:43:51 -0500
Subject: [PATCH 2/5] Documentation: mmc: sdhci-of-arasan: add power
 domain description
In-Reply-To: <1469765703-30999-3-git-send-email-shawn.lin@rock-chips.com>
References: <1469765703-30999-1-git-send-email-shawn.lin@rock-chips.com>
 <1469765703-30999-3-git-send-email-shawn.lin@rock-chips.com>
Message-ID: <20160729214351.GA21203@rob-hp-laptop>

On Fri, Jul 29, 2016 at 12:15:00PM +0800, Shawn Lin wrote:
> This patch adds power domain as a optional property for sdhci-of-arasan.
> We should handle the power domain if available as it's in off
> state due to the policy of power-saving after booting up. Otherwise
> controller can not be accessed.
> 
> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
> ---
> 
>  Documentation/devicetree/bindings/mmc/arasan,sdhci.txt | 1 +
>  1 file changed, 1 insertion(+)

Acked-by: Rob Herring <robh at kernel.org>


From wxt at rock-chips.com  Fri Jul 29 16:13:41 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Sat, 30 Jul 2016 07:13:41 +0800
Subject: [PATCH v3 1/4] iio: adc: rockchip_saradc: reset saradc controller
 before programming it
In-Reply-To: <20160729212833.GA12688@rob-hp-laptop>
References: <1469629447-544-1-git-send-email-wxt@rock-chips.com>
 <20160729212833.GA12688@rob-hp-laptop>
Message-ID: <579BE325.4010103@rock-chips.com>


On 2016?07?30? 05:28, Rob Herring wrote:
> On Wed, Jul 27, 2016 at 10:24:04PM +0800, Caesar Wang wrote:
>> SARADC controller needs to be reset before programming it, otherwise
>> it will not function properly.
>>
>> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
>> Cc: Jonathan Cameron <jic23 at kernel.org>
>> Cc: Heiko Stuebner <heiko at sntech.de>
>> Cc: Rob Herring <robh+dt at kernel.org>
>> Cc: linux-iio at vger.kernel.org
>> Cc: linux-rockchip at lists.infradead.org
>> Tested-by: Guenter Roeck <linux at roeck-us.net>
>>
>> ---
>>
>> Changes in v3:
>> - %s/devm_reset_control_get_optional()/devm_reset_control_get()
>> - add Guente's test tag.
>>
>> Changes in v2:
>> - Make the reset as an optional property, since it should work
>> with old devicetrees as well.
>>
>>   .../bindings/iio/adc/rockchip-saradc.txt           |  7 +++++
>>   drivers/iio/adc/Kconfig                            |  1 +
>>   drivers/iio/adc/rockchip_saradc.c                  | 30 ++++++++++++++++++++++
>>   3 files changed, 38 insertions(+)
> Please add acks when posting new versions.

I didn't get your ACK when posting the V3. :-P

---
Your ack on 2016-07-27 23:12

  .../bindings/iio/adc/rockchip-saradc.txt           |  7 +++++

Acked-by: Rob Herring<robh at kernel.org>



>
> Rob
>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip


-- 
caesar wang | software engineer | wxt at rock-chip.com




From sasukewxt at 163.com  Fri Jul 29 16:13:33 2016
From: sasukewxt at 163.com (Caesar Wang)
Date: Sat, 30 Jul 2016 07:13:33 +0800
Subject: [PATCH v3 1/4] iio: adc: rockchip_saradc: reset saradc controller
 before programming it
In-Reply-To: <20160729212833.GA12688@rob-hp-laptop>
References: <1469629447-544-1-git-send-email-wxt@rock-chips.com>
 <20160729212833.GA12688@rob-hp-laptop>
Message-ID: <579BE31D.6020802@163.com>


On 2016?07?30? 05:28, Rob Herring wrote:
> On Wed, Jul 27, 2016 at 10:24:04PM +0800, Caesar Wang wrote:
>> SARADC controller needs to be reset before programming it, otherwise
>> it will not function properly.
>>
>> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
>> Cc: Jonathan Cameron <jic23 at kernel.org>
>> Cc: Heiko Stuebner <heiko at sntech.de>
>> Cc: Rob Herring <robh+dt at kernel.org>
>> Cc: linux-iio at vger.kernel.org
>> Cc: linux-rockchip at lists.infradead.org
>> Tested-by: Guenter Roeck <linux at roeck-us.net>
>>
>> ---
>>
>> Changes in v3:
>> - %s/devm_reset_control_get_optional()/devm_reset_control_get()
>> - add Guente's test tag.
>>
>> Changes in v2:
>> - Make the reset as an optional property, since it should work
>> with old devicetrees as well.
>>
>>   .../bindings/iio/adc/rockchip-saradc.txt           |  7 +++++
>>   drivers/iio/adc/Kconfig                            |  1 +
>>   drivers/iio/adc/rockchip_saradc.c                  | 30 ++++++++++++++++++++++
>>   3 files changed, 38 insertions(+)
> Please add acks when posting new versions.

I didn't get your ACK when posting the V3. :-P

---
Your ack on 2016-07-27 23:12

  .../bindings/iio/adc/rockchip-saradc.txt           |  7 +++++

Acked-by: Rob Herring<robh at kernel.org>



>
> Rob
>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip




From wxt at rock-chips.com  Fri Jul 29 16:21:45 2016
From: wxt at rock-chips.com (Caesar Wang)
Date: Sat, 30 Jul 2016 07:21:45 +0800
Subject: [PATCH v3 1/4] iio: adc: rockchip_saradc: reset saradc controller
 before programming it
In-Reply-To: <alpine.DEB.2.02.1607271642110.22647@pmeerw.net>
References: <1469629447-544-1-git-send-email-wxt@rock-chips.com>
 <alpine.DEB.2.02.1607271642110.22647@pmeerw.net>
Message-ID: <579BE509.8010104@rock-chips.com>


On 2016?07?27? 22:47, Peter Meerwald-Stadler wrote:
>> SARADC controller needs to be reset before programming it, otherwise
>> it will not function properly.
> nitpicking on wording below
>   
>> Signed-off-by: Caesar Wang <wxt at rock-chips.com>
>> Cc: Jonathan Cameron <jic23 at kernel.org>
>> Cc: Heiko Stuebner <heiko at sntech.de>
>> Cc: Rob Herring <robh+dt at kernel.org>
>> Cc: linux-iio at vger.kernel.org
>> Cc: linux-rockchip at lists.infradead.org
>> Tested-by: Guenter Roeck <linux at roeck-us.net>
>>
>> ---
>>
>> Changes in v3:
>> - %s/devm_reset_control_get_optional()/devm_reset_control_get()
>> - add Guente's test tag.
>>
>> Changes in v2:
>> - Make the reset as an optional property, since it should work
>> with old devicetrees as well.
>>
>>   .../bindings/iio/adc/rockchip-saradc.txt           |  7 +++++
>>   drivers/iio/adc/Kconfig                            |  1 +
>>   drivers/iio/adc/rockchip_saradc.c                  | 30 ++++++++++++++++++++++
>>   3 files changed, 38 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt b/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt
>> index bf99e2f..205593f 100644
>> --- a/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt
>> +++ b/Documentation/devicetree/bindings/iio/adc/rockchip-saradc.txt
>> @@ -16,6 +16,11 @@ Required properties:
>>   - vref-supply: The regulator supply ADC reference voltage.
>>   - #io-channel-cells: Should be 1, see ../iio-bindings.txt
>>   
>> +Optional properties:
>> +- resets: Must contain an entry for each entry in reset-names if need support
>> +	  this option. See ../reset/reset.txt for details.
> '... if need support this option.' doesn't sound right, maybe
> simply: '... if needed.' or drop this clause.

I don't plan to resend this series patches.
I'm assuming that Jonathan will help me fix it when ready to apply it.:-)

Glad to resend this series patches if Jonathan boss ask me to do.

Thanks,

Caesar

>
>> +- reset-names: Must include the name "saradc-apb".
>> +
>>   Example:
>>   	saradc: saradc at 2006c000 {
>>   		compatible = "rockchip,saradc";
>> @@ -23,6 +28,8 @@ Example:
>>   		interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
>>   		clocks = <&cru SCLK_SARADC>, <&cru PCLK_SARADC>;
>>   		clock-names = "saradc", "apb_pclk";
>> +		resets = <&cru SRST_SARADC>;
>> +		reset-names = "saradc-apb";
>>   		#io-channel-cells = <1>;
>>   		vref-supply = <&vcc18>;
>>   	};
>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>> index 1de31bd..7675772 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -389,6 +389,7 @@ config QCOM_SPMI_VADC
>>   config ROCKCHIP_SARADC
>>   	tristate "Rockchip SARADC driver"
>>   	depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST)
>> +	depends on RESET_CONTROLLER
>>   	help
>>   	  Say yes here to build support for the SARADC found in SoCs from
>>   	  Rockchip.
>> diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c
>> index f9ad6c2..85d7012 100644
>> --- a/drivers/iio/adc/rockchip_saradc.c
>> +++ b/drivers/iio/adc/rockchip_saradc.c
>> @@ -21,6 +21,8 @@
>>   #include <linux/of_device.h>
>>   #include <linux/clk.h>
>>   #include <linux/completion.h>
>> +#include <linux/delay.h>
>> +#include <linux/reset.h>
>>   #include <linux/regulator/consumer.h>
>>   #include <linux/iio/iio.h>
>>   
>> @@ -53,6 +55,7 @@ struct rockchip_saradc {
>>   	struct clk		*clk;
>>   	struct completion	completion;
>>   	struct regulator	*vref;
>> +	struct reset_control	*reset;
>>   	const struct rockchip_saradc_data *data;
>>   	u16			last_val;
>>   };
>> @@ -190,6 +193,16 @@ static const struct of_device_id rockchip_saradc_match[] = {
>>   };
>>   MODULE_DEVICE_TABLE(of, rockchip_saradc_match);
>>   
>> +/**
>> + * Reset SARADC Controller.
>> + */
>> +static void rockchip_saradc_reset_controller(struct reset_control *reset)
>> +{
>> +	reset_control_assert(reset);
>> +	usleep_range(10, 20);
>> +	reset_control_deassert(reset);
>> +}
>> +
>>   static int rockchip_saradc_probe(struct platform_device *pdev)
>>   {
>>   	struct rockchip_saradc *info = NULL;
>> @@ -218,6 +231,20 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
>>   	if (IS_ERR(info->regs))
>>   		return PTR_ERR(info->regs);
>>   
>> +	/*
>> +	 * The reset should be an optional property, as it should work
>> +	 * with old devicetrees as well
>> +	 */
>> +	info->reset = devm_reset_control_get(&pdev->dev, "saradc-apb");
>> +	if (IS_ERR(info->reset)) {
>> +		ret = PTR_ERR(info->reset);
>> +		if (ret != -ENOENT)
>> +			return ret;
>> +
>> +		dev_dbg(&pdev->dev, "no reset control found\n");
>> +		info->reset = NULL;
>> +	}
>> +
>>   	init_completion(&info->completion);
>>   
>>   	irq = platform_get_irq(pdev, 0);
>> @@ -252,6 +279,9 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
>>   		return PTR_ERR(info->vref);
>>   	}
>>   
>> +	if (info->reset)
>> +		rockchip_saradc_reset_controller(info->reset);
>> +
>>   	/*
>>   	 * Use a default value for the converter clock.
>>   	 * This may become user-configurable in the future.
>>


-- 
caesar wang | software engineer | wxt at rock-chip.com




From briannorris at chromium.org  Fri Jul 29 16:39:51 2016
From: briannorris at chromium.org (Brian Norris)
Date: Fri, 29 Jul 2016 16:39:51 -0700
Subject: [PATCH] arm64: dts: rockchip: add spiX aliases for rk3399
In-Reply-To: <3484113.eKHnDqRbfE@diego>
References: <1468545873-134339-1-git-send-email-briannorris@chromium.org>
 <20160719192912.GA142821@google.com> <1841177.NDa9D09EGz@diego>
 <3484113.eKHnDqRbfE@diego>
Message-ID: <20160729233950.GA102046@google.com>

+ Mark

On Wed, Jul 20, 2016 at 10:11:11AM +0200, Heiko Stuebner wrote:
> Am Mittwoch, 20. Juli 2016, 00:18:40 schrieb Heiko St?bner:
> > Am Dienstag, 19. Juli 2016, 12:29:14 schrieb Brian Norris:
> > > On Tue, Jul 19, 2016 at 12:27:54PM -0700, Brian Norris wrote:
> > > > On Tue, Jul 19, 2016 at 08:56:47PM +0200, Heiko Stuebner wrote:
> > > > > Am Dienstag, 19. Juli 2016, 08:39:55 schrieb Uwe Kleine-K?nig:
> > > > > > > --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> > > > > > > +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
> > > > > > > @@ -70,6 +70,12 @@
> > > > > > > 
> > > > > > >  		serial2 = &uart2;
> > > > > > >  		serial3 = &uart3;
> > > > > > >  		serial4 = &uart4;
> > > > > > > 
> > > > > > > +		spi0 = &spi0;
> > > > > > > +		spi1 = &spi1;
> > > > > > > +		spi2 = &spi2;
> > > > > > > +		spi3 = &spi3;
> > > > > > > +		spi4 = &spi4;
> > > > > > > +		spi5 = &spi5;
> > > > > > 
> > > > > > Note that Rob Herring (with his dt-maintainer hat on) doesn't like
> > > > > > these
> > > > > > aliases.
> > > > > > 
> > > > > > See for example:
> > > > > > 	http://mid.gmane.org/20160705140546.GA10601 at rob-hp-laptop
> > > > 
> > > > But why? I believe half the arguments in the linked thread were
> > > > Catch-22's -- there were no "mainline" users (which is a false target
> > > > IMO anyway, as Christer Weinigel mentioned somewhere in the thread Rob
> > > > linked), and so we couldn't accept more documentation (or users) for the
> > > > feature. FWIW, my quick grep now shows there are currently 43 mainline
> > > > users.
> > > > 
> > > > This feature is very useful. Some of the thread Rob pointed to argued
> > > > that the indexing isn't HW documentation; in this case, it most
> > > > certainly is. The RK3399 TRM explicitly has these SPI buses named SPI0,
> > > > SPI1, SPI2, SPI3, SPI4, and SPI5, and schematics that I've seen use the
> > > > same terminology. It is therefore *much* nicer to have my device show up
> > > > as 'spi2.0' (reflecting their correct HW name) rather than spi32764.0.
> > > > Sometimes I can't even get as far as mounting sysfs to check what this
> > > > maps to, but having spi2.0 in the kernel log can clearly tell me what
> > > > device is causing problems.
> > > > 
> > > > If Rob can support a better alternative solution, I'd be happy to
> > > > switch. But I don't understand why we can't use a useful (not just to
> > > > me, but presumably to at least 43 other independent users, and many
> > > > more out of tree), existing, and long-supported feature here.
> > 
> > Personally, I don't have any real opinion on this but the argument sounds
> > plausible to me (aka they are named with numbers in every manual,
> > schematics) so it might be helpful to have that around - similar to i2c
> > that is in the dtsi already.
> > 
> > I think I remember Doug having a similar discussion around mmc-aliases some
> > time ago - that met opposition as well [0] - although I guess the numbering
> > was a bit more arbitary there.
> > 
> > I guess we'll just see what Rob says.
> 
> Uwe pointed me to a very similar discussion and response from Rob about spi 
> aliases at:
> https://lkml.org/lkml/2016/5/25/566

I had read that already. I figured that was just rationale for not
documenting the feature (still silly IMO), and not for avoiding using
the existing feature.

What is this "label" feature Rob speaks of? Does it exist in practice
(AFAICT the answer is "no")? The description doesn't seem terrible,
depending on the implementation. Would that become the actual device
name (i.e., dev_name(dev))? Or just a filesystem symlink? And how does
one assign such a label?

Brian


From broonie at kernel.org  Fri Jul 29 18:05:36 2016
From: broonie at kernel.org (Mark Brown)
Date: Sat, 30 Jul 2016 02:05:36 +0100
Subject: [PATCH] arm64: dts: rockchip: add spiX aliases for rk3399
In-Reply-To: <20160729233950.GA102046@google.com>
References: <1468545873-134339-1-git-send-email-briannorris@chromium.org>
 <20160719192912.GA142821@google.com> <1841177.NDa9D09EGz@diego>
 <3484113.eKHnDqRbfE@diego> <20160729233950.GA102046@google.com>
Message-ID: <20160730010535.GO10376@sirena.org.uk>

On Fri, Jul 29, 2016 at 04:39:51PM -0700, Brian Norris wrote:

> + Mark

I very nearly deleted this unread based on the subject line...

> I had read that already. I figured that was just rationale for not
> documenting the feature (still silly IMO), and not for avoiding using
> the existing feature.

All in tree aliases provided for SPI buses are strings so if we're doing
anything with aliases we really ought to be parsing them as strings not
numbers (they're people describing the use of buses on their board).  

I quite seriously am more inclined to remove the numbering functionality
as stands than document it as stands given how many layers of
implementation specifics are required to make practical use of it and
the existing practical use for human readable purposes, all we're doing
is trying to be helpful with some random string we found somewhere and
we don't want to encourage people to do that rather than doing what
they're currently doing any providing human readable names.  We
shouldn't preclude being more helpful if we get better ideas and we
shouldn't encourage people to make bad decisions about identifying
things from userspace.  Or I suppose we could go the way of all
standards and document useful behaviour without implementing it but that
doesn't seem awesome either.

> What is this "label" feature Rob speaks of? Does it exist in practice
> (AFAICT the answer is "no")? The description doesn't seem terrible,

It's a property called "label" which is a standard thing in DT for
providing descriptive labels, if you look in git you can see quite a few
examples for other subsystems.  I'd expect to see it show up in the
string for the device name in place of the numbers (both if used for a
device, the bus number for the bus).  Nobody has written any code yet,
I've never personally found the motivation and nobody sent me any
patches.

> depending on the implementation. Would that become the actual device
> name (i.e., dev_name(dev))? Or just a filesystem symlink? And how does
> one assign such a label?

It should really show up in dev_name() since for most devices it'll be
more likely to show up in a log message than anywhere else.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rockchip/attachments/20160730/9c5babf6/attachment.sig>

From ykk at rock-chips.com  Sun Jul 31 18:49:30 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Mon, 1 Aug 2016 09:49:30 +0800
Subject: [PATCH v14 0/17] Add Analogix Core Display Port Driver
In-Reply-To: <CAAObsKAkVd1KC5+MJ4=RyZVsEC1kwsz6V946T1wTkpr8sPeRhA@mail.gmail.com>
References: <1455534485-1154-1-git-send-email-ykk@rock-chips.com>
 <20160331101535.GZ2510@phenom.ffwll.local> <57031D8E.1020903@rock-chips.com>
 <CAAObsKAkVd1KC5+MJ4=RyZVsEC1kwsz6V946T1wTkpr8sPeRhA@mail.gmail.com>
Message-ID: <579EAAAA.200@rock-chips.com>


On 07/29/2016 04:38 PM, Tomeu Vizoso wrote:
> On 5 April 2016 at 04:06, Yakir Yang <ykk at rock-chips.com> wrote:
>> Hi Daniel,
>>
>>
>> On 03/31/2016 06:15 PM, Daniel Vetter wrote:
>>> On Mon, Feb 15, 2016 at 07:08:05PM +0800, Yakir Yang wrote:
>>>> Hi all,
>>>>
>>>>     The Samsung Exynos eDP controller and Rockchip RK3288 eDP controller
>>>> share the same IP, so a lot of parts can be re-used. I split the common
>>>> code into bridge directory, then rk3288 and exynos only need to keep
>>>> some platform code. Cause I can't find the exact IP name of exynos dp
>>>> controller, so I decide to name dp core driver with "analogix" which I
>>>> find in rk3288 eDP TRM
>>>>
>>>> But there are still three light registers setting different between
>>>> exynos and rk3288.
>>>> 1. RK3288 have five special pll registers which not indicate in exynos
>>>>      dp controller.
>>>> 2. The address of DP_PHY_PD(dp phy power manager register) are different
>>>>      between rk3288 and exynos.
>>>> 3. Rk3288 and exynos have different setting with AUX_HW_RETRY_CTL(dp
>>>> debug
>>>>      register).
>>>>
>>>> Due to Mark Yao have introduced the ATOMIC support to Rockchip drm, so
>>>> it's
>>>> okay to use the ATOMIC helpers functions in connector_funcs. No need to
>>>> splict
>>>> the connector init to platform driver anymore, and this is the biggest
>>>> change
>>>> since version 11.
>>>>
>>>> This v14 didn't have lots of new changes which seems not the correct time
>>>> to
>>>> upgrade the version number, but I have changed ordering of patches
>>>> (adding 2
>>>> more, and removing 2 out). Especially to prevent confusing people, so I
>>>> updated
>>>> the whole series.
>>> So I'm jumping into this part way late, but just noticed (well Thierry
>>> pointed this out to me) that the exynos dp driver reinvents all the dp and
>>> dp-aux helpers we already. That's somewhat okish for a private driver (and
>>> exynos has a reputation for that kind of stuff), but imo not ok for a
>>> shared driver.
>>>
>>> Not saying this should block merging this patch, but it really needs to be
>>> addressed. All the dp aux and edid read code in the current
>>> exynos_dp_core/reg.c files needs to be replaced with dp helpers and the
>>> core i2c edid reading code.
>>>
>>> Who's going to sign up to do this?
>>
>> Volunteer to that, after finish this thread, I would send new series to
>> switch to take use of dp helper.
> Hi Yakir,
>
> do you have plans to do this work in the short term? If not, I can tackle it.

Wow, that would be great if you like to tackle it  :-D

I always keep this in my mind, but haven't found an chance
to finish it. I would like to sit in your Cc list when you post
them out  ;)

Best Regards,
- Yakir

> Regards,
>
> Tomeu
>
>> :-D
>> - Yakir
>>
>>
>>> -Daniel
>>>
>>>> Thanks,
>>>> - Yakir
>>>>
>>>>
>>>> Changes in v14:
>>>> - Rebase the new changes in imx-dp driver
>>>> - Split up this patch into 3 parts, make this easy to review (Heiko)
>>>> - Remove the Rockchip DP PHY to an separate thread (Heiko)
>>>>       https://patchwork.kernel.org/patch/8312701/
>>>>
>>>> Changes in v13:
>>>> - Use .enable instead of preprare/commit in encoder_helper_funcs (Heiko)
>>>> - Fix the missing parameters with drm_encoder_init() helper function.
>>>> (Heiko)
>>>>
>>>> Changes in v12:
>>>> - Move the connector init to analogix_dp driver, and using ATOMIC helper
>>>> (Heiko)
>>>> - Add the ack from Jingoo
>>>> - Remove the enum link_rate_type struct, using the marcos in
>>>> drm_dp_helper.h (Jingoo)
>>>>
>>>> Changes in v11:
>>>> - Uses tabs to fix the indentation issues in analogix_dp_core.h (Heiko)
>>>> - Rename the "analogix,need-force-hpd" to common 'force-hpd' (Rob)
>>>> - Add the ack from Rob Herring
>>>> - Revert parts of Gustavo Padovan's changes in commit:
>>>>          drm/exynos: do not start enabling DP at bind() phase
>>>>     Add dp phy poweron function in bind time.
>>>> - Move the panel prepare from get_modes time to bind time, and move
>>>>     the panel unprepare from bridge->disable to unbind time. (Heiko)
>>>>
>>>> Changes in v10:
>>>> - Add the ack from Rob Herring
>>>> - Correct the ROCKCHIP_ANALOGIX_DP indentation in Kconfig to tabs here
>>>> (Heiko)
>>>> - Add the ack from Rob Herring
>>>> - Remove the surplus "plat_data" check. (Heiko)
>>>> -       switch (dp->plat_data && dp->plat_data->dev_type) {
>>>> +       switch (dp->plat_data->dev_type) {
>>>>
>>>> Changes in v9:
>>>> - Document more details for 'ports' property.
>>>>
>>>> Changes in v8:
>>>> - Correct the right document path of display-timing.txt (Heiko)
>>>> - Correct the misspell of 'from' to 'frm'. (Heiko)
>>>> - Modify the commit subject name. (Heiko)
>>>>
>>>> Changes in v7:
>>>> - Back to use the of_property_read_bool() interfacs to provoid backward
>>>>     compatibility of "hsync-active-high" "vsync-active-high" "interlaced"
>>>>     to avoid -EOVERFLOW error (Krzysztof)
>>>>
>>>> Changes in v6:
>>>> - Fix the Kconfig recursive dependency (Javier)
>>>> - Fix Peach Pit hpd property name error:
>>>> -       hpd-gpio = <&gpx2 6 0>;
>>>> +       hpd-gpios = <&gpx2 6 0>;
>>>>
>>>> Changes in v5:
>>>> - Correct the check condition of gpio_is_valid when driver try to get
>>>>     the "hpd-gpios" DT propery. (Heiko)
>>>> - Move the platform attach callback in the front of core driver bridge
>>>>     attch function. Cause once platform failed at attach, core driver
>>>> should
>>>>     still failed, so no need to init connector before platform attached
>>>> (Krzysztof)
>>>> - Keep code style no changes with the previous exynos_dp_code.c in this
>>>>     patch, and update commit message about the new export symbol
>>>> (Krzysztof)
>>>> - Gather the device type patch (v4 11/16) into this one. (Krzysztof)
>>>> - leave out the connector registration to analogix platform driver.
>>>> (Thierry)
>>>> - Resequence this patch after analogix_dp driver have been split
>>>>     from exynos_dp code, and rephrase reasonable commit message, and
>>>>     remove some controversial style (Krzysztof)
>>>>       -          analogix_dp_write_byte_to_dpcd(
>>>>       -                          dp, DP_TEST_RESPONSE,
>>>>       +          analogix_dp_write_byte_to_dpcd(dp,
>>>>       +                          DP_TEST_RESPONSE,
>>>>                                  DP_TEST_EDID_CHECKSUM_WRITE);
>>>> - Switch video timing type to "u32", so driver could use
>>>> "of_property_read_u32"
>>>>     to get the backword timing values. Krzysztof suggest me that driver
>>>> could use
>>>>     the "of_property_read_bool" to get backword timing values, but that
>>>> interfacs
>>>>     would modify the original drm_display_mode timing directly (whether
>>>> those
>>>>     properties exists or not).
>>>> - Correct the misspell in commit message. (Krzysztof)
>>>> - Remove the empty line at the end of document, and correct the endpoint
>>>>     numbers in the example DT node, and remove the regulator iomux setting
>>>>     in driver code while using the pinctl in devicetree instead. (Heiko)
>>>> - Add device type declared, cause the previous "platform device type
>>>>     support (v4 11/16)" already merge into (v5 02/14).
>>>> - Implement connector registration code. (Thierry)
>>>> - Split binding doc's from driver changes. (Rob)
>>>> - Add eDP hotplug pinctrl property. (Heiko)
>>>>
>>>> Changes in v4:
>>>> - Update "analogix,hpd-gpios" to "hpd-gpios" DT propery. (Rob)
>>>> - Rename "analogix_dp-exynos.c" file name to "exynos_dp.c" (Jingoo)
>>>> - Create a separate folder for analogix code in bridge/ (Archit)
>>>> - Update commit message more readable. (Jingoo)
>>>> - Adjust the order from 05 to 04
>>>> - Provide backword compatibility with samsung. (Krzysztof)
>>>> - Split all DTS changes, and provide backward compatibility. Mark old
>>>>     properties as deprecated but still support them. (Krzysztof)
>>>> - Update "analogix,hpd-gpio" to "hpd-gpios" prop name. (Rob)
>>>> - Deprecated some properties which could parsed from Edid/Mode/DPCD.
>>>> (Thierry)
>>>>       "analogix,color-space" & "analogix,color-depth"   &
>>>>       "analogix,link-rate"   & "analogix,lane-count"    &
>>>>       "analogix,ycbcr-coeff" & "analogix,dynamic-range" &
>>>>       "vsync-active-high"    & "hsync-active-high"      & "interlaces"
>>>> - Separate all DTS changes to a separate patch. (Krzysztof)
>>>> - Remove some deprecated DT properties in rockchip dp document.
>>>> - Seprate the link-rate and lane-count limit out with the device_type
>>>>     flag. (Thierry)
>>>> - Take Jingoo suggest, add commit messages.
>>>> - Call drm_panel_prepare() in .get_modes function, ensure panel should
>>>>     power on before driver try to read edid message.
>>>>
>>>> Changes in v3:
>>>> - Move exynos's video_timing code to analogix_dp-exynos platform driver,
>>>>     add get_modes method to struct analogix_dp_plat_data. (Thierry)
>>>> - Rename some "samsung*" dts propery to "analogix*". (Heiko)
>>>> - The link_rate and lane_count shouldn't config to the DT property value
>>>>     directly, but we can take those as hardware limite. For example,
>>>> RK3288
>>>>     only support 4 physical lanes of 2.7/1.62 Gbps/lane, so DT property
>>>> would
>>>>     like "link-rate = 0x0a" "lane-count = 4". (Thierry)
>>>> - Dynamic parse video timing info from struct drm_display_mode and
>>>>     struct drm_display_info. (Thierry)
>>>> - Add devicetree binding documents. (Heiko)
>>>> - Remove sync pol & colorimetry properies from the new analogix dp driver
>>>>     devicetree binding. (Thierry)
>>>> - Update the exist exynos dtsi file with the latest DP DT properies.
>>>> - Leave "sclk_edp_24m" to rockchip dp phy driver which name to "24m",
>>>>     and leave "sclk_edp" to analogix dp core driver which name to "dp",
>>>>     and leave "pclk_edp" to rockchip dp platform driver which name to
>>>>     "pclk". (Thierry & Heiko)
>>>> - Add devicetree binding document. (Heiko)
>>>> - Remove "rockchip,panel" DT property, take use of remote point to get
>>>> panel
>>>>     node. (Heiko)
>>>> - Add the new function point dp_platdata->get_modes() init.
>>>> - Add "analogix,need-force-hpd" to indicate whether driver need foce
>>>>     hpd when hpd detect failed.
>>>> - move dp hpd detect to connector detect function.
>>>> - Add edid modes parse support
>>>>
>>>> Changes in v2:
>>>> - Remove new copyright (Jingoo)
>>>> - Fix compiled failed due to analogix_dp_device misspell
>>>> - Improved commit message more readable, and avoid using some
>>>>     uncommon style like bellow: (Joe Preches)
>>>>       -  retval = exynos_dp_read_bytes_from_i2c(...
>>>>                                    ...);
>>>>       +  retval =
>>>>       +  exynos_dp_read_bytes_from_i2c(......);
>>>> - Get panel node with remote-endpoint method, and create devicetree
>>>> binding
>>>>     for driver. (Heiko)
>>>> - Remove the clock enable/disbale with "sclk_edp" & "sclk_edp_24m",
>>>>     leave those clock to rockchip dp phy driver.
>>>> - Fix compile failed dut to phy_pd_addr variable misspell error
>>>>
>>>> Heiko Stuebner (2):
>>>>     drm/exynos: dp: rename implementation specific driver part
>>>>     drm: bridge: analogix/dp: rename register constants
>>>>
>>>> Yakir Yang (15):
>>>>     drm: bridge: analogix/dp: split exynos dp driver to bridge directory
>>>>     drm: bridge: analogix/dp: fix some obvious code style
>>>>     drm: bridge: analogix/dp: remove duplicate configuration of link rate
>>>>       and link count
>>>>     drm: bridge: analogix/dp: dynamic parse sync_pol & interlace &
>>>>       dynamic_range
>>>>     dt-bindings: add document for analogix display port driver
>>>>     ARM: dts: exynos/dp: remove some properties that deprecated by
>>>>       analogix_dp driver
>>>>     drm: rockchip: dp: add rockchip platform dp driver
>>>>     dt-bindings: add document for rockchip variant of analogix_dp
>>>>     drm: bridge: analogix/dp: add some rk3288 special registers setting
>>>>     drm: bridge: analogix/dp: add max link rate and lane count limit for
>>>>       RK3288
>>>>     drm: bridge: analogix/dp: try force hpd after plug in lookup failed
>>>>     drm: bridge: analogix/dp: move hpd detect to connector detect function
>>>>     drm: bridge: analogix/dp: add edid modes parse in get_modes method
>>>>     drm: bridge: analogix/dp: add panel prepare/unprepare in
>>>>       suspend/resume time
>>>>     drm: bridge: analogix/dp: Fix the possible dead lock in bridge disable
>>>>       time
>>>>
>>>>    .../bindings/display/bridge/analogix_dp.txt        |   52 +
>>>>    .../bindings/display/exynos/exynos_dp.txt          |   93 +-
>>>>    .../display/rockchip/analogix_dp-rockchip.txt      |   92 ++
>>>>    arch/arm/boot/dts/exynos5250-arndale.dts           |    2 -
>>>>    arch/arm/boot/dts/exynos5250-smdk5250.dts          |    2 -
>>>>    arch/arm/boot/dts/exynos5250-snow-common.dtsi      |    4 +-
>>>>    arch/arm/boot/dts/exynos5250-spring.dts            |    4 +-
>>>>    arch/arm/boot/dts/exynos5420-peach-pit.dts         |    4 +-
>>>>    arch/arm/boot/dts/exynos5420-smdk5420.dts          |    2 -
>>>>    arch/arm/boot/dts/exynos5800-peach-pi.dts          |    2 -
>>>>    drivers/gpu/drm/bridge/Kconfig                     |    2 +
>>>>    drivers/gpu/drm/bridge/Makefile                    |    1 +
>>>>    drivers/gpu/drm/bridge/analogix/Kconfig            |    3 +
>>>>    drivers/gpu/drm/bridge/analogix/Makefile           |    1 +
>>>>    drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 1430
>>>> ++++++++++++++++++
>>>>    drivers/gpu/drm/bridge/analogix/analogix_dp_core.h |  281 ++++
>>>>    drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c  | 1320
>>>> +++++++++++++++++
>>>>    .../analogix/analogix_dp_reg.h}                    |  270 ++--
>>>>    drivers/gpu/drm/exynos/Kconfig                     |    3 +-
>>>>    drivers/gpu/drm/exynos/Makefile                    |    2 +-
>>>>    drivers/gpu/drm/exynos/exynos_dp.c                 |  324 +++++
>>>>    drivers/gpu/drm/exynos/exynos_dp_core.c            | 1510
>>>> --------------------
>>>>    drivers/gpu/drm/exynos/exynos_dp_core.h            |  282 ----
>>>>    drivers/gpu/drm/exynos/exynos_dp_reg.c             | 1263
>>>> ----------------
>>>>    drivers/gpu/drm/rockchip/Kconfig                   |    9 +
>>>>    drivers/gpu/drm/rockchip/Makefile                  |    1 +
>>>>    drivers/gpu/drm/rockchip/analogix_dp-rockchip.c    |  384 +++++
>>>>    include/drm/bridge/analogix_dp.h                   |   41 +
>>>>    28 files changed, 4115 insertions(+), 3269 deletions(-)
>>>>    create mode 100644
>>>> Documentation/devicetree/bindings/display/bridge/analogix_dp.txt
>>>>    create mode 100644
>>>> Documentation/devicetree/bindings/display/rockchip/analogix_dp-rockchip.txt
>>>>    create mode 100644 drivers/gpu/drm/bridge/analogix/Kconfig
>>>>    create mode 100644 drivers/gpu/drm/bridge/analogix/Makefile
>>>>    create mode 100644 drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
>>>>    create mode 100644 drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
>>>>    create mode 100644 drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
>>>>    rename drivers/gpu/drm/{exynos/exynos_dp_reg.h =>
>>>> bridge/analogix/analogix_dp_reg.h} (62%)
>>>>    create mode 100644 drivers/gpu/drm/exynos/exynos_dp.c
>>>>    delete mode 100644 drivers/gpu/drm/exynos/exynos_dp_core.c
>>>>    delete mode 100644 drivers/gpu/drm/exynos/exynos_dp_core.h
>>>>    delete mode 100644 drivers/gpu/drm/exynos/exynos_dp_reg.c
>>>>    create mode 100644 drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
>>>>    create mode 100644 include/drm/bridge/analogix_dp.h
>>>>
>>>> --
>>>> 1.9.1
>>>>
>>>>
>>>> _______________________________________________
>>>> dri-devel mailing list
>>>> dri-devel at lists.freedesktop.org
>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel at lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>
>




From shawn.lin at rock-chips.com  Sun Jul 31 20:03:09 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Mon, 1 Aug 2016 11:03:09 +0800
Subject: [PATCH v4 1/2] Documentation: bindings: add dt documentation for
 Rockchip PCIe PHY
In-Reply-To: <20160729213435.GA15549@rob-hp-laptop>
References: <1469755326-10263-1-git-send-email-shawn.lin@rock-chips.com>
 <20160729213435.GA15549@rob-hp-laptop>
Message-ID: <56d82103-1561-9410-d058-3b7a3534490f@rock-chips.com>

? 2016/7/30 5:34, Rob Herring ??:
> On Fri, Jul 29, 2016 at 09:22:05AM +0800, Shawn Lin wrote:
>> This patch adds a binding that describes the Rockchip PCIe PHY
>> found on Rockchip SoCs PCIe interface.
>>
>> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
>>
>> ---
>>
>> Changes in v4: None
>> Changes in v3:
>> - rename the node to pcie_phy: pcie-phy suggested by Doug
>>
>> Changes in v2:
>> - add clk and reset description
>> - remove unit-address
>>
>>  .../devicetree/bindings/phy/rockchip-pcie-phy.txt  | 32 ++++++++++++++++++++++
>>  1 file changed, 32 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/phy/rockchip-pcie-phy.txt
>
> Please add acks when posting new versions.

yup, but I didn't see your ack for my previous version.
Anyway, I will add it if needing to respin one, or Kishon could add
it if all others look good to him when applying these? :)

>
> Rob
>
>
>


-- 
Best Regards
Shawn Lin



From shawn.lin at rock-chips.com  Sun Jul 31 20:10:57 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Mon, 1 Aug 2016 11:10:57 +0800
Subject: [PATCH 0/5] Add power domain support for dw_mmc/sdhci-of-arasan
In-Reply-To: <CAPDyKFq6itpANwnLqgOqKHT0fJH4SvZi2FWrafa=HSBfiZE52w@mail.gmail.com>
References: <1469765703-30999-1-git-send-email-shawn.lin@rock-chips.com>
 <CAPDyKFq6itpANwnLqgOqKHT0fJH4SvZi2FWrafa=HSBfiZE52w@mail.gmail.com>
Message-ID: <81c11db1-f3cf-3eb1-57b0-ede932acd5f7@rock-chips.com>

? 2016/7/29 17:21, Ulf Hansson ??:
> On 29 July 2016 at 06:14, Shawn Lin <shawn.lin at rock-chips.com> wrote:
>>
>> We need to enable the power domain manually while probing as the
>> power policy will turn off the pd which is not referenced. Otherwise
>
> I assume you are going to use the generic PM domain (genpd)?

yes.

> If so, this statement isn't entirely correct.
>
> As a matter of fact, the PM domain will be "powered on" once you have
> registered your PM domain with genpd and when the device (dw_mmc etc)
> gets attached to the domain. The attach occurs when the platform bus'
> ->probe() function get invoked for the device.
>
> Genpd monitors each device within the domain. To allow it to "power
> off", all devices in same domain needs to be runtime PM enabled and
> runtime PM suspended.

I will check this with my PM guys as I have limited knowledge about
PM stuff. Thanks for these great info.


So, if my real intention is to just enable genpd to make sure it will
be in "power on " stat, I just need to update my DTS/DTB. That would be
great.:)

>
> Due to this, you should be able to deploy genpd support+an updated
> DTB, without first having to deploying runtime PM support in the
> related drivers. In other words, you should be able to work on these
> things almost independently of each other.

sure.

>
>> we may take risk of breaking kernel once Feng Xiao land the rockchip pd
>> relate stuff. So, let's do it in advance.
>>
>> This pachset is based on the latest linux-next[1] which should contain
>> the next branch of linux-mmc and dt stuff. It's ideal to pack these all
>> into Ulf' tree with Heiko's ack for dts, Rob's ack for Doc and Jaehoon's
>> ack for dw_mmc. But it's okay to be merged seperately.
>>
>> [1] git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
>>
>> Thanks.
>>
>>
>>
>> Shawn Lin (5):
>>   Documentation: mmc: synopsys-dw-mshc: add power domain description
>>   Documentation: mmc: sdhci-of-arasan: add power domain description
>
> To me this isn't necessary to describe for each device. I think
> Documentation/devicetree/bindings/power/power_domain.txt describes all
> that is needed.
>
>>   arm64: dts: rockchip: Provide power-domains for sd/sdio/emmc
>>     controllers
>>   mmc: dw_mmc: add power domain support
>>   mmc: sdhci-of-arasan: add power domain support
>
> I would rephrase these commit message headers, to something like
> "Deploy runtime PM support", as that is actually what you intend to
> do.
>
>>
>>  Documentation/devicetree/bindings/mmc/arasan,sdhci.txt     |  1 +
>>  Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt |  2 ++
>>  arch/arm64/boot/dts/rockchip/rk3399.dtsi                   |  3 +++
>>  drivers/mmc/host/dw_mmc.c                                  | 11 +++++++++++
>>  drivers/mmc/host/sdhci-of-arasan.c                         | 10 ++++++++++
>>  5 files changed, 27 insertions(+)
>>
>> --
>> 2.3.7
>>
>>
>
> Kind regards
> Uffe
>
>
>


-- 
Best Regards
Shawn Lin



From shawn.lin at rock-chips.com  Sun Jul 31 20:19:48 2016
From: shawn.lin at rock-chips.com (Shawn Lin)
Date: Mon, 1 Aug 2016 11:19:48 +0800
Subject: [PATCH v8 2/2] PCI: Rockchip: Add Rockchip PCIe controller support
In-Reply-To: <20160729164847.GB23928@localhost>
References: <1469760493-25821-1-git-send-email-shawn.lin@rock-chips.com>
 <1469760493-25821-2-git-send-email-shawn.lin@rock-chips.com>
 <20160729164847.GB23928@localhost>
Message-ID: <33cc6bf3-ad88-d1b2-10c9-b6a94b6cc0e0@rock-chips.com>

Hi Bjorn,

On 2016/7/30 0:48, Bjorn Helgaas wrote:
> Hi Shawn,
>
> You fixed some of the things I mentioned, but not all, and you
> over-fixed some things, leading to issues.
>
> On Fri, Jul 29, 2016 at 10:48:13AM +0800, Shawn Lin wrote:
>> This patch adds Rockchip PCIe controller support found
>> on RK3399 Soc platform.
>>
>> Signed-off-by: Shawn Lin <shawn.lin at rock-chips.com>
>>
>> Reviewed-by: Brian Norris <briannorris at chromium.org>
>> ---
>>
>> Changes in v8:
>> - fix all the comments suggested by Bjorn Helgaas in v7[1]
>>   [1] https://patchwork.kernel.org/patch/9233887/
>> - enable bandwith interrupt for debug
>>
>> Changes in v7:
>> - make it as a build-in driver
>> - improve gen1/2 training timeout checking
>> - only clear known interrupt
>> - fix INTx for 0-base index
>>
>> Changes in v6:
>> - use "depends on PCI_MSI_IRQ_DOMAIN" suggested by Arnd
>>
>> Changes in v5:
>> - handle multiple pending INTx at the same time
>>   suggested by Marc
>>
>> Changes in v4:
>> - address the comments from Brain
>>
>> Changes in v3:
>> - remove header file
>> - remove struct msi_controller and move most of variables
>>   of rockchip_pcie_port to become the local ones.
>> - allow legacy int even if enabling MSI
>> - drop regulator set voltage operation suggested by Doug
>>
>> Changes in v2:
>> - remove phy related stuff and call phy API
>> - add new head file and define lots of macro to make
>>   the code more readable
>> - remove lots msi related code suggested by Marc
>> - add IO window address translation
>> - init_port and parse_dt reconstruction suggested by Bharat
>> - improve wr_own_conf suggested by Arnd
>> - make pcie as an interrupt controller and fix wrong int handler
>>   suggested by Marc
>> - remove PCI_PROBE_ONLY suggested by Lorenzo
>>
>>  drivers/pci/host/Kconfig         |   11 +
>>  drivers/pci/host/Makefile        |    1 +
>>  drivers/pci/host/pcie-rockchip.c | 1257 ++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 1269 insertions(+)
>>  create mode 100644 drivers/pci/host/pcie-rockchip.c
>>
>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>> index c917e2f..4d714d2 100644
>> --- a/drivers/pci/host/Kconfig
>> +++ b/drivers/pci/host/Kconfig
>> @@ -264,4 +264,15 @@ config PCIE_ARTPEC6
>>  	  Say Y here to enable PCIe controller support on Axis ARTPEC-6
>>  	  SoCs.  This PCIe controller uses the DesignWare core.
>>
>> +config PCIE_ROCKCHIP
>> +	bool "Rockchip PCIe controller"
>> +	depends on ARM64 && ARCH_ROCKCHIP
>> +	depends on OF
>> +	depends on PCI_MSI_IRQ_DOMAIN
>> +	select MFD_SYSCON
>> +	help
>> +	  Say Y here if you want internal PCI support on Rockchip SoC.
>> +	  There is 1 internal PCIe port available to support GEN2 with
>> +	  4 slots.
>> +
>>  endmenu
>> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
>> index 5bc0af2..695f716 100644
>> --- a/drivers/pci/host/Makefile
>> +++ b/drivers/pci/host/Makefile
>> @@ -30,3 +30,4 @@ obj-$(CONFIG_PCI_HOST_THUNDER_ECAM) += pci-thunder-ecam.o
>>  obj-$(CONFIG_PCI_HOST_THUNDER_PEM) += pci-thunder-pem.o
>>  obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o
>>  obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o
>> +obj-$(CONFIG_PCIE_ROCKCHIP) += pcie-rockchip.o
>> diff --git a/drivers/pci/host/pcie-rockchip.c b/drivers/pci/host/pcie-rockchip.c
>> new file mode 100644
>> index 0000000..13ac199
>> --- /dev/null
>> +++ b/drivers/pci/host/pcie-rockchip.c
>> @@ -0,0 +1,1257 @@
>> +/*
>> + * Rockchip AXI PCIe host controller driver
>> + *
>> + * Copyright (c) 2016 Rockchip, Inc.
>> + *
>> + * Author: Shawn Lin <shawn.lin at rock-chips.com>
>> + *         Wenrui Li <wenrui.li at rock-chips.com>
>> + *
>> + * Bits taken from Synopsys Designware Host controller driver and
>> + * ARM PCI Host generic driver.
>> + *
>> + * This program is free software: you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation, either version 2 of the License, or
>> + * (at your option) any later version.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/gpio/consumer.h>
>> +#include <linux/init.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/irq.h>
>> +#include <linux/irqchip/chained_irq.h>
>> +#include <linux/irqdomain.h>
>> +#include <linux/kernel.h>
>> +#include <linux/mfd/syscon.h>
>> +#include <linux/of_address.h>
>> +#include <linux/of_device.h>
>> +#include <linux/of_pci.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/of_irq.h>
>> +#include <linux/pci.h>
>> +#include <linux/pci_ids.h>
>> +#include <linux/phy/phy.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/reset.h>
>> +#include <linux/regmap.h>
>> +
>
> Indent all the following constants with tabs, not spaces.

ok, will fix.

>
>> +#define PCIE_CLIENT_BASE                   0x0
>> +#define PCIE_RC_CONFIG_NORMAL_BASE         0x800000
>> +#define PCIE_RC_CONFIG_BASE                0xa00000
>
>> +#define PCIE_RC_CONFIG_LCSR_OFFSET         0xd0
>> +#define PCIE_RC_CONFIG_LCSR_BW_ENA         0x1
>> +#define PCIE_RC_CONFIG_LCSR_LBMIE          BIT(10)
>> +#define PCIE_RC_CONFIG_LCSR_LABIE          BIT(11)
>> +#define PCIE_RC_CONFIG_LCSR_LBMS           BIT(30)
>> +#define PCIE_RC_CONFIG_LCSR_LAMS           BIT(31)
>
> This section looks like the offset and fields in one register (LCSR).
>
> 1) Remove "_OFFSET".  You don't use it on similar registers below,
> e.g., IE_CORE_OB_REGION_ADDR1.
>

sure.

> 2) Indent the field definitions a space or two to show the association
> with the register name, e.g.,
>
>   #define PCIE_RC_CONFIG_LCSR                0xd0
>   #define   PCIE_RC_CONFIG_LCSR_BW_ENA       0x1

I will amend it and the similar cases below.

>
> 3) Make the field definition the same width as the register.  In this
> case, you use readl, so it would be:
>
>   #define   PCIE_RC_CONFIG_LCSR_BW_ENA       0x00000001

I think this one isn't used, so I remove it for the next version.

>
> 4) Use the same type of definition (plain hex value vs. BIT()) for all
> fields in a register.  BIT(x) likely maps to your documentation, but
> my personal preference is the plain hex value because it's simpler to
> debug, it shows the register width, and it works nicely for multi-bit
> fields.  I guess GENMASK() is one way to handle multi-bit fields, too.
> Whichever you choose, use it consistently.

Yup, I will use BIT(X) consistenly

>
>> +#define PCIE_CORE_LINK_CTRL_STATUS         0x8000d0
>
> Unused, remove.
>
>> +#define PCIE_CORE_CTRL_MGMT_BASE           0x900000
>> +#define PCIE_CORE_AXI_CONF_BASE            0xc00000
>> +#define PCIE_CORE_AXI_INBOUND_BASE         0xc00800
>> +#define PCIE_CLIENT_BASIC_STATUS0          0x44
>
> Unused, remove.

sure.

>
>> +#define PCIE_CLIENT_BASIC_STATUS1          0x48
>> +#define PCIE_CLIENT_INT_MASK               0x4c
>> +#define PCIE_CLIENT_INT_STATUS             0x50
>> +#define PCIE_CLIENT_INT_LEGACY_DONE        BIT(15)
>> +#define PCIE_CLIENT_INT_MSG                BIT(14)
>> +#define PCIE_CLIENT_INT_HOT_RST            BIT(13)
>> +#define PCIE_CLIENT_INT_DPA                BIT(12)
>> +#define PCIE_CLIENT_INT_FATAL_ERR          BIT(11)
>> +#define PCIE_CLIENT_INT_NFATAL_ERR         BIT(10)
>> +#define PCIE_CLIENT_INT_CORR_ERR           BIT(9)
>> +#define PCIE_CLIENT_INT_INTD               BIT(8)
>> +#define PCIE_CLIENT_INT_INTC               BIT(7)
>> +#define PCIE_CLIENT_INT_INTB               BIT(6)
>> +#define PCIE_CLIENT_INT_INTA               BIT(5)
>> +#define PCIE_CLIENT_INT_LOCAL              BIT(4)
>> +#define PCIE_CLIENT_INT_UDMA               BIT(3)
>> +#define PCIE_CLIENT_INT_PHY                BIT(2)
>> +#define PCIE_CLIENT_INT_HOT_PLUG           BIT(1)
>> +#define PCIE_CLIENT_INT_PWR_STCG           BIT(0)
>> +#define PCIE_RC_CONFIG_RID_CCR             0x8
>> +#define PCIE_RC_CONFIG_LCS                 0xd0
>> +#define PCIE_RC_BAR_CONF                   0x300
>> +#define PCIE_CORE_OB_REGION_ADDR1          0x4
>> +#define PCIE_CORE_OB_REGION_DESC0          0x8
>> +#define PCIE_CORE_OB_REGION_DESC1          0xc
>> +#define PCIE_CORE_OB_REGION_ADDR0_NUM_BITS 0x3fU
>
> Why do some of these constants have "U" and others not?  I don't think
> they're needed.

Will fix.

>
>> +#define PCIE_CORE_OB_REGION_ADDR0_LO_ADDR  0xffffff00U
>> +#define PCIE_CORE_IB_REGION_ADDR0_NUM_BITS 0x3fU
>> +#define PCIE_CORE_IB_REGION_ADDR0_LO_ADDR  0xffffff00U
>> +#define PCIE_RP_IB_ADDR_TRANS              0x4
>> +#define PCIE_CORE_INT_MASK                 0x900210
>> +#define PCIE_CORE_INT_STATUS               0x90020c
>> +#define PCIE_CORE_INT_PRFPE                BIT(0)
>> +#define PCIE_CORE_INT_CRFPE                BIT(1)
>> +#define PCIE_CORE_INT_RRPE                 BIT(2)
>> +#define PCIE_CORE_INT_PRFO                 BIT(3)
>> +#define PCIE_CORE_INT_CRFO                 BIT(4)
>> +#define PCIE_CORE_INT_RT                   BIT(5)
>> +#define PCIE_CORE_INT_RTR                  BIT(6)
>> +#define PCIE_CORE_INT_PE                   BIT(7)
>> +#define PCIE_CORE_INT_MTR                  BIT(8)
>> +#define PCIE_CORE_INT_UCR                  BIT(9)
>> +#define PCIE_CORE_INT_FCE                  BIT(10)
>> +#define PCIE_CORE_INT_CT                   BIT(11)
>> +#define PCIE_CORE_INT_UTC                  BIT(18)
>> +#define PCIE_CORE_INT_MMVC                 BIT(19)
>> +
>> +/* Size of one AXI Region (not Region 0) */
>> +#define AXI_REGION_SIZE                    BIT(20)
>> +/* Size of Region 0, equal to sum of sizes of other regions */
>> +#define AXI_REGION_0_SIZE                 (32 * (0x1 << 20))
>> +#define OB_REG_SIZE_SHIFT                  5
>> +#define IB_ROOT_PORT_REG_SIZE_SHIFT        3
>> +#define AXI_WRAPPER_IO_WRITE               0x6
>> +#define AXI_WRAPPER_MEM_WRITE              0x2
>
> This could do with a blank line.  The below are not directly
> associated with the registers above.

Done.

>
>> +#define MAX_AXI_IB_ROOTPORT_REGION_NUM     3
>> +#define MIN_AXI_ADDR_BITS_PASSED           8
>> +#define ROCKCHIP_VENDOR_ID                 0x1d87
>> +#define PCIE_ECAM_BUS(x)		  (((x) & 0xff) << 20)
>> +#define PCIE_ECAM_DEV(x)		  (((x) & 0x1f) << 15)
>> +#define PCIE_ECAM_FUNC(x)		  (((x) & 0x7) << 12)
>> +#define PCIE_ECAM_REG(x)		  (((x) & 0xfff) << 0)
>> +#define PCIE_ECAM_ADDR(bus, dev, func, reg) \
>> +	  (PCIE_ECAM_BUS(bus) | PCIE_ECAM_DEV(dev) | \
>> +	   PCIE_ECAM_FUNC(func) | PCIE_ECAM_REG(reg))
>> +
>> +/*
>> +  * The higher 16-bit of this register is used for write protection
>> +  * only if BIT(x + 16) set to 1 the BIT(x) can be written.
>> +  */
>> +#define HIWORD_UPDATE(val, mask, shift) \
>> +	((val) << (shift) | (mask) << ((shift) + 16))
>> +
>> +#define RC_REGION_0_ADDR_TRANS_H          0x00000000
>> +#define RC_REGION_0_ADDR_TRANS_L          0x00000000
>> +#define RC_REGION_0_PASS_BITS             (25 - 1)
>> +#define RC_REGION_1_ADDR_TRANS_H          0x00000000
>> +#define RC_REGION_1_ADDR_TRANS_L          0x00400000
>> +#define RC_REGION_1_PASS_BITS             (20 - 1)
>> +#define MAX_AXI_WRAPPER_REGION_NUM        33
>> +#define PCIE_CORE_LCSR_RETRAIN_LINK       BIT(5)
>> +#define PCIE_CLIENT_CONF_ENABLE           1
>> +#define PCIE_CLIENT_CONF_ENABLE_SHIFT     0
>> +#define PCIE_CLIENT_CONF_ENABLE_MASK      0x1
>> +#define PCIE_CLIENT_LINK_TRAIN_ENABLE     1
>> +#define PCIE_CLIENT_LINK_TRAIN_SHIFT      1
>> +#define PCIE_CLIENT_LINK_TRAIN_MASK       0x1
>> +#define PCIE_CLIENT_ARI_ENABLE            1
>> +#define PCIE_CLIENT_ARI_ENABLE_SHIFT      3
>> +#define PCIE_CLIENT_ARI_ENABLE_MASK       0x1
>> +#define PCIE_CLIENT_CONF_LANE_NUM(x)      (x / 2)
>> +#define PCIE_CLIENT_CONF_LANE_NUM_SHIFT   4
>> +#define PCIE_CLIENT_CONF_LANE_NUM_MASK    0x3
>> +#define PCIE_CLIENT_MODE_RC               1
>> +#define PCIE_CLIENT_MODE_SHIFT            6
>> +#define PCIE_CLIENT_MODE_MASK             0x1
>> +#define PCIE_CLIENT_GEN_SEL_2             1
>> +#define PCIE_CLIENT_GEN_SEL_1             0
>> +#define PCIE_CLIENT_GEN_SEL_SHIFT         7
>> +#define PCIE_CLIENT_GEN_SEL_MASK          0x1
>> +#define PCIE_CLIENT_LINK_STATUS_UP        0x3
>> +#define PCIE_CLIENT_LINK_STATUS_SHIFT     20
>> +#define PCIE_CLIENT_LINK_STATUS_MASK      0x3
>> +#define PCIE_CORE_PL_CONF_SPEED_2_5G      0x0
>> +#define PCIE_CORE_PL_CONF_SPEED_5G        0x1
>> +#define PCIE_CORE_PL_CONF_SPEED_8G        0x2
>> +#define PCIE_CORE_PL_CONF_SPEED_SHIFT     3
>> +#define PCIE_CORE_PL_CONF_SPEED_MASK      0x3
>> +#define PCIE_CORE_PL_CONF_LANE_SHIFT      1
>> +#define PCIE_CORE_PL_CONF_LANE_MASK       0x3
>> +#define PCIE_CORE_RC_CONF_SCC_SHIFT       16
>> +
>> +#define ROCKCHIP_PCIE_RPIFR1_INTR_MASK    GENMASK(8, 5)
>> +#define ROCKCHIP_PCIE_RPIFR1_INTR_SHIFT   5
>> +
>> +#define PCIE_CORE_INT \
>> +		(PCIE_CORE_INT_PRFPE | PCIE_CORE_INT_CRFPE | \
>> +		 PCIE_CORE_INT_RRPE | PCIE_CORE_INT_CRFO | \
>> +		 PCIE_CORE_INT_RT | PCIE_CORE_INT_RTR | \
>> +		 PCIE_CORE_INT_PE | PCIE_CORE_INT_MTR | \
>> +		 PCIE_CORE_INT_UCR | PCIE_CORE_INT_FCE | \
>> +		 PCIE_CORE_INT_CT | PCIE_CORE_INT_UTC | \
>> +		 PCIE_CORE_INT_MMVC)
>> +
>> +#define PCIE_CLIENT_INT_SUBSYSTEM \
>> +	(PCIE_CLIENT_INT_PWR_STCG | PCIE_CLIENT_INT_HOT_PLUG | \
>> +	PCIE_CLIENT_INT_PHY | PCIE_CLIENT_INT_UDMA | \
>> +	PCIE_CLIENT_INT_LOCAL)
>> +
>> +#define PCIE_CLIENT_INT_LEGACY \
>> +	(PCIE_CLIENT_INT_INTA | PCIE_CLIENT_INT_INTB | \
>> +	PCIE_CLIENT_INT_INTC | PCIE_CLIENT_INT_INTD)
>> +
>> +#define PCIE_CLIENT_INT_CLI \
>> +	(PCIE_CLIENT_INT_CORR_ERR | PCIE_CLIENT_INT_NFATAL_ERR | \
>> +	PCIE_CLIENT_INT_FATAL_ERR | PCIE_CLIENT_INT_DPA | \
>> +	PCIE_CLIENT_INT_HOT_RST | PCIE_CLIENT_INT_MSG | \
>> +	PCIE_CLIENT_INT_LEGACY_DONE | PCIE_CLIENT_INT_LEGACY | \
>> +	PCIE_CLIENT_INT_PHY)
>> +
>> +struct rockchip_pcie_port {
>> +	void	__iomem *reg_base;
>> +	void	__iomem *apb_base;
>> +	struct	phy *phy;
>> +	struct	reset_control *core_rst;
>> +	struct	reset_control *mgmt_rst;
>> +	struct	reset_control *mgmt_sticky_rst;
>> +	struct	reset_control *pipe_rst;
>> +	struct	clk *aclk_pcie;
>> +	struct	clk *aclk_perf_pcie;
>> +	struct	clk *hclk_pcie;
>> +	struct	clk *clk_pcie_pm;
>> +	struct	regulator *vpcie3v3; /* 3.3V power supply */
>> +	struct	regulator *vpcie1v8; /* 1.8V power supply */
>> +	struct	regulator *vpcie0v9; /* 0.9V power supply */
>> +	struct	gpio_desc *ep_gpio;
>> +	u32	lanes;
>> +	u8	root_bus_nr;
>> +	struct	device *dev;
>> +	struct	irq_domain *irq_domain;
>> +};
>> +
>> +static inline u32 pcie_read(struct rockchip_pcie_port *port, u32 reg)
>> +{
>> +	return readl(port->apb_base + reg);
>> +}
>> +
>> +static inline void pcie_write(struct rockchip_pcie_port *port,
>> +			      u32 val, u32 reg)
>> +{
>> +	writel(val, port->apb_base + reg);
>> +}
>> +
>> +static void rockchip_pcie_enable_bw_int(struct rockchip_pcie_port *port)
>> +{
>> +	u32 status;
>> +
>> +	status = pcie_read(port, PCIE_RC_CONFIG_BASE +
>> +				 PCIE_RC_CONFIG_LCSR_OFFSET);
>> +	status |= (PCIE_RC_CONFIG_LCSR_LBMIE | PCIE_RC_CONFIG_LCSR_LABIE);
>> +	pcie_write(port, status, PCIE_RC_CONFIG_BASE +
>> +				 PCIE_RC_CONFIG_LCSR_OFFSET);
>> +}
>> +
>> +static void rockchip_pcie_clr_bw_int(struct rockchip_pcie_port *port)
>> +{
>> +	u32 status;
>> +
>> +	status = pcie_read(port, PCIE_RC_CONFIG_BASE +
>> +				 PCIE_RC_CONFIG_LCSR_OFFSET);
>> +	status |= (PCIE_RC_CONFIG_LCSR_LBMS | PCIE_RC_CONFIG_LCSR_LAMS);
>> +	pcie_write(port, status, PCIE_RC_CONFIG_BASE +
>> +				 PCIE_RC_CONFIG_LCSR_OFFSET);
>> +}
>> +
>> +static int rockchip_pcie_valid_config(struct rockchip_pcie_port *pp,
>> +				      struct pci_bus *bus, int dev)
>> +{
>> +	/* access only one slot on each root port */
>> +	if (bus->number == pp->root_bus_nr && dev > 0)
>> +		return 0;
>> +
>> +	/*
>> +	 * do not read more than one device on the bus directly attached
>> +	 * to RC's downstream side.
>> +	 */
>> +	if (bus->primary == pp->root_bus_nr && dev > 0)
>> +		return 0;
>> +
>> +	return 1;
>> +}
>> +
>> +static int rockchip_pcie_rd_own_conf(struct rockchip_pcie_port *pp,
>> +				     int where, int size,
>> +				     u32 *val)
>> +{
>> +	void __iomem *addr = pp->apb_base + PCIE_RC_CONFIG_BASE + where;
>> +
>> +	if (!IS_ALIGNED((uintptr_t)addr, size)) {
>> +		*val = 0;
>> +		return PCIBIOS_BAD_REGISTER_NUMBER;
>> +	}
>> +
>> +	if (size == 4) {
>> +		*val = readl(addr);
>> +	} else if (size == 2) {
>> +		*val = readw(addr);
>> +	} else if (size == 1) {
>> +		*val = readb(addr);
>> +	} else {
>> +		*val = 0;
>> +		return PCIBIOS_BAD_REGISTER_NUMBER;
>> +	}
>> +	return PCIBIOS_SUCCESSFUL;
>> +}
>> +
>> +static int rockchip_pcie_wr_own_conf(struct rockchip_pcie_port *pp,
>> +				     int where, int size, u32 val)
>> +{
>> +	u32 mask, tmp, offset;
>> +
>> +	offset = (where & (~0x3));
>> +
>> +	if (size == 4) {
>> +		writel(val, pp->apb_base + PCIE_RC_CONFIG_BASE + offset);
>> +		return PCIBIOS_SUCCESSFUL;
>> +	}
>> +
>> +	mask = ~(((1 << (size * 8)) - 1) << ((where & 0x3) * 8));
>> +
>> +	tmp = readl(pp->apb_base + PCIE_RC_CONFIG_BASE + offset) & mask;
>> +	tmp |= val << ((where & 0x3) * 8);
>> +	writel(tmp, pp->apb_base + PCIE_RC_CONFIG_BASE + offset);
>> +
>> +	return PCIBIOS_SUCCESSFUL;
>> +}
>> +
>> +static int rockchip_pcie_rd_other_conf(struct rockchip_pcie_port *pp,
>> +				       struct pci_bus *bus, u32 devfn,
>> +				       int where, int size, u32 *val)
>> +{
>> +	u32 busdev;
>> +
>> +	busdev = PCIE_ECAM_ADDR(bus->number, PCI_SLOT(devfn),
>> +				PCI_FUNC(devfn), where);
>> +
>> +	if (!IS_ALIGNED(busdev, size)) {
>> +		*val = 0;
>> +		return PCIBIOS_BAD_REGISTER_NUMBER;
>> +	}
>> +
>> +	if (size == 4) {
>> +		*val = readl(pp->reg_base + busdev);
>> +	} else if (size == 2) {
>> +		*val = readw(pp->reg_base + busdev);
>> +	} else if (size == 1) {
>> +		*val = readb(pp->reg_base + busdev);
>> +	} else {
>> +		*val = 0;
>> +		return PCIBIOS_BAD_REGISTER_NUMBER;
>> +	}
>> +	return PCIBIOS_SUCCESSFUL;
>> +}
>> +
>> +static int rockchip_pcie_wr_other_conf(struct rockchip_pcie_port *pp,
>> +				       struct pci_bus *bus, u32 devfn,
>> +				       int where, int size, u32 val)
>> +{
>> +	u32 busdev;
>> +
>> +	busdev = PCIE_ECAM_ADDR(bus->number, PCI_SLOT(devfn),
>> +				PCI_FUNC(devfn), where);
>> +	if (!IS_ALIGNED(busdev, size))
>> +		return PCIBIOS_BAD_REGISTER_NUMBER;
>> +
>> +	if (size == 4)
>> +		writel(val, pp->reg_base + busdev);
>> +	else if (size == 2)
>> +		writew(val, pp->reg_base + busdev);
>> +	else if (size == 1)
>> +		writeb(val, pp->reg_base + busdev);
>> +	else
>> +		return PCIBIOS_BAD_REGISTER_NUMBER;
>> +
>> +	return PCIBIOS_SUCCESSFUL;
>> +}
>> +
>> +static int rockchip_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
>> +				 int size, u32 *val)
>> +{
>> +	struct rockchip_pcie_port *pp = bus->sysdata;
>> +
>> +	if (rockchip_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) {
>> +		*val = 0xffffffff;
>> +		return PCIBIOS_DEVICE_NOT_FOUND;
>> +	}
>> +
>> +	if (bus->number != pp->root_bus_nr)
>> +		return rockchip_pcie_rd_other_conf(pp, bus, devfn,
>> +						  where, size, val);
>> +
>> +	return rockchip_pcie_rd_own_conf(pp, where, size, val);
>
> You only did this half-way:
>
> 1) Do this instead to avoid the negation:
>
>   if (bus->number == pp->root_bus_nr)
>     return rockchip_pcie_rd_own_conf(pp, where, size, val);
>
>   return rockchip_pcie_rd_other_conf(pp, bus, devfn, ...);
>
> 2) Apply the same change to rockchip_pcie_wr_conf() below.

Sure.

>
>> +}
>> +
>> +static int rockchip_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
>> +				 int where, int size, u32 val)
>> +{
>> +	struct rockchip_pcie_port *pp = bus->sysdata;
>> +	int ret;
>> +
>> +	if (rockchip_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0)
>> +		return PCIBIOS_DEVICE_NOT_FOUND;
>> +
>> +	if (bus->number != pp->root_bus_nr)
>> +		ret = rockchip_pcie_wr_other_conf(pp, bus, devfn,
>> +						  where, size, val);
>> +	else
>> +		ret = rockchip_pcie_wr_own_conf(pp, where, size, val);
>> +
>> +	return ret;
>> +}
>> +
>> +static struct pci_ops rockchip_pcie_ops = {
>> +	.read = rockchip_pcie_rd_conf,
>> +	.write = rockchip_pcie_wr_conf,
>> +};
>> +
>> +/**
>> + * rockchip_pcie_init_port - Initialize hardware
>> + * @port: PCIe port information
>> + */
>> +static int rockchip_pcie_init_port(struct rockchip_pcie_port *port)
>> +{
>> +	int err;
>> +	u32 status;
>> +	unsigned long timeout;
>> +
>> +	gpiod_set_value(port->ep_gpio, 0);
>> +
>> +	err = phy_init(port->phy);
>> +	if (err < 0) {
>> +		dev_err(port->dev, "fail to init phy, err %d\n", err);
>> +		return err;
>> +	}
>> +
>> +	err = reset_control_assert(port->core_rst);
>> +	if (err) {
>> +		dev_err(port->dev, "assert core_rst err %d\n", err);
>> +		return err;
>> +	}
>> +
>> +	err = reset_control_assert(port->mgmt_rst);
>> +	if (err) {
>> +		dev_err(port->dev, "assert mgmt_rst err %d\n", err);
>> +		return err;
>> +	}
>> +
>> +	err = reset_control_assert(port->mgmt_sticky_rst);
>> +	if (err) {
>> +		dev_err(port->dev, "assert mgmt_sticky_rst err %d\n", err);
>> +		return err;
>> +	}
>> +
>> +	err = reset_control_assert(port->pipe_rst);
>> +	if (err) {
>> +		dev_err(port->dev, "assert pipe_rst err %d\n", err);
>> +		return err;
>> +	}
>> +
>> +	pcie_write(port,
>> +		   HIWORD_UPDATE(PCIE_CLIENT_CONF_ENABLE,
>> +				 PCIE_CLIENT_CONF_ENABLE_MASK,
>> +				 PCIE_CLIENT_CONF_ENABLE_SHIFT) |
>> +		   HIWORD_UPDATE(PCIE_CLIENT_CONF_LANE_NUM(port->lanes),
>> +				 PCIE_CLIENT_CONF_LANE_NUM_MASK,
>> +				 PCIE_CLIENT_CONF_LANE_NUM_SHIFT) |
>> +		   HIWORD_UPDATE(PCIE_CLIENT_MODE_RC,
>> +				 PCIE_CLIENT_MODE_MASK,
>> +				 PCIE_CLIENT_MODE_SHIFT) |
>> +		   HIWORD_UPDATE(PCIE_CLIENT_ARI_ENABLE,
>> +				 PCIE_CLIENT_ARI_ENABLE_MASK,
>> +				 PCIE_CLIENT_ARI_ENABLE_SHIFT) |
>> +		   HIWORD_UPDATE(PCIE_CLIENT_GEN_SEL_2,
>> +				 PCIE_CLIENT_GEN_SEL_MASK,
>> +				 PCIE_CLIENT_GEN_SEL_SHIFT),
>> +		   PCIE_CLIENT_BASE);
>> +
>> +	err = phy_power_on(port->phy);
>> +	if (err) {
>> +		dev_err(port->dev, "fail to power on phy, err %d\n", err);
>> +		return err;
>> +	}
>> +
>> +	err = reset_control_deassert(port->core_rst);
>> +	if (err) {
>> +		dev_err(port->dev, "deassert core_rst err %d\n", err);
>> +		return err;
>> +	}
>> +
>> +	err = reset_control_deassert(port->mgmt_rst);
>> +	if (err) {
>> +		dev_err(port->dev, "deassert mgmt_rst err %d\n", err);
>> +		return err;
>> +	}
>> +
>> +	err = reset_control_deassert(port->mgmt_sticky_rst);
>> +	if (err) {
>> +		dev_err(port->dev, "deassert mgmt_sticky_rst err %d\n", err);
>> +		return err;
>> +	}
>> +
>> +	err = reset_control_deassert(port->pipe_rst);
>> +	if (err) {
>> +		dev_err(port->dev, "deassert pipe_rst err %d\n", err);
>> +		return err;
>> +	}
>> +
>> +	/* Enable Gen1 training */
>> +	pcie_write(port,
>> +		   HIWORD_UPDATE(PCIE_CLIENT_LINK_TRAIN_ENABLE,
>> +				 PCIE_CLIENT_LINK_TRAIN_MASK,
>> +				 PCIE_CLIENT_LINK_TRAIN_SHIFT),
>> +		   PCIE_CLIENT_BASE);
>> +
>> +	gpiod_set_value(port->ep_gpio, 1);
>> +
>> +	/* 500ms timeout value should be enough for Gen1/2 training */
>> +	timeout = jiffies + msecs_to_jiffies(500);
>> +
>> +	for (;;) {
>> +		status = pcie_read(port, PCIE_CLIENT_BASIC_STATUS1);
>> +		if (((status >> PCIE_CLIENT_LINK_STATUS_SHIFT) &
>> +		      PCIE_CLIENT_LINK_STATUS_MASK) ==
>> +		      PCIE_CLIENT_LINK_STATUS_UP) {
>> +			dev_dbg(port->dev, "PCIe link training gen1 pass!\n");
>> +			break;
>> +		}
>> +
>> +		msleep(20);
>> +
>> +		if (!time_before(jiffies, timeout)) {
>> +			err = -ETIMEDOUT;
>> +			break;
>> +		}
>> +
>> +	}
>> +
>> +	/* Double check gen1 training */
>> +	if (err) {
>> +		status = pcie_read(port, PCIE_CLIENT_BASIC_STATUS1);
>> +		err = (((status >> PCIE_CLIENT_LINK_STATUS_SHIFT) &
>> +			PCIE_CLIENT_LINK_STATUS_MASK) ==
>> +			PCIE_CLIENT_LINK_STATUS_UP) ? 0 : -ETIMEDOUT;
>> +		if (err) {
>> +			dev_err(port->dev, "PCIe link training gen1 timeout!\n");
>> +			return err;
>> +		}
>> +	}
>> +
>> +	/*
>> +	 * Enable retrain for gen2. This should be configured only after
>> +	 * gen1 finished.
>> +	 */
>> +	status = pcie_read(port,
>> +			   PCIE_RC_CONFIG_LCS + PCIE_RC_CONFIG_BASE);
>> +	status |= PCIE_CORE_LCSR_RETRAIN_LINK;
>> +	pcie_write(port, status,
>> +		   PCIE_RC_CONFIG_LCS + PCIE_RC_CONFIG_BASE);
>> +
>> +	timeout = jiffies + msecs_to_jiffies(500);
>> +	for (;;) {
>> +		status = pcie_read(port, PCIE_CORE_CTRL_MGMT_BASE);
>> +		if (((status >> PCIE_CORE_PL_CONF_SPEED_SHIFT) &
>> +		     PCIE_CORE_PL_CONF_SPEED_MASK) ==
>> +		     PCIE_CORE_PL_CONF_SPEED_5G) {
>> +			dev_dbg(port->dev, "PCIe link training gen2 pass!\n");
>> +			break;
>> +		}
>> +
>> +		msleep(20);
>> +
>> +		if (!time_before(jiffies, timeout)) {
>> +			err = -ETIMEDOUT;
>> +			break;
>> +		}
>> +	}
>> +
>> +	/* Double check gen2 training */
>> +	if (err) {
>> +		status = pcie_read(port, PCIE_CORE_CTRL_MGMT_BASE);
>> +		err = (((status >> PCIE_CORE_PL_CONF_SPEED_SHIFT) &
>> +			PCIE_CORE_PL_CONF_SPEED_MASK) ==
>> +			PCIE_CORE_PL_CONF_SPEED_5G) ? 0 : -ETIMEDOUT;
>> +		if (err)
>> +			dev_dbg(port->dev, "PCIe link training gen2 timeout, fall back to gen1!\n");
>> +	}
>> +
>> +	/* Check the final link width from negotiated lane counter from MGMT */
>> +	status = pcie_read(port, PCIE_CORE_CTRL_MGMT_BASE);
>> +	status =  0x1 << ((status >> PCIE_CORE_PL_CONF_LANE_SHIFT) &
>> +			   PCIE_CORE_PL_CONF_LANE_MASK);
>> +	dev_dbg(port->dev, "current link width is x%d\n", status);
>> +
>> +	pcie_write(port, ROCKCHIP_VENDOR_ID, PCIE_RC_CONFIG_BASE);
>> +	pcie_write(port, PCI_CLASS_BRIDGE_PCI << PCIE_CORE_RC_CONF_SCC_SHIFT,
>> +		   PCIE_RC_CONFIG_BASE + PCIE_RC_CONFIG_RID_CCR);
>> +	pcie_write(port, 0x0, PCIE_CORE_CTRL_MGMT_BASE + PCIE_RC_BAR_CONF);
>> +
>> +	pcie_write(port, (RC_REGION_0_ADDR_TRANS_L + RC_REGION_0_PASS_BITS),
>> +		   PCIE_CORE_AXI_CONF_BASE);
>> +	pcie_write(port, RC_REGION_0_ADDR_TRANS_H,
>> +		   PCIE_CORE_AXI_CONF_BASE + PCIE_CORE_OB_REGION_ADDR1);
>> +	pcie_write(port, 0x0080000a,
>> +		   PCIE_CORE_AXI_CONF_BASE + PCIE_CORE_OB_REGION_DESC0);
>> +	pcie_write(port, 0x0,
>> +		   PCIE_CORE_AXI_CONF_BASE + PCIE_CORE_OB_REGION_DESC1);
>> +
>> +	return 0;
>> +}
>> +
>> +static irqreturn_t rockchip_pcie_subsys_irq_handler(int irq, void *arg)
>> +{
>> +	struct rockchip_pcie_port *pp = arg;
>> +	u32 reg;
>> +	u32 sub_reg;
>> +
>> +	reg = pcie_read(pp, PCIE_CLIENT_INT_STATUS);
>> +	if (reg & PCIE_CLIENT_INT_LOCAL) {
>> +		dev_dbg(pp->dev, "local interrupt received\n");
>> +		sub_reg = pcie_read(pp, PCIE_CORE_INT_STATUS);
>> +		if (sub_reg & PCIE_CORE_INT_PRFPE)
>> +			dev_dbg(pp->dev, "parity error detected while reading from the PNP receive FIFO RAM\n");
>> +
>> +		if (sub_reg & PCIE_CORE_INT_CRFPE)
>> +			dev_dbg(pp->dev, "parity error detected while reading from the Completion Receive FIFO RAM\n");
>> +
>> +		if (sub_reg & PCIE_CORE_INT_RRPE)
>> +			dev_dbg(pp->dev, "parity error detected while reading from replay buffer RAM\n");
>> +
>> +		if (sub_reg & PCIE_CORE_INT_PRFO)
>> +			dev_dbg(pp->dev, "overflow occurred in the PNP receive FIFO\n");
>> +
>> +		if (sub_reg & PCIE_CORE_INT_CRFO)
>> +			dev_dbg(pp->dev, "overflow occurred in the completion receive FIFO\n");
>> +
>> +		if (sub_reg & PCIE_CORE_INT_RT)
>> +			dev_dbg(pp->dev, "replay timer timed out\n");
>> +
>> +		if (sub_reg & PCIE_CORE_INT_RTR)
>> +			dev_dbg(pp->dev, "replay timer rolled over after 4 transmissions of the same TLP\n");
>> +
>> +		if (sub_reg & PCIE_CORE_INT_PE)
>> +			dev_dbg(pp->dev, "phy error detected on receive side\n");
>> +
>> +		if (sub_reg & PCIE_CORE_INT_MTR)
>> +			dev_dbg(pp->dev, "malformed TLP received from the link\n");
>> +
>> +		if (sub_reg & PCIE_CORE_INT_UCR)
>> +			dev_dbg(pp->dev, "malformed TLP received from the link\n");
>> +
>> +		if (sub_reg & PCIE_CORE_INT_FCE)
>> +			dev_dbg(pp->dev, "an error was observed in the flow control advertisements from the other side\n");
>> +
>> +		if (sub_reg & PCIE_CORE_INT_CT)
>> +			dev_dbg(pp->dev, "a request timed out waiting for completion\n");
>> +
>> +		if (sub_reg & PCIE_CORE_INT_UTC)
>> +			dev_dbg(pp->dev, "unmapped TC error\n");
>> +
>> +		if (sub_reg & PCIE_CORE_INT_MMVC)
>> +			dev_dbg(pp->dev, "MSI mask register changes\n");
>> +
>> +		pcie_write(pp, sub_reg, PCIE_CORE_INT_STATUS);
>> +	} else if (reg & PCIE_CLIENT_INT_PHY) {
>> +		dev_dbg(pp->dev, "phy link changes\n");
>> +		rockchip_pcie_clr_bw_int(pp);
>> +	}
>> +
>> +	pcie_write(pp, reg & PCIE_CLIENT_INT_LOCAL, PCIE_CLIENT_INT_STATUS);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static irqreturn_t rockchip_pcie_client_irq_handler(int irq, void *arg)
>> +{
>> +	struct rockchip_pcie_port *pp = arg;
>> +	u32 reg;
>> +
>> +	reg = pcie_read(pp, PCIE_CLIENT_INT_STATUS);
>> +	if (reg & PCIE_CLIENT_INT_LEGACY_DONE)
>> +		dev_dbg(pp->dev, "legacy done interrupt received\n");
>> +
>> +	if (reg & PCIE_CLIENT_INT_MSG)
>> +		dev_dbg(pp->dev, "message done interrupt received\n");
>> +
>> +	if (reg & PCIE_CLIENT_INT_HOT_RST)
>> +		dev_dbg(pp->dev, "hot reset interrupt received\n");
>> +
>> +	if (reg & PCIE_CLIENT_INT_DPA)
>> +		dev_dbg(pp->dev, "dpa interrupt received\n");
>> +
>> +	if (reg & PCIE_CLIENT_INT_FATAL_ERR)
>> +		dev_dbg(pp->dev, "fatal error interrupt received\n");
>> +
>> +	if (reg & PCIE_CLIENT_INT_NFATAL_ERR)
>> +		dev_dbg(pp->dev, "no fatal error interrupt received\n");
>> +
>> +	if (reg & PCIE_CLIENT_INT_CORR_ERR)
>> +		dev_dbg(pp->dev, "correctable error interrupt received\n");
>> +
>> +	if (reg & PCIE_CLIENT_INT_PHY)
>> +		dev_dbg(pp->dev, "phy interrupt received\n");
>> +
>> +	pcie_write(pp, reg & (PCIE_CLIENT_INT_LEGACY_DONE |
>> +			      PCIE_CLIENT_INT_MSG | PCIE_CLIENT_INT_HOT_RST |
>> +			      PCIE_CLIENT_INT_DPA | PCIE_CLIENT_INT_FATAL_ERR |
>> +			      PCIE_CLIENT_INT_NFATAL_ERR |
>> +			      PCIE_CLIENT_INT_CORR_ERR),
>> +			      PCIE_CLIENT_INT_STATUS |
>> +			      PCIE_CLIENT_INT_PHY);
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static void rockchip_pcie_legacy_int_handler(struct irq_desc *desc)
>> +{
>> +	struct irq_chip *chip = irq_desc_get_chip(desc);
>> +	struct rockchip_pcie_port *port;
>> +	u32 reg;
>> +	u32 hwirq;
>> +	u32 virq;
>> +
>> +	chained_irq_enter(chip, desc);
>> +	port = irq_desc_get_handler_data(desc);
>> +
>> +	reg = pcie_read(port, PCIE_CLIENT_INT_STATUS);
>> +	reg = (reg & ROCKCHIP_PCIE_RPIFR1_INTR_MASK) >>
>> +	       ROCKCHIP_PCIE_RPIFR1_INTR_SHIFT;
>> +
>> +	while (reg) {
>> +		hwirq = ffs(reg) - 1;
>> +		reg &= ~BIT(hwirq);
>> +
>> +		virq = irq_find_mapping(port->irq_domain, hwirq);
>> +		if (virq)
>> +			generic_handle_irq(virq);
>> +		else
>> +			dev_err(port->dev, "unexpected IRQ, INT%d\n", hwirq);
>> +	}
>> +
>> +	chained_irq_exit(chip, desc);
>> +}
>> +
>> +
>> +/**
>> + * rockchip_pcie_parse_dt - Parse Device tree
>
> s/Device tree/Device Tree/

will fix.

>
>> + * @port: PCIe port information
>> + *
>> + * Return: '0' on success and error value on failure
>> + */
>> +static int rockchip_pcie_parse_dt(struct rockchip_pcie_port *port)
>> +{
>> +	struct device *dev = port->dev;
>> +	struct platform_device *pdev = to_platform_device(dev);
>> +	struct device_node *node = dev->of_node;
>> +	struct resource *regs;
>> +	int irq;
>> +	int err;
>> +
>> +	regs = platform_get_resource_byname(pdev,
>> +					    IORESOURCE_MEM,
>> +					    "axi-base");
>> +	if (!regs) {
>> +		dev_err(dev, "missing axi-base property\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	port->reg_base = devm_ioremap_resource(dev, regs);
>> +	if (IS_ERR(port->reg_base))
>> +		return PTR_ERR(port->reg_base);
>> +
>> +	regs = platform_get_resource_byname(pdev,
>> +					    IORESOURCE_MEM,
>> +					    "apb-base");
>> +	if (!regs) {
>> +		dev_err(dev, "missing apb-base property\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	port->apb_base = devm_ioremap_resource(dev, regs);
>> +	if (IS_ERR(port->apb_base))
>> +		return PTR_ERR(port->apb_base);
>> +
>> +	port->phy = devm_phy_get(dev, "pcie-phy");
>> +	if (IS_ERR(port->phy)) {
>> +		if (PTR_ERR(port->phy) != -EPROBE_DEFER)
>> +			dev_err(dev, "Missing phy\n");
>> +		return PTR_ERR(port->phy);
>> +	}
>> +
>> +	port->lanes = 1;
>> +	err = of_property_read_u32(node, "num-lanes", &port->lanes);
>> +	if (!err && ((port->lanes == 0) ||
>> +		     (port->lanes == 3) ||
>> +		     (port->lanes > 4))) {
>> +		dev_warn(dev, "invalid num-lanes, default use one lane\n");
>> +		port->lanes = 1;
>> +	}
>> +
>> +	port->core_rst = devm_reset_control_get(dev, "core");
>> +	if (IS_ERR(port->core_rst)) {
>> +		if (PTR_ERR(port->core_rst) != -EPROBE_DEFER)
>> +			dev_err(dev, "missing core rst property in node\n");
>> +		return PTR_ERR(port->core_rst);
>> +	}
>> +
>> +	port->mgmt_rst = devm_reset_control_get(dev, "mgmt");
>> +	if (IS_ERR(port->mgmt_rst)) {
>> +		if (PTR_ERR(port->mgmt_rst) != -EPROBE_DEFER)
>> +			dev_err(dev, "missing mgmt rst property in node\n");
>> +		return PTR_ERR(port->mgmt_rst);
>> +	}
>> +
>> +	port->mgmt_sticky_rst = devm_reset_control_get(dev, "mgmt-sticky");
>> +	if (IS_ERR(port->mgmt_sticky_rst)) {
>> +		if (PTR_ERR(port->mgmt_sticky_rst) != -EPROBE_DEFER)
>> +			dev_err(dev, "missing mgmt-sticky rst property in node\n");
>> +		return PTR_ERR(port->mgmt_sticky_rst);
>> +	}
>> +
>> +	port->pipe_rst = devm_reset_control_get(dev, "pipe");
>> +	if (IS_ERR(port->pipe_rst)) {
>> +		if (PTR_ERR(port->pipe_rst) != -EPROBE_DEFER)
>> +			dev_err(dev, "missing pipe rst property in node\n");
>> +		return PTR_ERR(port->pipe_rst);
>> +	}
>> +
>> +	port->ep_gpio = devm_gpiod_get(dev, "ep", GPIOD_OUT_HIGH);
>> +	if (IS_ERR(port->ep_gpio)) {
>> +		dev_err(dev, "missing ep-gpios property in node\n");
>> +		return PTR_ERR(port->ep_gpio);
>> +	}
>> +
>> +	port->aclk_pcie = devm_clk_get(dev, "aclk");
>> +	if (IS_ERR(port->aclk_pcie)) {
>> +		dev_err(dev, "aclk clock not found\n");
>> +		return PTR_ERR(port->aclk_pcie);
>> +	}
>> +
>> +	port->aclk_perf_pcie = devm_clk_get(dev, "aclk-perf");
>> +	if (IS_ERR(port->aclk_perf_pcie)) {
>> +		dev_err(dev, "aclk_perf clock not found\n");
>> +		return PTR_ERR(port->aclk_perf_pcie);
>> +	}
>> +
>> +	port->hclk_pcie = devm_clk_get(dev, "hclk");
>> +	if (IS_ERR(port->hclk_pcie)) {
>> +		dev_err(dev, "hclk clock not found\n");
>> +		return PTR_ERR(port->hclk_pcie);
>> +	}
>> +
>> +	port->clk_pcie_pm = devm_clk_get(dev, "pm");
>> +	if (IS_ERR(port->clk_pcie_pm)) {
>> +		dev_err(dev, "pm clock not found\n");
>> +		return PTR_ERR(port->clk_pcie_pm);
>> +	}
>> +
>> +	irq = platform_get_irq_byname(pdev, "sys");
>> +	if (irq < 0) {
>> +		dev_err(dev, "missing sys IRQ resource\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	err = devm_request_irq(dev, irq, rockchip_pcie_subsys_irq_handler,
>> +			       IRQF_SHARED, "pcie-sys", port);
>> +	if (err) {
>> +		dev_err(dev, "failed to request pcie subsystem IRQ\n");
>> +		return err;
>> +	}
>> +
>> +	irq = platform_get_irq_byname(pdev, "legacy");
>> +	if (irq < 0) {
>> +		dev_err(dev, "missing legacy IRQ resource\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	irq_set_chained_handler_and_data(irq,
>> +					 rockchip_pcie_legacy_int_handler,
>> +					 port);
>> +
>> +	irq = platform_get_irq_byname(pdev, "client");
>> +	if (irq < 0) {
>> +		dev_err(dev, "missing client IRQ resource\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	err = devm_request_irq(dev, irq, rockchip_pcie_client_irq_handler,
>> +			       IRQF_SHARED, "pcie-client", port);
>> +	if (err) {
>> +		dev_err(dev, "failed to request pcie client IRQ\n");
>
> "PCIe"

Will fix.

>
>> +		return err;
>> +	}
>> +
>> +	port->vpcie3v3 = devm_regulator_get_optional(dev, "vpcie3v3");
>> +	if (IS_ERR(port->vpcie3v3)) {
>> +		if (PTR_ERR(port->vpcie3v3) == -EPROBE_DEFER)
>> +			return -EPROBE_DEFER;
>> +		dev_info(dev, "no vpcie3v3 regulator found\n");
>> +	}
>> +
>> +	port->vpcie1v8 = devm_regulator_get_optional(dev, "vpcie1v8");
>> +	if (IS_ERR(port->vpcie1v8)) {
>> +		if (PTR_ERR(port->vpcie1v8) == -EPROBE_DEFER)
>> +			return -EPROBE_DEFER;
>> +		dev_info(dev, "no vpcie1v8 regulator found\n");
>> +	}
>> +
>> +	port->vpcie0v9 = devm_regulator_get_optional(dev, "vpcie0v9");
>> +	if (IS_ERR(port->vpcie0v9)) {
>> +		if (PTR_ERR(port->vpcie0v9) == -EPROBE_DEFER)
>> +			return -EPROBE_DEFER;
>> +		dev_info(dev, "no vpcie0v9 regulator found\n");
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int rockchip_pcie_set_vpcie(struct rockchip_pcie_port *port)
>> +{
>> +	int err;
>> +
>> +	if (!IS_ERR(port->vpcie3v3)) {
>> +		err = regulator_enable(port->vpcie3v3);
>> +		if (err) {
>> +			dev_err(port->dev, "Fail to enable vpcie3v3 regulator.\n");
>
> s/Fail/fail/ here and similar cases below.

Will fix it as well as some other upper-case.

>
>> +			goto err_out;
>> +		}
>> +	}
>> +
>> +	if (!IS_ERR(port->vpcie1v8)) {
>> +		err = regulator_enable(port->vpcie1v8);
>> +		if (err) {
>> +			dev_err(port->dev, "Fail to enable vpcie1v8 regulator.\n");
>> +			goto err_disable_3v3;
>> +		}
>> +	}
>> +
>> +	if (!IS_ERR(port->vpcie0v9)) {
>> +		err = regulator_enable(port->vpcie0v9);
>> +		if (err) {
>> +			dev_err(port->dev, "Fail to enable vpcie0v9 regulator.\n");
>> +			goto err_disable_1v8;
>> +		}
>> +	}
>> +
>> +	return 0;
>> +
>> +err_disable_1v8:
>> +	if (!IS_ERR(port->vpcie1v8))
>> +		regulator_disable(port->vpcie1v8);
>> +err_disable_3v3:
>> +	if (!IS_ERR(port->vpcie3v3))
>> +		regulator_disable(port->vpcie3v3);
>> +err_out:
>> +	return err;
>> +}
>> +
>> +static void rockchip_pcie_enable_interrupts(struct rockchip_pcie_port *port)
>> +{
>> +	pcie_write(port, (PCIE_CLIENT_INT_CLI << 16) &
>> +		   (~PCIE_CLIENT_INT_CLI), PCIE_CLIENT_INT_MASK);
>> +	pcie_write(port, (u32)(~PCIE_CORE_INT), PCIE_CORE_INT_MASK);
>> +
>> +	rockchip_pcie_enable_bw_int(port);
>> +}
>> +
>> +static int rockchip_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
>> +				  irq_hw_number_t hwirq)
>> +{
>> +	irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq);
>> +	irq_set_chip_data(irq, domain->host_data);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct irq_domain_ops intx_domain_ops = {
>> +	.map = rockchip_pcie_intx_map,
>> +};
>> +
>> +static int rockchip_pcie_init_irq_domain(struct rockchip_pcie_port *pp)
>> +{
>> +	struct device *dev = pp->dev;
>> +	struct device_node *intc = of_get_next_child(dev->of_node, NULL);
>> +
>> +	if (!intc) {
>> +		dev_err(dev, "Missing child interrupt-controller node\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	pp->irq_domain = irq_domain_add_linear(intc, 4, &intx_domain_ops, pp);
>> +	if (!pp->irq_domain) {
>> +		dev_err(dev, "Failed to get a INTx IRQ domain\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int rockchip_pcie_prog_ob_atu(struct rockchip_pcie_port *pp,
>> +				     int region_no,
>> +				     int type, u8 num_pass_bits,
>> +				     u32 lower_addr, u32 upper_addr)
>> +{
>> +	u32 ob_addr_0;
>> +	u32 ob_addr_1;
>> +	u32 ob_desc_0;
>> +	void __iomem *aw_base;
>> +
>> +	if (region_no >= MAX_AXI_WRAPPER_REGION_NUM)
>> +		return -EINVAL;
>> +	if ((num_pass_bits + 1) < 8)
>> +		return -EINVAL;
>> +	if (num_pass_bits > 63)
>> +		return -EINVAL;
>> +	if (region_no == 0) {
>> +		if (AXI_REGION_0_SIZE < (2ULL << num_pass_bits))
>> +		return -EINVAL;
>> +	}
>> +	if (region_no != 0) {
>> +		if (AXI_REGION_SIZE < (2ULL << num_pass_bits))
>> +		return -EINVAL;
>
> Misleading indentation.
>

will fix.

>> +	}
>
> Add a blank line between all the error checking and the meat of the
> function.  Also below in the corresponding ib_atu code.
>

ok.

>> +	aw_base = pp->apb_base + PCIE_CORE_AXI_CONF_BASE;
>> +	aw_base += (region_no << OB_REG_SIZE_SHIFT);
>> +
>> +	ob_addr_0 = num_pass_bits & PCIE_CORE_OB_REGION_ADDR0_NUM_BITS;
>> +	ob_addr_0 |= lower_addr & PCIE_CORE_OB_REGION_ADDR0_LO_ADDR;
>> +	ob_addr_1 = upper_addr;
>> +	ob_desc_0 = (1 << 23 | type);
>> +
>> +	writel(ob_addr_0, aw_base);
>> +	writel(ob_addr_1, aw_base + PCIE_CORE_OB_REGION_ADDR1);
>> +	writel(ob_desc_0, aw_base + PCIE_CORE_OB_REGION_DESC0);
>> +	writel(0, aw_base + PCIE_CORE_OB_REGION_DESC1);
>> +
>> +	return 0;
>> +}
>> +
>> +static int rockchip_pcie_prog_ib_atu(struct rockchip_pcie_port *pp,
>> +				     int region_no,
>> +				     u8 num_pass_bits,
>> +				     u32 lower_addr,
>> +				     u32 upper_addr)
>> +{
>> +	u32 ib_addr_0;
>> +	u32 ib_addr_1;
>> +	void __iomem *aw_base;
>> +
>> +	if (!pp)
>> +		return -EINVAL;
>
> Remove this test here, too, same as in the ob_atu path.

Sure.

>
>> +	if (region_no > MAX_AXI_IB_ROOTPORT_REGION_NUM)
>> +		return -EINVAL;
>> +	if ((num_pass_bits + 1) < MIN_AXI_ADDR_BITS_PASSED)
>> +		return -EINVAL;
>> +	if (num_pass_bits > 63)
>> +		return -EINVAL;
>> +	aw_base = pp->apb_base + PCIE_CORE_AXI_INBOUND_BASE;
>> +	aw_base += (region_no << IB_ROOT_PORT_REG_SIZE_SHIFT);
>> +
>> +	ib_addr_0 = num_pass_bits & PCIE_CORE_IB_REGION_ADDR0_NUM_BITS;
>> +	ib_addr_0 |= (lower_addr << 8) & PCIE_CORE_IB_REGION_ADDR0_LO_ADDR;
>> +	ib_addr_1 = upper_addr;
>> +
>> +	writel(ib_addr_0, aw_base);
>> +	writel(ib_addr_1, aw_base + PCIE_RP_IB_ADDR_TRANS);
>> +
>> +	return 0;
>> +}
>> +
>> +static int rockchip_pcie_probe(struct platform_device *pdev)
>> +{
>> +	struct rockchip_pcie_port *port;
>> +	struct device *dev = &pdev->dev;
>> +	struct pci_bus *bus, *child;
>> +	struct resource_entry *win;
>> +	resource_size_t io_base;
>> +	struct resource	*busn;
>
> Removing the initialization here means "busn" may be used
> uninitialized below if the DT doesn't include an IORESOURCE_BUS item.
> That's why I didn't suggest removing the initialization from "busn".
>
> I didn't check all these variables again, so there may be others that
> actually need the initialization.  Please check them all yourself.

I will check all the variables again.

>
>> +	struct resource	*mem;
>> +	struct resource	*io;
>> +	phys_addr_t io_bus_addr;
>> +	u32 io_size;
>> +	phys_addr_t mem_bus_addr;
>> +	u32 mem_size;
>> +	int reg_no;
>> +	int err;
>> +	int offset;
>> +
>> +	LIST_HEAD(res);
>> +
>> +	if (!dev->of_node)
>> +		return -ENODEV;
>> +
>> +	port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
>> +	if (!port)
>> +		return -ENOMEM;
>> +
>> +	port->dev = dev;
>> +
>> +	err = rockchip_pcie_parse_dt(port);
>> +	if (err)
>> +		return err;
>> +
>> +	err = clk_prepare_enable(port->aclk_pcie);
>> +	if (err) {
>> +		dev_err(dev, "Unable to enable aclk_pcie clock.\n");
>> +		goto err_aclk_pcie;
>> +	}
>> +
>> +	err = clk_prepare_enable(port->aclk_perf_pcie);
>> +	if (err) {
>> +		dev_err(dev, "Unable to enable aclk_perf_pcie clock.\n");
>> +		goto err_aclk_perf_pcie;
>> +	}
>> +
>> +	err = clk_prepare_enable(port->hclk_pcie);
>> +	if (err) {
>> +		dev_err(dev, "Unable to enable hclk_pcie clock.\n");
>> +		goto err_hclk_pcie;
>> +	}
>> +
>> +	err = clk_prepare_enable(port->clk_pcie_pm);
>> +	if (err) {
>> +		dev_err(dev, "Unable to enable hclk_pcie clock.\n");
>> +		goto err_pcie_pm;
>> +	}
>> +
>> +	err = rockchip_pcie_set_vpcie(port);
>> +	if (err) {
>> +		dev_err(port->dev, "Fail to set vpcie regulator.\n");
>> +		goto err_set_vpcie;
>> +	}
>> +
>> +	err = rockchip_pcie_init_port(port);
>> +	if (err)
>> +		goto err_vpcie;
>> +
>> +	platform_set_drvdata(pdev, port);
>> +
>> +	rockchip_pcie_enable_interrupts(port);
>> +
>> +	err = rockchip_pcie_init_irq_domain(port);
>> +	if (err < 0)
>> +		goto err_vpcie;
>> +
>> +	err = of_pci_get_host_bridge_resources(dev->of_node, 0, 0xff,
>> +					       &res, &io_base);
>> +	if (err)
>> +		goto err_vpcie;
>> +
>> +	err = devm_request_pci_bus_resources(dev, &res);
>> +	if (err)
>> +		goto err_vpcie;
>> +
>> +	/* Get the I/O and memory ranges from DT */
>> +	resource_list_for_each_entry(win, &res) {
>> +		switch (resource_type(win->res)) {
>> +		case IORESOURCE_IO:
>> +			io = win->res;
>> +			io->name = "I/O";
>> +			io_size = resource_size(io);
>> +			io_bus_addr = io->start - win->offset;
>> +			err = pci_remap_iospace(io, io_base);
>> +			if (err) {
>> +				dev_warn(port->dev, "error %d: failed to map resource %pR\n",
>> +					 err, io);
>> +				continue;
>> +			}
>> +			break;
>> +		case IORESOURCE_MEM:
>> +			mem = win->res;
>> +			mem->name = "MEM";
>> +			mem_size = resource_size(mem);
>> +			mem_bus_addr = mem->start - win->offset;
>> +			break;
>> +		case IORESOURCE_BUS:
>> +			busn = win->res;
>> +			break;
>> +		default:
>> +			continue;
>> +		}
>> +	}
>> +
>> +	if (mem_size)
>> +		for (reg_no = 0; reg_no < (mem_size >> 20); reg_no++) {
>> +			err = rockchip_pcie_prog_ob_atu(port, reg_no + 1,
>> +							AXI_WRAPPER_MEM_WRITE,
>> +							20 - 1,
>> +							mem_bus_addr +
>> +							(reg_no << 20),
>> +							0);
>> +			if (err) {
>> +				dev_err(dev, "Program RC mem outbound ATU failed\n");
>> +				goto err_vpcie;
>> +			}
>> +		}
>> +
>> +	err = rockchip_pcie_prog_ib_atu(port, 2, 32 - 1, 0x0, 0);
>> +	if (err) {
>> +		dev_err(dev, "Program RC mem inbound ATU failed\n");
>> +		goto err_vpcie;
>> +	}
>> +
>> +	offset = mem_size >> 20;
>> +
>> +	if (io_size)
>> +		for (reg_no = 0; reg_no < (io_size >> 20); reg_no++) {
>> +			err = rockchip_pcie_prog_ob_atu(port,
>> +							reg_no + 1 + offset,
>> +							AXI_WRAPPER_IO_WRITE,
>> +							20 - 1,
>> +							io_bus_addr +
>> +							(reg_no << 20),
>> +							0);
>> +			if (err) {
>> +				dev_err(dev, "Program RC io outbound ATU failed\n");
>> +				goto err_vpcie;
>> +			}
>> +		}
>> +
>> +	if (busn)
>> +		port->root_bus_nr = busn->start;
>> +
>> +	bus = pci_scan_root_bus(&pdev->dev, 0,
>> +				&rockchip_pcie_ops, port, &res);
>
> General practice is to fill the line with as much as will fit in 80
> columns, then wrap.  It looks like "&rockchip_pcie_ops" would fit on
> the previous line.  Actually, I think *everything* fits.
>

Got it.

>> +
>> +	if (!bus) {
>> +		err = -ENOMEM;
>> +		goto err_vpcie;
>> +	}
>> +
>> +	pci_bus_size_bridges(bus);
>> +	pci_bus_assign_resources(bus);
>> +	list_for_each_entry(child, &bus->children, node)
>> +		pcie_bus_configure_settings(child);
>> +
>> +	pci_bus_add_devices(bus);
>> +
>> +	dev_warn(dev, "only 32-bit config accesses supported; smaller writes may corrupt adjacent RW1C fields\n");
>> +
>> +	return err;
>> +
>> +err_vpcie:
>> +	if (!IS_ERR(port->vpcie3v3))
>> +		regulator_disable(port->vpcie3v3);
>> +	if (!IS_ERR(port->vpcie1v8))
>> +		regulator_disable(port->vpcie1v8);
>> +	if (!IS_ERR(port->vpcie0v9))
>> +		regulator_disable(port->vpcie0v9);
>> +err_set_vpcie:
>> +	clk_disable_unprepare(port->clk_pcie_pm);
>> +err_pcie_pm:
>> +	clk_disable_unprepare(port->hclk_pcie);
>> +err_hclk_pcie:
>> +	clk_disable_unprepare(port->aclk_perf_pcie);
>> +err_aclk_perf_pcie:
>> +	clk_disable_unprepare(port->aclk_pcie);
>> +err_aclk_pcie:
>> +	return err;
>> +}
>> +
>> +static const struct of_device_id rockchip_pcie_of_match[] = {
>> +	{ .compatible = "rockchip,rk3399-pcie", },
>> +	{}
>> +};
>> +
>> +static struct platform_driver rockchip_pcie_driver = {
>> +	.driver = {
>> +		.name = "rockchip-pcie",
>> +		.of_match_table = rockchip_pcie_of_match,
>> +	},
>> +	.probe = rockchip_pcie_probe,
>> +
>> +};
>> +builtin_platform_driver(rockchip_pcie_driver);
>> +
>
> Spurious blank line here.

will fix.

>
>
>


-- 
Best Regards
Shawn Lin



From ykk at rock-chips.com  Sun Jul 31 20:27:51 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Mon, 1 Aug 2016 11:27:51 +0800
Subject: [PATCH] drm/analogix_dp: Ensure the panel is properly
 prepared/unprepared
In-Reply-To: <1469819791-31860-1-git-send-email-seanpaul@chromium.org>
References: <CAOw6vbKbLzm4mPtp2LsG=7=KJkS_4Oe7rA4Of0uT5vM_Xoiphw@mail.gmail.com>
 <1469819791-31860-1-git-send-email-seanpaul@chromium.org>
Message-ID: <579EC1B7.60608@rock-chips.com>

Sean,

On 07/30/2016 03:16 AM, Sean Paul wrote:
> Instead of just preparing the panel on bind, actually prepare/unprepare
> during modeset/disable. The panel must be prepared in order to read hpd
> status, so we need to refcount the prepares in order to ensure we don't
> accidentally turn the panel off at the wrong time.
>
> Signed-off-by: Sean Paul <seanpaul at chromium.org>
> ---
>
>
> Hi Yakir,
> This is what I was talking about upthread. I've tested it and it seems to be working.
>
> What do you think?

Thanks for your patch, and it works. But I have introduced two 
questions, and I haven't found a way to fixed them.

> Sean
>
>
>
> drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 48 +++++++++++++++++-----
>   1 file changed, 37 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> index 32715da..7b764a4 100644
> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> @@ -960,11 +960,27 @@ enum drm_connector_status
>   analogix_dp_detect(struct drm_connector *connector, bool force)
>   {
>   	struct analogix_dp_device *dp = to_dp(connector);
> +	enum drm_connector_status status = connector_status_disconnected;
> +	int ret;
>   
> -	if (analogix_dp_detect_hpd(dp))
> -		return connector_status_disconnected;
> +	if (dp->plat_data->panel && dp->dpms_mode != DRM_MODE_DPMS_ON) {
> +		ret = drm_panel_prepare(dp->plat_data->panel);
> +		if (ret) {
> +			DRM_ERROR("failed to setup panel (%d)\n", ret);
> +			return connector_status_disconnected;
> +		}
> +	}
> +
> +	if (!analogix_dp_detect_hpd(dp))
> +		status = connector_status_connected;
> +
> +	if (dp->plat_data->panel && dp->dpms_mode != DRM_MODE_DPMS_ON) {
> +		ret = drm_panel_unprepare(dp->plat_data->panel);
> +		if (ret)
> +			DRM_ERROR("failed to setup the panel ret = %d\n", ret);
> +	}
>   
> -	return connector_status_connected;
> +	return status;

1. Panel would flicker at system boot time. Your patch would flash the 
panel power in connector->detect() function when dp->dpms_mode isn't 
DRM_MODE_DPMS_OFF. So when userspace keep detect the connector status in 
boot time, we could see panel would flicker (light up for a while, and 
turn off again, and keep loop for several time). I have copied some 
kernel logs:


[   11.065267] YKK --------- analogix_dp_detect:1052
[   11.729596] YKK --------- analogix_dp_get_modes:1016
[   11.737608] rockchip-dp ff970000.edp: 
analogix_dp_read_bytes_from_dpcd: Aux Transaction fail!
[   11.749229] rockchip-dp ff970000.edp: 
analogix_dp_read_bytes_from_dpcd: Aux Transaction fail!
[   11.760799] rockchip-dp ff970000.edp: 
analogix_dp_read_bytes_from_dpcd: Aux Transaction fail!
[   13.315962] YKK --------- analogix_dp_detect:1052
[   13.984702] YKK --------- analogix_dp_get_modes:1016
[   13.992977] rockchip-dp ff970000.edp: 
analogix_dp_read_bytes_from_dpcd: Aux Transaction fail!
[   14.004414] rockchip-dp ff970000.edp: 
analogix_dp_read_bytes_from_dpcd: Aux Transaction fail!
[   14.015842] rockchip-dp ff970000.edp: 
analogix_dp_read_bytes_from_dpcd: Aux Transaction fail!
[   14.183109] YKK --------- analogix_dp_bridge_pre_enable:1147
[   14.306301] rockchip-dp ff970000.edp: Link Training Clock Recovery 
success
[   14.319130] rockchip-dp ff970000.edp: Link Training success!
[   14.326388] rockchip-dp ff970000.edp: wait SYS_CTL_2.
[   14.437247] rockchip-dp ff970000.edp: Timeout of video streamclk ok
[   14.443585] rockchip-dp ff970000.edp: unable to config video


>   }
>   
>   static void analogix_dp_connector_destroy(struct drm_connector *connector)
> @@ -1035,6 +1051,18 @@ static int analogix_dp_bridge_attach(struct drm_bridge *bridge)
>   	return 0;
>   }
>   
> +static void analogix_dp_bridge_pre_enable(struct drm_bridge *bridge)
> +{
> +	struct analogix_dp_device *dp = bridge->driver_private;
> +	int ret;
> +
> +	if (dp->plat_data->panel) {
> +		ret = drm_panel_prepare(dp->plat_data->panel);
> +		if (ret)
> +			DRM_ERROR("failed to setup the panel ret = %d\n", ret);
> +	}

2. Driver would failed to read EDID in some case. Panel would only be 
powered up in bridge->pre_enable() function which later than 
connector->get_modes() function, and this would caused DPCD transfer 
failed in analogix_dp_handle_edid(). This seem won't caused too big 
issue, cause userspace would read EDID again after bridge/encoder is 
enabled. But it's better to avoid this potential bug.


[   11.065267] YKK --------- analogix_dp_detect:1052
[   11.729596] YKK --------- analogix_dp_get_modes:1016
[   11.737608] rockchip-dp ff970000.edp: 
analogix_dp_read_bytes_from_dpcd: Aux Transaction fail!
[   11.749229] rockchip-dp ff970000.edp: 
analogix_dp_read_bytes_from_dpcd: Aux Transaction fail!
[   11.760799] rockchip-dp ff970000.edp: 
analogix_dp_read_bytes_from_dpcd: Aux Transaction fail!
[   13.315962] YKK --------- analogix_dp_detect:1052
[   13.984702] YKK --------- analogix_dp_get_modes:1016
[   13.992977] rockchip-dp ff970000.edp: 
analogix_dp_read_bytes_from_dpcd: Aux Transaction fail!
[   14.004414] rockchip-dp ff970000.edp: 
analogix_dp_read_bytes_from_dpcd: Aux Transaction fail!
[   14.015842] rockchip-dp ff970000.edp: 
analogix_dp_read_bytes_from_dpcd: Aux Transaction fail!
[   14.183109] YKK --------- analogix_dp_bridge_pre_enable:1147
[   14.306301] rockchip-dp ff970000.edp: Link Training Clock Recovery 
success
[   14.319130] rockchip-dp ff970000.edp: Link Training success!
[   14.326388] rockchip-dp ff970000.edp: wait SYS_CTL_2.
[   14.437247] rockchip-dp ff970000.edp: Timeout of video streamclk ok
[   14.443585] rockchip-dp ff970000.edp: unable to config video
[   14.520565] YKK --------- analogix_dp_detect:1052
[   14.525339] YKK --------- analogix_dp_get_modes:1016
[   14.531233] rockchip-dp ff970000.edp: EDID data does not include any 
extensions.
[   15.595724] YKK --------- analogix_dp_detect:1052
[   15.600488] YKK --------- analogix_dp_get_modes:1016
[   15.606451] rockchip-dp ff970000.edp: EDID data does not include any 
extensions.
[   17.327354] YKK --------- analogix_dp_detect:1052
[   17.332105] YKK --------- analogix_dp_get_modes:1016
[   17.338171] rockchip-dp ff970000.edp: EDID data does not include any 
extensions.
[   56.236024] YKK --------- analogix_dp_detect:1052
[   56.240820] YKK --------- analogix_dp_get_modes:1016
[   56.246855] rockchip-dp ff970000.edp: EDID data does not include any 
extensions.
[   64.092477] YKK --------- analogix_dp_detect:1052
[   64.763712] YKK --------- analogix_dp_get_modes:1016



BR,
- Yakir

> +}
> +
>   static void analogix_dp_bridge_enable(struct drm_bridge *bridge)
>   {
>   	struct analogix_dp_device *dp = bridge->driver_private;
> @@ -1058,6 +1086,7 @@ static void analogix_dp_bridge_enable(struct drm_bridge *bridge)
>   static void analogix_dp_bridge_disable(struct drm_bridge *bridge)
>   {
>   	struct analogix_dp_device *dp = bridge->driver_private;
> +	int ret;
>   
>   	if (dp->dpms_mode != DRM_MODE_DPMS_ON)
>   		return;
> @@ -1077,6 +1106,10 @@ static void analogix_dp_bridge_disable(struct drm_bridge *bridge)
>   
>   	pm_runtime_put_sync(dp->dev);
>   
> +	ret = drm_panel_unprepare(dp->plat_data->panel);
> +	if (ret)
> +		DRM_ERROR("failed to setup the panel ret = %d\n", ret);
> +
>   	dp->dpms_mode = DRM_MODE_DPMS_OFF;
>   }
>   
> @@ -1165,9 +1198,9 @@ static void analogix_dp_bridge_nop(struct drm_bridge *bridge)
>   }
>   
>   static const struct drm_bridge_funcs analogix_dp_bridge_funcs = {
> +	.pre_enable = analogix_dp_bridge_pre_enable,
>   	.enable = analogix_dp_bridge_enable,
>   	.disable = analogix_dp_bridge_disable,
> -	.pre_enable = analogix_dp_bridge_nop,
>   	.post_disable = analogix_dp_bridge_nop,
>   	.mode_set = analogix_dp_bridge_mode_set,
>   	.attach = analogix_dp_bridge_attach,
> @@ -1333,13 +1366,6 @@ int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev,
>   
>   	phy_power_on(dp->phy);
>   
> -	if (dp->plat_data->panel) {
> -		if (drm_panel_prepare(dp->plat_data->panel)) {
> -			DRM_ERROR("failed to setup the panel\n");
> -			return -EBUSY;
> -		}
> -	}
> -
>   	analogix_dp_init_dp(dp);
>   
>   	ret = devm_request_threaded_irq(&pdev->dev, dp->irq,




From ykk at rock-chips.com  Sun Jul 31 20:28:15 2016
From: ykk at rock-chips.com (Yakir Yang)
Date: Mon, 1 Aug 2016 11:28:15 +0800
Subject: [PATCH] drm/analogix_dp: Ensure the panel is properly
 prepared/unprepared
In-Reply-To: <1469819791-31860-1-git-send-email-seanpaul@chromium.org>
References: <CAOw6vbKbLzm4mPtp2LsG=7=KJkS_4Oe7rA4Of0uT5vM_Xoiphw@mail.gmail.com>
 <1469819791-31860-1-git-send-email-seanpaul@chromium.org>
Message-ID: <579EC1CF.9000302@rock-chips.com>

Sean,

On 07/30/2016 03:16 AM, Sean Paul wrote:
> Instead of just preparing the panel on bind, actually prepare/unprepare
> during modeset/disable. The panel must be prepared in order to read hpd
> status, so we need to refcount the prepares in order to ensure we don't
> accidentally turn the panel off at the wrong time.
>
> Signed-off-by: Sean Paul <seanpaul at chromium.org>
> ---
>
>
> Hi Yakir,
> This is what I was talking about upthread. I've tested it and it seems to be working.
>
> What do you think?

Thanks for your patch, and it works. But I have introduced two 
questions, and I haven't found a way to fixed them.

> Sean
>
>
>
> drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 48 +++++++++++++++++-----
>   1 file changed, 37 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> index 32715da..7b764a4 100644
> --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
> @@ -960,11 +960,27 @@ enum drm_connector_status
>   analogix_dp_detect(struct drm_connector *connector, bool force)
>   {
>   	struct analogix_dp_device *dp = to_dp(connector);
> +	enum drm_connector_status status = connector_status_disconnected;
> +	int ret;
>   
> -	if (analogix_dp_detect_hpd(dp))
> -		return connector_status_disconnected;
> +	if (dp->plat_data->panel && dp->dpms_mode != DRM_MODE_DPMS_ON) {
> +		ret = drm_panel_prepare(dp->plat_data->panel);
> +		if (ret) {
> +			DRM_ERROR("failed to setup panel (%d)\n", ret);
> +			return connector_status_disconnected;
> +		}
> +	}
> +
> +	if (!analogix_dp_detect_hpd(dp))
> +		status = connector_status_connected;
> +
> +	if (dp->plat_data->panel && dp->dpms_mode != DRM_MODE_DPMS_ON) {
> +		ret = drm_panel_unprepare(dp->plat_data->panel);
> +		if (ret)
> +			DRM_ERROR("failed to setup the panel ret = %d\n", ret);
> +	}
>   
> -	return connector_status_connected;
> +	return status;

1. Panel would flicker at system boot time. Your patch would flash the 
panel power in connector->detect() function when dp->dpms_mode isn't 
DRM_MODE_DPMS_OFF. So when userspace keep detect the connector status in 
boot time, we could see panel would flicker (light up for a while, and 
turn off again, and keep loop for several time). I have copied some 
kernel logs:


[   11.065267] YKK --------- analogix_dp_detect:1052
[   11.729596] YKK --------- analogix_dp_get_modes:1016
[   11.737608] rockchip-dp ff970000.edp: 
analogix_dp_read_bytes_from_dpcd: Aux Transaction fail!
[   11.749229] rockchip-dp ff970000.edp: 
analogix_dp_read_bytes_from_dpcd: Aux Transaction fail!
[   11.760799] rockchip-dp ff970000.edp: 
analogix_dp_read_bytes_from_dpcd: Aux Transaction fail!
[   13.315962] YKK --------- analogix_dp_detect:1052
[   13.984702] YKK --------- analogix_dp_get_modes:1016
[   13.992977] rockchip-dp ff970000.edp: 
analogix_dp_read_bytes_from_dpcd: Aux Transaction fail!
[   14.004414] rockchip-dp ff970000.edp: 
analogix_dp_read_bytes_from_dpcd: Aux Transaction fail!
[   14.015842] rockchip-dp ff970000.edp: 
analogix_dp_read_bytes_from_dpcd: Aux Transaction fail!
[   14.183109] YKK --------- analogix_dp_bridge_pre_enable:1147
[   14.306301] rockchip-dp ff970000.edp: Link Training Clock Recovery 
success
[   14.319130] rockchip-dp ff970000.edp: Link Training success!
[   14.326388] rockchip-dp ff970000.edp: wait SYS_CTL_2.
[   14.437247] rockchip-dp ff970000.edp: Timeout of video streamclk ok
[   14.443585] rockchip-dp ff970000.edp: unable to config video


>   }
>   
>   static void analogix_dp_connector_destroy(struct drm_connector *connector)
> @@ -1035,6 +1051,18 @@ static int analogix_dp_bridge_attach(struct drm_bridge *bridge)
>   	return 0;
>   }
>   
> +static void analogix_dp_bridge_pre_enable(struct drm_bridge *bridge)
> +{
> +	struct analogix_dp_device *dp = bridge->driver_private;
> +	int ret;
> +
> +	if (dp->plat_data->panel) {
> +		ret = drm_panel_prepare(dp->plat_data->panel);
> +		if (ret)
> +			DRM_ERROR("failed to setup the panel ret = %d\n", ret);
> +	}

2. Driver would failed to read EDID in some case. Panel would only be 
powered up in bridge->pre_enable() function which later than 
connector->get_modes() function, and this would caused DPCD transfer 
failed in analogix_dp_handle_edid(). This seem won't caused too big 
issue, cause userspace would read EDID again after bridge/encoder is 
enabled. But it's better to avoid this potential bug.


[   11.065267] YKK --------- analogix_dp_detect:1052
[   11.729596] YKK --------- analogix_dp_get_modes:1016
[   11.737608] rockchip-dp ff970000.edp: 
analogix_dp_read_bytes_from_dpcd: Aux Transaction fail!
[   11.749229] rockchip-dp ff970000.edp: 
analogix_dp_read_bytes_from_dpcd: Aux Transaction fail!
[   11.760799] rockchip-dp ff970000.edp: 
analogix_dp_read_bytes_from_dpcd: Aux Transaction fail!
[   13.315962] YKK --------- analogix_dp_detect:1052
[   13.984702] YKK --------- analogix_dp_get_modes:1016
[   13.992977] rockchip-dp ff970000.edp: 
analogix_dp_read_bytes_from_dpcd: Aux Transaction fail!
[   14.004414] rockchip-dp ff970000.edp: 
analogix_dp_read_bytes_from_dpcd: Aux Transaction fail!
[   14.015842] rockchip-dp ff970000.edp: 
analogix_dp_read_bytes_from_dpcd: Aux Transaction fail!
[   14.183109] YKK --------- analogix_dp_bridge_pre_enable:1147
[   14.306301] rockchip-dp ff970000.edp: Link Training Clock Recovery 
success
[   14.319130] rockchip-dp ff970000.edp: Link Training success!
[   14.326388] rockchip-dp ff970000.edp: wait SYS_CTL_2.
[   14.437247] rockchip-dp ff970000.edp: Timeout of video streamclk ok
[   14.443585] rockchip-dp ff970000.edp: unable to config video
[   14.520565] YKK --------- analogix_dp_detect:1052
[   14.525339] YKK --------- analogix_dp_get_modes:1016
[   14.531233] rockchip-dp ff970000.edp: EDID data does not include any 
extensions.
[   15.595724] YKK --------- analogix_dp_detect:1052
[   15.600488] YKK --------- analogix_dp_get_modes:1016
[   15.606451] rockchip-dp ff970000.edp: EDID data does not include any 
extensions.
[   17.327354] YKK --------- analogix_dp_detect:1052
[   17.332105] YKK --------- analogix_dp_get_modes:1016
[   17.338171] rockchip-dp ff970000.edp: EDID data does not include any 
extensions.
[   56.236024] YKK --------- analogix_dp_detect:1052
[   56.240820] YKK --------- analogix_dp_get_modes:1016
[   56.246855] rockchip-dp ff970000.edp: EDID data does not include any 
extensions.
[   64.092477] YKK --------- analogix_dp_detect:1052
[   64.763712] YKK --------- analogix_dp_get_modes:1016



BR,
- Yakir

> +}
> +
>   static void analogix_dp_bridge_enable(struct drm_bridge *bridge)
>   {
>   	struct analogix_dp_device *dp = bridge->driver_private;
> @@ -1058,6 +1086,7 @@ static void analogix_dp_bridge_enable(struct drm_bridge *bridge)
>   static void analogix_dp_bridge_disable(struct drm_bridge *bridge)
>   {
>   	struct analogix_dp_device *dp = bridge->driver_private;
> +	int ret;
>   
>   	if (dp->dpms_mode != DRM_MODE_DPMS_ON)
>   		return;
> @@ -1077,6 +1106,10 @@ static void analogix_dp_bridge_disable(struct drm_bridge *bridge)
>   
>   	pm_runtime_put_sync(dp->dev);
>   
> +	ret = drm_panel_unprepare(dp->plat_data->panel);
> +	if (ret)
> +		DRM_ERROR("failed to setup the panel ret = %d\n", ret);
> +
>   	dp->dpms_mode = DRM_MODE_DPMS_OFF;
>   }
>   
> @@ -1165,9 +1198,9 @@ static void analogix_dp_bridge_nop(struct drm_bridge *bridge)
>   }
>   
>   static const struct drm_bridge_funcs analogix_dp_bridge_funcs = {
> +	.pre_enable = analogix_dp_bridge_pre_enable,
>   	.enable = analogix_dp_bridge_enable,
>   	.disable = analogix_dp_bridge_disable,
> -	.pre_enable = analogix_dp_bridge_nop,
>   	.post_disable = analogix_dp_bridge_nop,
>   	.mode_set = analogix_dp_bridge_mode_set,
>   	.attach = analogix_dp_bridge_attach,
> @@ -1333,13 +1366,6 @@ int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev,
>   
>   	phy_power_on(dp->phy);
>   
> -	if (dp->plat_data->panel) {
> -		if (drm_panel_prepare(dp->plat_data->panel)) {
> -			DRM_ERROR("failed to setup the panel\n");
> -			return -EBUSY;
> -		}
> -	}
> -
>   	analogix_dp_init_dp(dp);
>   
>   	ret = devm_request_threaded_irq(&pdev->dev, dp->irq,




From zhengxing at rock-chips.com  Sun Jul 31 20:37:19 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Mon,  1 Aug 2016 11:37:19 +0800
Subject: [PATCH 1/6] clk: rockchip: rk3399: export USBPHYx_480M_SRC clock IDs
In-Reply-To: <1470022644-2152-1-git-send-email-zhengxing@rock-chips.com>
References: <1470022644-2152-1-git-send-email-zhengxing@rock-chips.com>
Message-ID: <1470022644-2152-2-git-send-email-zhengxing@rock-chips.com>

We export some clock IDs for the usb phy 480m source clocks.


Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
---

 include/dt-bindings/clock/rk3399-cru.h |    2 ++
 1 file changed, 2 insertions(+)

diff --git a/include/dt-bindings/clock/rk3399-cru.h b/include/dt-bindings/clock/rk3399-cru.h
index 50a44cf..c4d8311 100644
--- a/include/dt-bindings/clock/rk3399-cru.h
+++ b/include/dt-bindings/clock/rk3399-cru.h
@@ -131,6 +131,8 @@
 #define SCLK_DPHY_RX0_CFG		165
 #define SCLK_RMII_SRC			166
 #define SCLK_PCIEPHY_REF100M		167
+#define SCLK_USBPHY0_480M_SRC		168
+#define SCLK_USBPHY1_480M_SRC		169
 
 #define DCLK_VOP0			180
 #define DCLK_VOP1			181
-- 
1.7.9.5




From zhengxing at rock-chips.com  Sun Jul 31 20:37:18 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Mon,  1 Aug 2016 11:37:18 +0800
Subject: [PATCH 0/6] fix and optimize some clock configuration for the RK3399
 platfom
Message-ID: <1470022644-2152-1-git-send-email-zhengxing@rock-chips.com>


Hi:
  In the development work, we found that some of the previous
incorrect clock configuration on the RK3399 platform, we should
fix and optimize them.


Elaine Zhang (1):
  clk: rockchip: rk3399: delete the CLK_IGNORE_UNUSED for aclk_pcie

Xing Zheng (5):
  clk: rockchip: rk3399: export USBPHYx_480M_SRC clock IDs
  clk: rockchip: rk3399: export 480M_SRC clock id for usbphy0/usbphy1
  clk: rockchip: rk3399: fix incorrect aclk_emmc source gate bits
  clk: rockchip: rk3399: add 65MHz and 106.5MHz clocks for HDMI
  clk: rockchip: rk3399: Add support frac mode frequencies

 drivers/clk/rockchip/clk-rk3399.c      |   35 +++++++++++++++++++++++++-------
 include/dt-bindings/clock/rk3399-cru.h |    2 ++
 2 files changed, 30 insertions(+), 7 deletions(-)

-- 
1.7.9.5




From zhengxing at rock-chips.com  Sun Jul 31 20:37:20 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Mon,  1 Aug 2016 11:37:20 +0800
Subject: [PATCH 2/6] clk: rockchip: rk3399: export 480M_SRC clock id for
 usbphy0/usbphy1
In-Reply-To: <1470022644-2152-1-git-send-email-zhengxing@rock-chips.com>
References: <1470022644-2152-1-git-send-email-zhengxing@rock-chips.com>
Message-ID: <1470022644-2152-3-git-send-email-zhengxing@rock-chips.com>

Export these source clocks for usbphy.


Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
---

 drivers/clk/rockchip/clk-rk3399.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c
index 78e51cb..f55f967f 100644
--- a/drivers/clk/rockchip/clk-rk3399.c
+++ b/drivers/clk/rockchip/clk-rk3399.c
@@ -403,9 +403,9 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = {
 	GATE(SCLK_USB2PHY1_REF, "clk_usb2phy1_ref", "xin24m", CLK_IGNORE_UNUSED,
 			RK3399_CLKGATE_CON(6), 6, GFLAGS),
 
-	GATE(0, "clk_usbphy0_480m_src", "clk_usbphy0_480m", CLK_IGNORE_UNUSED,
+	GATE(SCLK_USBPHY0_480M_SRC, "clk_usbphy0_480m_src", "clk_usbphy0_480m", CLK_IGNORE_UNUSED,
 			RK3399_CLKGATE_CON(13), 12, GFLAGS),
-	GATE(0, "clk_usbphy1_480m_src", "clk_usbphy1_480m", CLK_IGNORE_UNUSED,
+	GATE(SCLK_USBPHY1_480M_SRC, "clk_usbphy1_480m_src", "clk_usbphy1_480m", CLK_IGNORE_UNUSED,
 			RK3399_CLKGATE_CON(13), 12, GFLAGS),
 	MUX(0, "clk_usbphy_480m", mux_usbphy_480m_p, CLK_IGNORE_UNUSED,
 			RK3399_CLKSEL_CON(14), 6, 1, MFLAGS),
-- 
1.7.9.5




From zhengxing at rock-chips.com  Sun Jul 31 20:37:21 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Mon,  1 Aug 2016 11:37:21 +0800
Subject: [PATCH 3/6] clk: rockchip: rk3399: fix incorrect aclk_emmc source
 gate bits
In-Reply-To: <1470022644-2152-1-git-send-email-zhengxing@rock-chips.com>
References: <1470022644-2152-1-git-send-email-zhengxing@rock-chips.com>
Message-ID: <1470022644-2152-4-git-send-email-zhengxing@rock-chips.com>

Dues to incorrect diagram, we need to fix incorrect bits for
(c/g)pll_aclk_emmc_src:
cpll_aclk_emmc_src --> G6[13]
gpll_aclk_emmc_src --> G6[12]

Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
---

 drivers/clk/rockchip/clk-rk3399.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c
index f55f967f..ad3860a 100644
--- a/drivers/clk/rockchip/clk-rk3399.c
+++ b/drivers/clk/rockchip/clk-rk3399.c
@@ -923,9 +923,9 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = {
 			RK3399_CLKGATE_CON(6), 14, GFLAGS),
 
 	GATE(0, "cpll_aclk_emmc_src", "cpll", CLK_IGNORE_UNUSED,
-			RK3399_CLKGATE_CON(6), 12, GFLAGS),
-	GATE(0, "gpll_aclk_emmc_src", "gpll", CLK_IGNORE_UNUSED,
 			RK3399_CLKGATE_CON(6), 13, GFLAGS),
+	GATE(0, "gpll_aclk_emmc_src", "gpll", CLK_IGNORE_UNUSED,
+			RK3399_CLKGATE_CON(6), 12, GFLAGS),
 	COMPOSITE_NOGATE(ACLK_EMMC, "aclk_emmc", mux_aclk_emmc_p, CLK_IGNORE_UNUSED,
 			RK3399_CLKSEL_CON(21), 7, 1, MFLAGS, 0, 5, DFLAGS),
 	GATE(ACLK_EMMC_CORE, "aclk_emmccore", "aclk_emmc", CLK_IGNORE_UNUSED,
-- 
1.7.9.5




From zhengxing at rock-chips.com  Sun Jul 31 20:37:22 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Mon,  1 Aug 2016 11:37:22 +0800
Subject: [PATCH 4/6] clk: rockchip: rk3399: add 65MHz and 106.5MHz clocks for
 HDMI
In-Reply-To: <1470022644-2152-1-git-send-email-zhengxing@rock-chips.com>
References: <1470022644-2152-1-git-send-email-zhengxing@rock-chips.com>
Message-ID: <1470022644-2152-5-git-send-email-zhengxing@rock-chips.com>

We need to add more clocks for supporting more display resolution
for HDMI.

Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
---

 drivers/clk/rockchip/clk-rk3399.c |    2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c
index ad3860a..74afec0 100644
--- a/drivers/clk/rockchip/clk-rk3399.c
+++ b/drivers/clk/rockchip/clk-rk3399.c
@@ -100,8 +100,10 @@ static struct rockchip_pll_rate_table rk3399_pll_rates[] = {
 	RK3036_PLL_RATE( 297000000, 1, 99, 4, 2, 1, 0),
 	RK3036_PLL_RATE( 216000000, 1, 72, 4, 2, 1, 0),
 	RK3036_PLL_RATE( 148500000, 1, 99, 4, 4, 1, 0),
+	RK3036_PLL_RATE( 106500000, 1, 71, 4, 4, 1, 0),
 	RK3036_PLL_RATE(  96000000, 1, 64, 4, 4, 1, 0),
 	RK3036_PLL_RATE(  74250000, 2, 99, 4, 4, 1, 0),
+	RK3036_PLL_RATE(  65000000, 1, 65, 6, 4, 1, 0),
 	RK3036_PLL_RATE(  54000000, 1, 54, 6, 4, 1, 0),
 	RK3036_PLL_RATE(  27000000, 1, 27, 6, 4, 1, 0),
 	{ /* sentinel */ },
-- 
1.7.9.5




From zhengxing at rock-chips.com  Sun Jul 31 20:46:14 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Mon,  1 Aug 2016 11:46:14 +0800
Subject: [PATCH 5/6] clk: rockchip: rk3399: delete the CLK_IGNORE_UNUSED for
 aclk_pcie
In-Reply-To: <1470022644-2152-1-git-send-email-zhengxing@rock-chips.com>
References: <1470022644-2152-1-git-send-email-zhengxing@rock-chips.com>
Message-ID: <1470023174-2435-1-git-send-email-zhengxing@rock-chips.com>

From: Elaine Zhang <zhangqing at rock-chips.com>

allow aclk_pcie and aclk_perf_pcie disabled when unused.


Signed-off-by: Elaine Zhang <zhangqing at rock-chips.com>
Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
---

 drivers/clk/rockchip/clk-rk3399.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c
index 74afec0..4a15ce5b 100644
--- a/drivers/clk/rockchip/clk-rk3399.c
+++ b/drivers/clk/rockchip/clk-rk3399.c
@@ -848,9 +848,9 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = {
 			RK3399_CLKSEL_CON(14), 12, 2, DFLAGS,
 			RK3399_CLKGATE_CON(5), 4, GFLAGS),
 
-	GATE(ACLK_PERF_PCIE, "aclk_perf_pcie", "aclk_perihp", CLK_IGNORE_UNUSED,
+	GATE(ACLK_PERF_PCIE, "aclk_perf_pcie", "aclk_perihp", 0,
 			RK3399_CLKGATE_CON(20), 2, GFLAGS),
-	GATE(ACLK_PCIE, "aclk_pcie", "aclk_perihp", CLK_IGNORE_UNUSED,
+	GATE(ACLK_PCIE, "aclk_pcie", "aclk_perihp", 0,
 			RK3399_CLKGATE_CON(20), 10, GFLAGS),
 	GATE(0, "aclk_perihp_noc", "aclk_perihp", CLK_IGNORE_UNUSED,
 			RK3399_CLKGATE_CON(20), 12, GFLAGS),
-- 
1.7.9.5




From zhengxing at rock-chips.com  Sun Jul 31 20:46:58 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Mon,  1 Aug 2016 11:46:58 +0800
Subject: [PATCH 6/6] clk: rockchip: rk3399: Add support frac mode frequencies
In-Reply-To: <1470022644-2152-1-git-send-email-zhengxing@rock-chips.com>
References: <1470022644-2152-1-git-send-email-zhengxing@rock-chips.com>
Message-ID: <1470023218-2529-1-git-send-email-zhengxing@rock-chips.com>

We need to support various display resolutions for external
display devices like HDMI/DP, the frac mode can help us to
acquire almost any frequencies, and need higher VCOs to reduce
clock jitters.

Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
---

 drivers/clk/rockchip/clk-rk3399.c |   21 ++++++++++++++++++++-
 1 file changed, 20 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c
index 4a15ce5b..3daa164 100644
--- a/drivers/clk/rockchip/clk-rk3399.c
+++ b/drivers/clk/rockchip/clk-rk3399.c
@@ -109,6 +109,25 @@ static struct rockchip_pll_rate_table rk3399_pll_rates[] = {
 	{ /* sentinel */ },
 };
 
+static struct rockchip_pll_rate_table rk3399_pll_frates[] = {
+	/* _mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac */
+	RK3036_PLL_RATE( 594000000, 1, 123, 5, 1, 0, 12582912),  /* vco = 2970000000 */
+	RK3036_PLL_RATE( 593406593, 1, 123, 5, 1, 0, 10508804),  /* vco = 2967032965 */
+	RK3036_PLL_RATE( 297000000, 1, 123, 5, 2, 0, 12582912),  /* vco = 2970000000 */
+	RK3036_PLL_RATE( 296703297, 1, 123, 5, 2, 0, 10508807),  /* vco = 2967032970 */
+	RK3036_PLL_RATE( 148500000, 1, 129, 7, 3, 0, 15728640),  /* vco = 3118500000 */
+	RK3036_PLL_RATE( 148351648, 1, 123, 5, 4, 0, 10508800),  /* vco = 2967032960 */
+	RK3036_PLL_RATE( 106500000, 1, 124, 7, 4, 0,  4194304),  /* vco = 2982000000 */
+	RK3036_PLL_RATE(  74250000, 1, 129, 7, 6, 0, 15728640),  /* vco = 3118500000 */
+	RK3036_PLL_RATE(  74175824, 1, 129, 7, 6, 0, 13550823),  /* vco = 3115384608 */
+	RK3036_PLL_RATE(  65000000, 1, 113, 7, 6, 0, 12582912),  /* vco = 2730000000 */
+	RK3036_PLL_RATE(  59340659, 1, 121, 7, 7, 0,  2581098),  /* vco = 2907692291 */
+	RK3036_PLL_RATE(  54000000, 1, 110, 7, 7, 0,  4194304),  /* vco = 2646000000 */
+	RK3036_PLL_RATE(  27000000, 1,  55, 7, 7, 0,  2097152),  /* vco = 1323000000 */
+	RK3036_PLL_RATE(  26973027, 1,  55, 7, 7, 0,  1173232),  /* vco = 1321678323 */
+	{ /* sentinel */ },
+};
+
 /* CRU parents */
 PNAME(mux_pll_p)				= { "xin24m", "xin32k" };
 
@@ -229,7 +248,7 @@ static struct rockchip_pll_clock rk3399_pll_clks[] __initdata = {
 	[npll] = PLL(pll_rk3399, PLL_NPLL, "npll",  mux_pll_p, 0, RK3399_PLL_CON(40),
 		     RK3399_PLL_CON(43), 8, 31, ROCKCHIP_PLL_SYNC_RATE, rk3399_pll_rates),
 	[vpll] = PLL(pll_rk3399, PLL_VPLL, "vpll",  mux_pll_p, 0, RK3399_PLL_CON(48),
-		     RK3399_PLL_CON(51), 8, 31, ROCKCHIP_PLL_SYNC_RATE, rk3399_pll_rates),
+		     RK3399_PLL_CON(51), 8, 31, ROCKCHIP_PLL_SYNC_RATE, rk3399_pll_frates),
 };
 
 static struct rockchip_pll_clock rk3399_pmu_pll_clks[] __initdata = {
-- 
1.7.9.5




From zhengxing at rock-chips.com  Sun Jul 31 21:23:03 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Mon,  1 Aug 2016 12:23:03 +0800
Subject: [RESEND PATCH 0/6] fix and optimize some clock configuration for the
 RK3399 platfom
Message-ID: <1470025389-4094-1-git-send-email-zhengxing@rock-chips.com>


Hi:
  In the development work, we found that some of the previous
incorrect clock configuration on the RK3399 platform, we should
fix and optimize them.


Elaine Zhang (1):
  clk: rockchip: rk3399: delete the CLK_IGNORE_UNUSED for aclk_pcie

Xing Zheng (5):
  clk: rockchip: rk3399: export USBPHYx_480M_SRC clock IDs
  clk: rockchip: rk3399: export 480M_SRC clock id for usbphy0/usbphy1
  clk: rockchip: rk3399: fix incorrect aclk_emmc source gate bits
  clk: rockchip: rk3399: add 65MHz and 106.5MHz clocks for HDMI
  clk: rockchip: rk3399: Add support frac mode frequencies

 drivers/clk/rockchip/clk-rk3399.c      |   35 +++++++++++++++++++++++++-------
 include/dt-bindings/clock/rk3399-cru.h |    2 ++
 2 files changed, 30 insertions(+), 7 deletions(-)

-- 
1.7.9.5




From zhengxing at rock-chips.com  Sun Jul 31 21:23:04 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Mon,  1 Aug 2016 12:23:04 +0800
Subject: [RESEND PATCH 1/6] clk: rockchip: rk3399: export USBPHYx_480M_SRC
 clock IDs
In-Reply-To: <1470025389-4094-1-git-send-email-zhengxing@rock-chips.com>
References: <1470025389-4094-1-git-send-email-zhengxing@rock-chips.com>
Message-ID: <1470025389-4094-2-git-send-email-zhengxing@rock-chips.com>

We export some clock IDs for the usb phy 480m source clocks.

Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>

---

 include/dt-bindings/clock/rk3399-cru.h |    2 ++
 1 file changed, 2 insertions(+)

diff --git a/include/dt-bindings/clock/rk3399-cru.h b/include/dt-bindings/clock/rk3399-cru.h
index 50a44cf..c4d8311 100644
--- a/include/dt-bindings/clock/rk3399-cru.h
+++ b/include/dt-bindings/clock/rk3399-cru.h
@@ -131,6 +131,8 @@
 #define SCLK_DPHY_RX0_CFG		165
 #define SCLK_RMII_SRC			166
 #define SCLK_PCIEPHY_REF100M		167
+#define SCLK_USBPHY0_480M_SRC		168
+#define SCLK_USBPHY1_480M_SRC		169
 
 #define DCLK_VOP0			180
 #define DCLK_VOP1			181
-- 
1.7.9.5




From zhengxing at rock-chips.com  Sun Jul 31 21:23:05 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Mon,  1 Aug 2016 12:23:05 +0800
Subject: [RESEND PATCH 2/6] clk: rockchip: rk3399: export 480M_SRC clock id
 for usbphy0/usbphy1
In-Reply-To: <1470025389-4094-1-git-send-email-zhengxing@rock-chips.com>
References: <1470025389-4094-1-git-send-email-zhengxing@rock-chips.com>
Message-ID: <1470025389-4094-3-git-send-email-zhengxing@rock-chips.com>

Export these source clocks for usbphy.

Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>

---

 drivers/clk/rockchip/clk-rk3399.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c
index 78e51cb..f55f967f 100644
--- a/drivers/clk/rockchip/clk-rk3399.c
+++ b/drivers/clk/rockchip/clk-rk3399.c
@@ -403,9 +403,9 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = {
 	GATE(SCLK_USB2PHY1_REF, "clk_usb2phy1_ref", "xin24m", CLK_IGNORE_UNUSED,
 			RK3399_CLKGATE_CON(6), 6, GFLAGS),
 
-	GATE(0, "clk_usbphy0_480m_src", "clk_usbphy0_480m", CLK_IGNORE_UNUSED,
+	GATE(SCLK_USBPHY0_480M_SRC, "clk_usbphy0_480m_src", "clk_usbphy0_480m", CLK_IGNORE_UNUSED,
 			RK3399_CLKGATE_CON(13), 12, GFLAGS),
-	GATE(0, "clk_usbphy1_480m_src", "clk_usbphy1_480m", CLK_IGNORE_UNUSED,
+	GATE(SCLK_USBPHY1_480M_SRC, "clk_usbphy1_480m_src", "clk_usbphy1_480m", CLK_IGNORE_UNUSED,
 			RK3399_CLKGATE_CON(13), 12, GFLAGS),
 	MUX(0, "clk_usbphy_480m", mux_usbphy_480m_p, CLK_IGNORE_UNUSED,
 			RK3399_CLKSEL_CON(14), 6, 1, MFLAGS),
-- 
1.7.9.5




From zhengxing at rock-chips.com  Sun Jul 31 21:23:06 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Mon,  1 Aug 2016 12:23:06 +0800
Subject: [RESEND PATCH 3/6] clk: rockchip: rk3399: fix incorrect aclk_emmc
 source gate bits
In-Reply-To: <1470025389-4094-1-git-send-email-zhengxing@rock-chips.com>
References: <1470025389-4094-1-git-send-email-zhengxing@rock-chips.com>
Message-ID: <1470025389-4094-4-git-send-email-zhengxing@rock-chips.com>

Dues to incorrect diagram, we need to fix incorrect bits for
(c/g)pll_aclk_emmc_src:
cpll_aclk_emmc_src --> G6[13]
gpll_aclk_emmc_src --> G6[12]

Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
---

 drivers/clk/rockchip/clk-rk3399.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c
index f55f967f..ad3860a 100644
--- a/drivers/clk/rockchip/clk-rk3399.c
+++ b/drivers/clk/rockchip/clk-rk3399.c
@@ -923,9 +923,9 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = {
 			RK3399_CLKGATE_CON(6), 14, GFLAGS),
 
 	GATE(0, "cpll_aclk_emmc_src", "cpll", CLK_IGNORE_UNUSED,
-			RK3399_CLKGATE_CON(6), 12, GFLAGS),
-	GATE(0, "gpll_aclk_emmc_src", "gpll", CLK_IGNORE_UNUSED,
 			RK3399_CLKGATE_CON(6), 13, GFLAGS),
+	GATE(0, "gpll_aclk_emmc_src", "gpll", CLK_IGNORE_UNUSED,
+			RK3399_CLKGATE_CON(6), 12, GFLAGS),
 	COMPOSITE_NOGATE(ACLK_EMMC, "aclk_emmc", mux_aclk_emmc_p, CLK_IGNORE_UNUSED,
 			RK3399_CLKSEL_CON(21), 7, 1, MFLAGS, 0, 5, DFLAGS),
 	GATE(ACLK_EMMC_CORE, "aclk_emmccore", "aclk_emmc", CLK_IGNORE_UNUSED,
-- 
1.7.9.5




From zhengxing at rock-chips.com  Sun Jul 31 21:23:07 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Mon,  1 Aug 2016 12:23:07 +0800
Subject: [RESEND PATCH 4/6] clk: rockchip: rk3399: add 65MHz and 106.5MHz
 clocks for HDMI
In-Reply-To: <1470025389-4094-1-git-send-email-zhengxing@rock-chips.com>
References: <1470025389-4094-1-git-send-email-zhengxing@rock-chips.com>
Message-ID: <1470025389-4094-5-git-send-email-zhengxing@rock-chips.com>

We need to add more clocks for supporting more display resolution
for HDMI.

Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
---

 drivers/clk/rockchip/clk-rk3399.c |    2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c
index ad3860a..74afec0 100644
--- a/drivers/clk/rockchip/clk-rk3399.c
+++ b/drivers/clk/rockchip/clk-rk3399.c
@@ -100,8 +100,10 @@ static struct rockchip_pll_rate_table rk3399_pll_rates[] = {
 	RK3036_PLL_RATE( 297000000, 1, 99, 4, 2, 1, 0),
 	RK3036_PLL_RATE( 216000000, 1, 72, 4, 2, 1, 0),
 	RK3036_PLL_RATE( 148500000, 1, 99, 4, 4, 1, 0),
+	RK3036_PLL_RATE( 106500000, 1, 71, 4, 4, 1, 0),
 	RK3036_PLL_RATE(  96000000, 1, 64, 4, 4, 1, 0),
 	RK3036_PLL_RATE(  74250000, 2, 99, 4, 4, 1, 0),
+	RK3036_PLL_RATE(  65000000, 1, 65, 6, 4, 1, 0),
 	RK3036_PLL_RATE(  54000000, 1, 54, 6, 4, 1, 0),
 	RK3036_PLL_RATE(  27000000, 1, 27, 6, 4, 1, 0),
 	{ /* sentinel */ },
-- 
1.7.9.5




From zhengxing at rock-chips.com  Sun Jul 31 21:24:45 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Mon,  1 Aug 2016 12:24:45 +0800
Subject: [RESEND PATCH 5/6] clk: rockchip: rk3399: delete the
 CLK_IGNORE_UNUSED for aclk_pcie
In-Reply-To: <1470025389-4094-1-git-send-email-zhengxing@rock-chips.com>
References: <1470025389-4094-1-git-send-email-zhengxing@rock-chips.com>
Message-ID: <1470025485-4218-1-git-send-email-zhengxing@rock-chips.com>

From: Elaine Zhang <zhangqing at rock-chips.com>

allow aclk_pcie and aclk_perf_pcie disabled when unused.

Signed-off-by: Elaine Zhang <zhangqing at rock-chips.com>
Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>

---

 drivers/clk/rockchip/clk-rk3399.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c
index 74afec0..4a15ce5b 100644
--- a/drivers/clk/rockchip/clk-rk3399.c
+++ b/drivers/clk/rockchip/clk-rk3399.c
@@ -848,9 +848,9 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = {
 			RK3399_CLKSEL_CON(14), 12, 2, DFLAGS,
 			RK3399_CLKGATE_CON(5), 4, GFLAGS),
 
-	GATE(ACLK_PERF_PCIE, "aclk_perf_pcie", "aclk_perihp", CLK_IGNORE_UNUSED,
+	GATE(ACLK_PERF_PCIE, "aclk_perf_pcie", "aclk_perihp", 0,
 			RK3399_CLKGATE_CON(20), 2, GFLAGS),
-	GATE(ACLK_PCIE, "aclk_pcie", "aclk_perihp", CLK_IGNORE_UNUSED,
+	GATE(ACLK_PCIE, "aclk_pcie", "aclk_perihp", 0,
 			RK3399_CLKGATE_CON(20), 10, GFLAGS),
 	GATE(0, "aclk_perihp_noc", "aclk_perihp", CLK_IGNORE_UNUSED,
 			RK3399_CLKGATE_CON(20), 12, GFLAGS),
-- 
1.7.9.5




From zhengxing at rock-chips.com  Sun Jul 31 21:25:01 2016
From: zhengxing at rock-chips.com (Xing Zheng)
Date: Mon,  1 Aug 2016 12:25:01 +0800
Subject: [RESEND PATCH 6/6] clk: rockchip: rk3399: Add support frac mode
 frequencies
In-Reply-To: <1470025389-4094-1-git-send-email-zhengxing@rock-chips.com>
References: <1470025389-4094-1-git-send-email-zhengxing@rock-chips.com>
Message-ID: <1470025501-4259-1-git-send-email-zhengxing@rock-chips.com>

We need to support various display resolutions for external
display devices like HDMI/DP, the frac mode can help us to
acquire almost any frequencies, and need higher VCOs to reduce
clock jitters.

Signed-off-by: Xing Zheng <zhengxing at rock-chips.com>
---

 drivers/clk/rockchip/clk-rk3399.c |   21 ++++++++++++++++++++-
 1 file changed, 20 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c
index 4a15ce5b..3daa164 100644
--- a/drivers/clk/rockchip/clk-rk3399.c
+++ b/drivers/clk/rockchip/clk-rk3399.c
@@ -109,6 +109,25 @@ static struct rockchip_pll_rate_table rk3399_pll_rates[] = {
 	{ /* sentinel */ },
 };
 
+static struct rockchip_pll_rate_table rk3399_pll_frates[] = {
+	/* _mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac */
+	RK3036_PLL_RATE( 594000000, 1, 123, 5, 1, 0, 12582912),  /* vco = 2970000000 */
+	RK3036_PLL_RATE( 593406593, 1, 123, 5, 1, 0, 10508804),  /* vco = 2967032965 */
+	RK3036_PLL_RATE( 297000000, 1, 123, 5, 2, 0, 12582912),  /* vco = 2970000000 */
+	RK3036_PLL_RATE( 296703297, 1, 123, 5, 2, 0, 10508807),  /* vco = 2967032970 */
+	RK3036_PLL_RATE( 148500000, 1, 129, 7, 3, 0, 15728640),  /* vco = 3118500000 */
+	RK3036_PLL_RATE( 148351648, 1, 123, 5, 4, 0, 10508800),  /* vco = 2967032960 */
+	RK3036_PLL_RATE( 106500000, 1, 124, 7, 4, 0,  4194304),  /* vco = 2982000000 */
+	RK3036_PLL_RATE(  74250000, 1, 129, 7, 6, 0, 15728640),  /* vco = 3118500000 */
+	RK3036_PLL_RATE(  74175824, 1, 129, 7, 6, 0, 13550823),  /* vco = 3115384608 */
+	RK3036_PLL_RATE(  65000000, 1, 113, 7, 6, 0, 12582912),  /* vco = 2730000000 */
+	RK3036_PLL_RATE(  59340659, 1, 121, 7, 7, 0,  2581098),  /* vco = 2907692291 */
+	RK3036_PLL_RATE(  54000000, 1, 110, 7, 7, 0,  4194304),  /* vco = 2646000000 */
+	RK3036_PLL_RATE(  27000000, 1,  55, 7, 7, 0,  2097152),  /* vco = 1323000000 */
+	RK3036_PLL_RATE(  26973027, 1,  55, 7, 7, 0,  1173232),  /* vco = 1321678323 */
+	{ /* sentinel */ },
+};
+
 /* CRU parents */
 PNAME(mux_pll_p)				= { "xin24m", "xin32k" };
 
@@ -229,7 +248,7 @@ static struct rockchip_pll_clock rk3399_pll_clks[] __initdata = {
 	[npll] = PLL(pll_rk3399, PLL_NPLL, "npll",  mux_pll_p, 0, RK3399_PLL_CON(40),
 		     RK3399_PLL_CON(43), 8, 31, ROCKCHIP_PLL_SYNC_RATE, rk3399_pll_rates),
 	[vpll] = PLL(pll_rk3399, PLL_VPLL, "vpll",  mux_pll_p, 0, RK3399_PLL_CON(48),
-		     RK3399_PLL_CON(51), 8, 31, ROCKCHIP_PLL_SYNC_RATE, rk3399_pll_rates),
+		     RK3399_PLL_CON(51), 8, 31, ROCKCHIP_PLL_SYNC_RATE, rk3399_pll_frates),
 };
 
 static struct rockchip_pll_clock rk3399_pmu_pll_clks[] __initdata = {
-- 
1.7.9.5




