From fd6f489456137d148132010c9da3251ba80f7948 Mon Sep 17 00:00:00 2001 From: Vladimir Barinov Date: Sun, 14 May 2017 13:43:24 +0300 Subject: [PATCH] media: soc_camera: add legacy VIN/CSI2 Add legacy/old R-CAR VIN/CSI2 drivers Signed-off-by: Vladimir Barinov --- arch/arm64/boot/dts/renesas/r8a7795-es1.dtsi | 348 --- arch/arm64/boot/dts/renesas/r8a7795.dtsi | 271 --- arch/arm64/boot/dts/renesas/r8a7796.dtsi | 258 --- drivers/media/platform/soc_camera/Kconfig | 26 + drivers/media/platform/soc_camera/Makefile | 2 + drivers/media/platform/soc_camera/rcar_csi2.c | 708 ++++++ drivers/media/platform/soc_camera/rcar_vin.c | 3071 +++++++++++++++++++++++++ include/media/rcar_csi2.h | 66 + 8 files changed, 3873 insertions(+), 877 deletions(-) create mode 100644 drivers/media/platform/soc_camera/rcar_csi2.c create mode 100644 drivers/media/platform/soc_camera/rcar_vin.c create mode 100644 include/media/rcar_csi2.h diff --git a/arch/arm64/boot/dts/renesas/r8a7795-es1.dtsi b/arch/arm64/boot/dts/renesas/r8a7795-es1.dtsi index 2bf5911..09e1284 100644 --- a/arch/arm64/boot/dts/renesas/r8a7795-es1.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a7795-es1.dtsi @@ -1927,31 +1927,6 @@ clocks = <&cpg CPG_MOD 811>; power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - - reg = <1>; - - vin0csi20: endpoint@0 { - reg = <0>; - remote-endpoint= <&csi20vin0>; - }; - vin0csi21: endpoint@1 { - reg = <1>; - remote-endpoint= <&csi21vin0>; - }; - vin0csi40: endpoint@2 { - reg = <2>; - remote-endpoint= <&csi40vin0>; - }; - }; - }; }; vin1: video@e6ef1000 { @@ -1961,31 +1936,6 @@ clocks = <&cpg CPG_MOD 810>; power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - - reg = <1>; - - vin1csi20: endpoint@0 { - reg = <0>; - remote-endpoint= <&csi20vin1>; - }; - vin1csi21: endpoint@1 { - reg = <1>; - remote-endpoint= <&csi21vin1>; - }; - vin1csi40: endpoint@2 { - reg = <2>; - remote-endpoint= <&csi40vin1>; - }; - }; - }; }; vin2: video@e6ef2000 { @@ -1995,31 +1945,6 @@ clocks = <&cpg CPG_MOD 809>; power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - - reg = <1>; - - vin2csi20: endpoint@0 { - reg = <0>; - remote-endpoint= <&csi20vin2>; - }; - vin2csi21: endpoint@1 { - reg = <1>; - remote-endpoint= <&csi21vin2>; - }; - vin2csi40: endpoint@2 { - reg = <2>; - remote-endpoint= <&csi40vin2>; - }; - }; - }; }; vin3: video@e6ef3000 { @@ -2029,31 +1954,6 @@ clocks = <&cpg CPG_MOD 808>; power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - - reg = <1>; - - vin3csi20: endpoint@0 { - reg = <0>; - remote-endpoint= <&csi20vin3>; - }; - vin3csi21: endpoint@1 { - reg = <1>; - remote-endpoint= <&csi21vin3>; - }; - vin3csi40: endpoint@2 { - reg = <2>; - remote-endpoint= <&csi40vin3>; - }; - }; - }; }; vin4: video@e6ef4000 { @@ -2063,31 +1963,6 @@ clocks = <&cpg CPG_MOD 807>; power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - - reg = <1>; - - vin4csi20: endpoint@0 { - reg = <0>; - remote-endpoint= <&csi20vin4>; - }; - vin4csi21: endpoint@1 { - reg = <1>; - remote-endpoint= <&csi21vin4>; - }; - vin4csi41: endpoint@3 { - reg = <3>; - remote-endpoint= <&csi41vin4>; - }; - }; - }; }; vin5: video@e6ef5000 { @@ -2097,31 +1972,6 @@ clocks = <&cpg CPG_MOD 806>; power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - - reg = <1>; - - vin5csi20: endpoint@0 { - reg = <0>; - remote-endpoint= <&csi20vin5>; - }; - vin5csi21: endpoint@1 { - reg = <1>; - remote-endpoint= <&csi21vin5>; - }; - vin5csi41: endpoint@3 { - reg = <3>; - remote-endpoint= <&csi41vin5>; - }; - }; - }; }; vin6: video@e6ef6000 { @@ -2131,31 +1981,6 @@ clocks = <&cpg CPG_MOD 805>; power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - - reg = <1>; - - vin6csi20: endpoint@0 { - reg = <0>; - remote-endpoint= <&csi20vin6>; - }; - vin6csi21: endpoint@1 { - reg = <1>; - remote-endpoint= <&csi21vin6>; - }; - vin6csi41: endpoint@3 { - reg = <3>; - remote-endpoint= <&csi41vin6>; - }; - }; - }; }; vin7: video@e6ef7000 { @@ -2165,31 +1990,6 @@ clocks = <&cpg CPG_MOD 804>; power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - - reg = <1>; - - vin7csi20: endpoint@0 { - reg = <0>; - remote-endpoint= <&csi20vin7>; - }; - vin7csi21: endpoint@1 { - reg = <1>; - remote-endpoint= <&csi21vin7>; - }; - vin7csi41: endpoint@3 { - reg = <3>; - remote-endpoint= <&csi41vin7>; - }; - }; - }; }; csi2_20: csi2@fea80000 { @@ -2199,51 +1999,6 @@ clocks = <&cpg CPG_MOD 714>; power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - - reg = <1>; - - csi20vin0: endpoint@0 { - reg = <0>; - remote-endpoint = <&vin0csi20>; - }; - csi20vin1: endpoint@1 { - reg = <1>; - remote-endpoint = <&vin1csi20>; - }; - csi20vin2: endpoint@2 { - reg = <2>; - remote-endpoint = <&vin2csi20>; - }; - csi20vin3: endpoint@3 { - reg = <3>; - remote-endpoint = <&vin3csi20>; - }; - csi20vin4: endpoint@4 { - reg = <4>; - remote-endpoint = <&vin4csi20>; - }; - csi20vin5: endpoint@5 { - reg = <5>; - remote-endpoint = <&vin5csi20>; - }; - csi20vin6: endpoint@6 { - reg = <6>; - remote-endpoint = <&vin6csi20>; - }; - csi20vin7: endpoint@7 { - reg = <7>; - remote-endpoint = <&vin7csi20>; - }; - }; - }; }; csi2_21: csi2@fea90000 { @@ -2253,51 +2008,6 @@ clocks = <&cpg CPG_MOD 713>; power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - - reg = <1>; - - csi21vin0: endpoint@0 { - reg = <0>; - remote-endpoint = <&vin0csi21>; - }; - csi21vin1: endpoint@1 { - reg = <1>; - remote-endpoint = <&vin1csi21>; - }; - csi21vin2: endpoint@2 { - reg = <2>; - remote-endpoint = <&vin2csi21>; - }; - csi21vin3: endpoint@3 { - reg = <3>; - remote-endpoint = <&vin3csi21>; - }; - csi21vin4: endpoint@4 { - reg = <4>; - remote-endpoint = <&vin4csi21>; - }; - csi21vin5: endpoint@5 { - reg = <5>; - remote-endpoint = <&vin5csi21>; - }; - csi21vin6: endpoint@6 { - reg = <6>; - remote-endpoint = <&vin6csi21>; - }; - csi21vin7: endpoint@7 { - reg = <7>; - remote-endpoint = <&vin7csi21>; - }; - }; - }; }; csi2_40: csi2@feaa0000 { @@ -2307,35 +2017,6 @@ clocks = <&cpg CPG_MOD 716>; power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - - reg = <1>; - - csi40vin0: endpoint@0 { - reg = <0>; - remote-endpoint = <&vin0csi40>; - }; - csi40vin1: endpoint@1 { - reg = <1>; - remote-endpoint = <&vin1csi40>; - }; - csi40vin2: endpoint@2 { - reg = <2>; - remote-endpoint = <&vin2csi40>; - }; - csi40vin3: endpoint@3 { - reg = <3>; - remote-endpoint = <&vin3csi40>; - }; - }; - }; }; csi2_41: csi2@feab0000 { @@ -2345,35 +2026,6 @@ clocks = <&cpg CPG_MOD 715>; power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - - reg = <1>; - - csi41vin4: endpoint@4 { - reg = <4>; - remote-endpoint = <&vin4csi41>; - }; - csi41vin5: endpoint@5 { - reg = <5>; - remote-endpoint = <&vin5csi41>; - }; - csi41vin6: endpoint@6 { - reg = <6>; - remote-endpoint = <&vin6csi41>; - }; - csi41vin7: endpoint@7 { - reg = <7>; - remote-endpoint = <&vin7csi41>; - }; - }; - }; }; sata: sata@ee300000 { diff --git a/arch/arm64/boot/dts/renesas/r8a7795.dtsi b/arch/arm64/boot/dts/renesas/r8a7795.dtsi index 94262a1..82ebfd4 100644 --- a/arch/arm64/boot/dts/renesas/r8a7795.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a7795.dtsi @@ -1902,27 +1902,6 @@ clocks = <&cpg CPG_MOD 811>; power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - - reg = <1>; - - vin0csi20: endpoint@0 { - reg = <0>; - remote-endpoint= <&csi20vin0>; - }; - vin0csi40: endpoint@2 { - reg = <2>; - remote-endpoint= <&csi40vin0>; - }; - }; - }; }; vin1: video@e6ef1000 { @@ -1932,27 +1911,6 @@ clocks = <&cpg CPG_MOD 810>; power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - - reg = <1>; - - vin1csi20: endpoint@0 { - reg = <0>; - remote-endpoint= <&csi20vin1>; - }; - vin1csi40: endpoint@2 { - reg = <2>; - remote-endpoint= <&csi40vin1>; - }; - }; - }; }; vin2: video@e6ef2000 { @@ -1962,27 +1920,6 @@ clocks = <&cpg CPG_MOD 809>; power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - - reg = <1>; - - vin2csi20: endpoint@0 { - reg = <0>; - remote-endpoint= <&csi20vin2>; - }; - vin2csi40: endpoint@2 { - reg = <2>; - remote-endpoint= <&csi40vin2>; - }; - }; - }; }; vin3: video@e6ef3000 { @@ -1992,27 +1929,6 @@ clocks = <&cpg CPG_MOD 808>; power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - - reg = <1>; - - vin3csi20: endpoint@0 { - reg = <0>; - remote-endpoint= <&csi20vin3>; - }; - vin3csi40: endpoint@2 { - reg = <2>; - remote-endpoint= <&csi40vin3>; - }; - }; - }; }; vin4: video@e6ef4000 { @@ -2022,27 +1938,6 @@ clocks = <&cpg CPG_MOD 807>; power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - - reg = <1>; - - vin4csi20: endpoint@0 { - reg = <0>; - remote-endpoint= <&csi20vin4>; - }; - vin4csi41: endpoint@3 { - reg = <3>; - remote-endpoint= <&csi41vin4>; - }; - }; - }; }; vin5: video@e6ef5000 { @@ -2052,27 +1947,6 @@ clocks = <&cpg CPG_MOD 806>; power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - - reg = <1>; - - vin5csi20: endpoint@0 { - reg = <0>; - remote-endpoint= <&csi20vin5>; - }; - vin5csi41: endpoint@3 { - reg = <3>; - remote-endpoint= <&csi41vin5>; - }; - }; - }; }; vin6: video@e6ef6000 { @@ -2082,27 +1956,6 @@ clocks = <&cpg CPG_MOD 805>; power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - - reg = <1>; - - vin6csi20: endpoint@0 { - reg = <0>; - remote-endpoint= <&csi20vin6>; - }; - vin6csi41: endpoint@3 { - reg = <3>; - remote-endpoint= <&csi41vin6>; - }; - }; - }; }; vin7: video@e6ef7000 { @@ -2112,27 +1965,6 @@ clocks = <&cpg CPG_MOD 804>; power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - - reg = <1>; - - vin7csi20: endpoint@0 { - reg = <0>; - remote-endpoint= <&csi20vin7>; - }; - vin7csi41: endpoint@3 { - reg = <3>; - remote-endpoint= <&csi41vin7>; - }; - }; - }; }; csi2_20: csi2@fea80000 { @@ -2142,51 +1974,6 @@ clocks = <&cpg CPG_MOD 714>; power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - - reg = <1>; - - csi20vin0: endpoint@0 { - reg = <0>; - remote-endpoint = <&vin0csi20>; - }; - csi20vin1: endpoint@1 { - reg = <1>; - remote-endpoint = <&vin1csi20>; - }; - csi20vin2: endpoint@2 { - reg = <2>; - remote-endpoint = <&vin2csi20>; - }; - csi20vin3: endpoint@3 { - reg = <3>; - remote-endpoint = <&vin3csi20>; - }; - csi20vin4: endpoint@4 { - reg = <4>; - remote-endpoint = <&vin4csi20>; - }; - csi20vin5: endpoint@5 { - reg = <5>; - remote-endpoint = <&vin5csi20>; - }; - csi20vin6: endpoint@6 { - reg = <6>; - remote-endpoint = <&vin6csi20>; - }; - csi20vin7: endpoint@7 { - reg = <7>; - remote-endpoint = <&vin7csi20>; - }; - }; - }; }; csi2_40: csi2@feaa0000 { @@ -2196,35 +1983,6 @@ clocks = <&cpg CPG_MOD 716>; power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - - reg = <1>; - - csi40vin0: endpoint@0 { - reg = <0>; - remote-endpoint = <&vin0csi40>; - }; - csi40vin1: endpoint@1 { - reg = <1>; - remote-endpoint = <&vin1csi40>; - }; - csi40vin2: endpoint@2 { - reg = <2>; - remote-endpoint = <&vin2csi40>; - }; - csi40vin3: endpoint@3 { - reg = <3>; - remote-endpoint = <&vin3csi40>; - }; - }; - }; }; csi2_41: csi2@feab0000 { @@ -2234,35 +1992,6 @@ clocks = <&cpg CPG_MOD 715>; power-domains = <&sysc R8A7795_PD_ALWAYS_ON>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - - reg = <1>; - - csi41vin4: endpoint@4 { - reg = <4>; - remote-endpoint = <&vin4csi41>; - }; - csi41vin5: endpoint@5 { - reg = <5>; - remote-endpoint = <&vin5csi41>; - }; - csi41vin6: endpoint@6 { - reg = <6>; - remote-endpoint = <&vin6csi41>; - }; - csi41vin7: endpoint@7 { - reg = <7>; - remote-endpoint = <&vin7csi41>; - }; - }; - }; }; sata: sata@ee300000 { diff --git a/arch/arm64/boot/dts/renesas/r8a7796.dtsi b/arch/arm64/boot/dts/renesas/r8a7796.dtsi index afdd69d..e653814 100644 --- a/arch/arm64/boot/dts/renesas/r8a7796.dtsi +++ b/arch/arm64/boot/dts/renesas/r8a7796.dtsi @@ -1592,27 +1592,6 @@ clocks = <&cpg CPG_MOD 811>; power-domains = <&sysc R8A7796_PD_ALWAYS_ON>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - - reg = <1>; - - vin0csi20: endpoint@0 { - reg = <0>; - remote-endpoint= <&csi20vin0>; - }; - vin0csi40: endpoint@2 { - reg = <2>; - remote-endpoint= <&csi40vin0>; - }; - }; - }; }; vin1: video@e6ef1000 { @@ -1622,27 +1601,6 @@ clocks = <&cpg CPG_MOD 810>; power-domains = <&sysc R8A7796_PD_ALWAYS_ON>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - - reg = <1>; - - vin1csi20: endpoint@0 { - reg = <0>; - remote-endpoint= <&csi20vin1>; - }; - vin1csi40: endpoint@2 { - reg = <2>; - remote-endpoint= <&csi40vin1>; - }; - }; - }; }; vin2: video@e6ef2000 { @@ -1652,27 +1610,6 @@ clocks = <&cpg CPG_MOD 809>; power-domains = <&sysc R8A7796_PD_ALWAYS_ON>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - - reg = <1>; - - vin2csi20: endpoint@0 { - reg = <0>; - remote-endpoint= <&csi20vin2>; - }; - vin2csi40: endpoint@2 { - reg = <2>; - remote-endpoint= <&csi40vin2>; - }; - }; - }; }; vin3: video@e6ef3000 { @@ -1682,27 +1619,6 @@ clocks = <&cpg CPG_MOD 808>; power-domains = <&sysc R8A7796_PD_ALWAYS_ON>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - - reg = <1>; - - vin3csi20: endpoint@0 { - reg = <0>; - remote-endpoint= <&csi20vin3>; - }; - vin3csi40: endpoint@2 { - reg = <2>; - remote-endpoint= <&csi40vin3>; - }; - }; - }; }; vin4: video@e6ef4000 { @@ -1712,27 +1628,6 @@ clocks = <&cpg CPG_MOD 807>; power-domains = <&sysc R8A7796_PD_ALWAYS_ON>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - - reg = <1>; - - vin4csi20: endpoint@0 { - reg = <0>; - remote-endpoint= <&csi20vin4>; - }; - vin4csi40: endpoint@2 { - reg = <2>; - remote-endpoint= <&csi40vin4>; - }; - }; - }; }; vin5: video@e6ef5000 { @@ -1742,27 +1637,6 @@ clocks = <&cpg CPG_MOD 806>; power-domains = <&sysc R8A7796_PD_ALWAYS_ON>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - - reg = <1>; - - vin5csi20: endpoint@0 { - reg = <0>; - remote-endpoint= <&csi20vin5>; - }; - vin5csi40: endpoint@2 { - reg = <2>; - remote-endpoint= <&csi40vin5>; - }; - }; - }; }; vin6: video@e6ef6000 { @@ -1772,27 +1646,6 @@ clocks = <&cpg CPG_MOD 805>; power-domains = <&sysc R8A7796_PD_ALWAYS_ON>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - - reg = <1>; - - vin6csi20: endpoint@0 { - reg = <0>; - remote-endpoint= <&csi20vin6>; - }; - vin6csi40: endpoint@2 { - reg = <2>; - remote-endpoint= <&csi40vin6>; - }; - }; - }; }; vin7: video@e6ef7000 { @@ -1802,27 +1655,6 @@ clocks = <&cpg CPG_MOD 804>; power-domains = <&sysc R8A7796_PD_ALWAYS_ON>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - - reg = <1>; - - vin7csi20: endpoint@0 { - reg = <0>; - remote-endpoint= <&csi20vin7>; - }; - vin7csi40: endpoint@2 { - reg = <2>; - remote-endpoint= <&csi40vin7>; - }; - }; - }; }; csi2_20: csi2@fea80000 { @@ -1832,51 +1664,6 @@ clocks = <&cpg CPG_MOD 714>; power-domains = <&sysc R8A7796_PD_ALWAYS_ON>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - - reg = <1>; - - csi20vin0: endpoint@0 { - reg = <0>; - remote-endpoint = <&vin0csi20>; - }; - csi20vin1: endpoint@1 { - reg = <1>; - remote-endpoint = <&vin1csi20>; - }; - csi20vin2: endpoint@2 { - reg = <2>; - remote-endpoint = <&vin2csi20>; - }; - csi20vin3: endpoint@3 { - reg = <3>; - remote-endpoint = <&vin3csi20>; - }; - csi20vin4: endpoint@4 { - reg = <4>; - remote-endpoint = <&vin4csi20>; - }; - csi20vin5: endpoint@5 { - reg = <5>; - remote-endpoint = <&vin5csi20>; - }; - csi20vin6: endpoint@6 { - reg = <6>; - remote-endpoint = <&vin6csi20>; - }; - csi20vin7: endpoint@7 { - reg = <7>; - remote-endpoint = <&vin7csi20>; - }; - }; - }; }; csi2_40: csi2@feaa0000 { @@ -1886,51 +1673,6 @@ clocks = <&cpg CPG_MOD 716>; power-domains = <&sysc R8A7796_PD_ALWAYS_ON>; status = "disabled"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@1 { - #address-cells = <1>; - #size-cells = <0>; - - reg = <1>; - - csi40vin0: endpoint@0 { - reg = <0>; - remote-endpoint = <&vin0csi40>; - }; - csi40vin1: endpoint@1 { - reg = <1>; - remote-endpoint = <&vin1csi40>; - }; - csi40vin2: endpoint@2 { - reg = <2>; - remote-endpoint = <&vin2csi40>; - }; - csi40vin3: endpoint@3 { - reg = <3>; - remote-endpoint = <&vin3csi40>; - }; - csi40vin4: endpoint@4 { - reg = <4>; - remote-endpoint = <&vin4csi40>; - }; - csi40vin5: endpoint@5 { - reg = <5>; - remote-endpoint = <&vin5csi40>; - }; - csi40vin6: endpoint@6 { - reg = <6>; - remote-endpoint = <&vin6csi40>; - }; - csi40vin7: endpoint@7 { - reg = <7>; - remote-endpoint = <&vin7csi40>; - }; - }; - }; }; vcplf: vcp4@fe910000 { diff --git a/drivers/media/platform/soc_camera/Kconfig b/drivers/media/platform/soc_camera/Kconfig index 86d7478..17178ad 100644 --- a/drivers/media/platform/soc_camera/Kconfig +++ b/drivers/media/platform/soc_camera/Kconfig @@ -17,6 +17,32 @@ config SOC_CAMERA_PLATFORM help This is a generic SoC camera platform driver, useful for testing +config VIDEO_RCAR_VIN_LEGACY + tristate "R-Car Video Input (VIN) support" + depends on VIDEO_DEV && SOC_CAMERA + depends on ARCH_RENESAS || COMPILE_TEST + depends on HAS_DMA + select VIDEOBUF2_DMA_CONTIG + select SOC_CAMERA_SCALE_CROP + ---help--- + This is a v4l2 driver for the R-Car VIN Interface + +config VIDEO_RCAR_VIN_LEGACY_DEBUG + bool "Renesas VIN overflow debug messages" + depends on VIDEO_RCAR_VIN_LEGACY + ---help--- + Enable debug overflow messages on R-Car Video + Input driver. + If you set to enable, When an overflow occurred, + a debug overflow message is output. + +config VIDEO_RCAR_CSI2_LEGACY + tristate "R-Car MIPI CSI-2 Interface driver" + depends on VIDEO_DEV && SOC_CAMERA && HAVE_CLK + depends on ARCH_R8A7795 || ARCH_R8A7796 || COMPILE_TEST + ---help--- + This is a v4l2 driver for the R-Car CSI-2 Interface + config VIDEO_SH_MOBILE_CEU tristate "SuperH Mobile CEU Interface driver" depends on VIDEO_DEV && SOC_CAMERA && HAS_DMA && HAVE_CLK diff --git a/drivers/media/platform/soc_camera/Makefile b/drivers/media/platform/soc_camera/Makefile index 7633a0f..8c7ede6 100644 --- a/drivers/media/platform/soc_camera/Makefile +++ b/drivers/media/platform/soc_camera/Makefile @@ -8,3 +8,5 @@ obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o # soc-camera host drivers have to be linked after camera drivers obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel-isi.o obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o +obj-$(CONFIG_VIDEO_RCAR_CSI2_LEGACY) += rcar_csi2.o +obj-$(CONFIG_VIDEO_RCAR_VIN_LEGACY) += rcar_vin.o diff --git a/drivers/media/platform/soc_camera/rcar_csi2.c b/drivers/media/platform/soc_camera/rcar_csi2.c new file mode 100644 index 0000000..05f623468 --- /dev/null +++ b/drivers/media/platform/soc_camera/rcar_csi2.c @@ -0,0 +1,708 @@ +/* + * drivers/media/platform/soc_camera/rcar_csi2.c + * This file is the driver for the R-Car MIPI CSI-2 unit. + * + * Copyright (C) 2015-2016 Renesas Electronics Corporation + * + * This file is based on the drivers/media/platform/soc_camera/sh_mobile_csi2.c + * + * Driver for the SH-Mobile MIPI CSI-2 unit + * + * Copyright (C) 2010, Guennadi Liakhovetski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DRV_NAME "rcar_csi2" +#define CONNECT_SLAVE_NAME "adv7482" +#define VC_MAX_CHANNEL 4 + +#define RCAR_CSI2_TREF 0x00 +#define RCAR_CSI2_SRST 0x04 +#define RCAR_CSI2_PHYCNT 0x08 +#define RCAR_CSI2_CHKSUM 0x0C +#define RCAR_CSI2_VCDT 0x10 + +#define RCAR_CSI2_VCDT2 0x14 /* Channel Data Type Select */ +#define RCAR_CSI2_FRDT 0x18 /* Frame Data Type Select */ +#define RCAR_CSI2_FLD 0x1C /* Field Detection Control */ +#define RCAR_CSI2_ASTBY 0x20 /* Automatic standby control */ +#define RCAR_CSI2_LNGDT0 0x28 +#define RCAR_CSI2_LNGDT1 0x2C +#define RCAR_CSI2_INTEN 0x30 +#define RCAR_CSI2_INTCLOSE 0x34 +#define RCAR_CSI2_INTSTATE 0x38 +#define RCAR_CSI2_INTERRSTATE 0x3C + +#define RCAR_CSI2_SHPDAT 0x40 +#define RCAR_CSI2_SHPCNT 0x44 + +#define RCAR_CSI2_LINKCNT 0x48 +#define RCAR_CSI2_LSWAP 0x4C +#define RCAR_CSI2_PHTC 0x58 +#define RCAR_CSI2_PHYPLL 0x68 + +#define RCAR_CSI2_PHEERM 0x74 +#define RCAR_CSI2_PHCLM 0x78 +#define RCAR_CSI2_PHDLM 0x7C + +#define RCAR_CSI2_PHYCNT_SHUTDOWNZ (1 << 17) +#define RCAR_CSI2_PHYCNT_RSTZ (1 << 16) +#define RCAR_CSI2_PHYCNT_ENABLECLK (1 << 4) +#define RCAR_CSI2_PHYCNT_ENABLE_3 (1 << 3) +#define RCAR_CSI2_PHYCNT_ENABLE_2 (1 << 2) +#define RCAR_CSI2_PHYCNT_ENABLE_1 (1 << 1) +#define RCAR_CSI2_PHYCNT_ENABLE_0 (1 << 0) + +#define RCAR_CSI2_VCDT_VCDTN_EN (1 << 15) +#define RCAR_CSI2_VCDT_SEL_VCN (1 << 8) +#define RCAR_CSI2_VCDT_SEL_DTN_ON (1 << 6) +#define RCAR_CSI2_VCDT_SEL_DTN (1 << 0) + +#define RCAR_CSI2_LINKCNT_MONITOR_EN (1 << 31) +#define RCAR_CSI2_LINKCNT_REG_MONI_PACT_EN (1 << 25) + +#define RCAR_CSI2_LSWAP_L3SEL_PLANE0 (0 << 6) +#define RCAR_CSI2_LSWAP_L3SEL_PLANE1 (1 << 6) +#define RCAR_CSI2_LSWAP_L3SEL_PLANE2 (2 << 6) +#define RCAR_CSI2_LSWAP_L3SEL_PLANE3 (3 << 6) + +#define RCAR_CSI2_LSWAP_L2SEL_PLANE0 (0 << 4) +#define RCAR_CSI2_LSWAP_L2SEL_PLANE1 (1 << 4) +#define RCAR_CSI2_LSWAP_L2SEL_PLANE2 (2 << 4) +#define RCAR_CSI2_LSWAP_L2SEL_PLANE3 (3 << 4) + +#define RCAR_CSI2_LSWAP_L1SEL_PLANE0 (0 << 2) +#define RCAR_CSI2_LSWAP_L1SEL_PLANE1 (1 << 2) +#define RCAR_CSI2_LSWAP_L1SEL_PLANE2 (2 << 2) +#define RCAR_CSI2_LSWAP_L1SEL_PLANE3 (3 << 2) + +#define RCAR_CSI2_LSWAP_L0SEL_PLANE0 (0 << 0) +#define RCAR_CSI2_LSWAP_L0SEL_PLANE1 (1 << 0) +#define RCAR_CSI2_LSWAP_L0SEL_PLANE2 (2 << 0) +#define RCAR_CSI2_LSWAP_L0SEL_PLANE3 (3 << 0) + +#define RCAR_CSI2_PHTC_TESTCLR (1 << 0) + +/* interrupt status registers */ +#define RCAR_CSI2_INTSTATE_EBD_CH1 (1 << 29) +#define RCAR_CSI2_INTSTATE_LESS_THAN_WC (1 << 28) +#define RCAR_CSI2_INTSTATE_AFIFO_OF (1 << 27) +#define RCAR_CSI2_INTSTATE_VD4_START (1 << 26) +#define RCAR_CSI2_INTSTATE_VD4_END (1 << 25) +#define RCAR_CSI2_INTSTATE_VD3_START (1 << 24) +#define RCAR_CSI2_INTSTATE_VD3_END (1 << 23) +#define RCAR_CSI2_INTSTATE_VD2_START (1 << 22) +#define RCAR_CSI2_INTSTATE_VD2_END (1 << 21) +#define RCAR_CSI2_INTSTATE_VD1_START (1 << 20) +#define RCAR_CSI2_INTSTATE_VD1_END (1 << 19) +#define RCAR_CSI2_INTSTATE_SHP (1 << 18) +#define RCAR_CSI2_INTSTATE_FSFE (1 << 17) +#define RCAR_CSI2_INTSTATE_LNP (1 << 16) +#define RCAR_CSI2_INTSTATE_CRC_ERR (1 << 15) +#define RCAR_CSI2_INTSTATE_HD_WC_ZERO (1 << 14) +#define RCAR_CSI2_INTSTATE_FRM_SEQ_ERR1 (1 << 13) +#define RCAR_CSI2_INTSTATE_FRM_SEQ_ERR2 (1 << 12) +#define RCAR_CSI2_INTSTATE_ECC_ERR (1 << 11) +#define RCAR_CSI2_INTSTATE_ECC_CRCT_ERR (1 << 10) +#define RCAR_CSI2_INTSTATE_LPDT_START (1 << 9) +#define RCAR_CSI2_INTSTATE_LPDT_END (1 << 8) +#define RCAR_CSI2_INTSTATE_ULPS_START (1 << 7) +#define RCAR_CSI2_INTSTATE_ULPS_END (1 << 6) +#define RCAR_CSI2_INTSTATE_RESERVED (1 << 5) +#define RCAR_CSI2_INTSTATE_ERRSOTHS (1 << 4) +#define RCAR_CSI2_INTSTATE_ERRSOTSYNCCHS (1 << 3) +#define RCAR_CSI2_INTSTATE_ERRESC (1 << 2) +#define RCAR_CSI2_INTSTATE_ERRSYNCESC (1 << 1) +#define RCAR_CSI2_INTSTATE_ERRCONTROL (1 << 0) + +/* monitoring registers of interrupt error status */ +#define RCAR_CSI2_INTSTATE_ECC_ERR (1 << 11) +#define RCAR_CSI2_INTSTATE_ECC_CRCT_ERR (1 << 10) +#define RCAR_CSI2_INTSTATE_LPDT_START (1 << 9) +#define RCAR_CSI2_INTSTATE_LPDT_END (1 << 8) +#define RCAR_CSI2_INTSTATE_ULPS_START (1 << 7) +#define RCAR_CSI2_INTSTATE_ULPS_END (1 << 6) +#define RCAR_CSI2_INTSTATE_RESERVED (1 << 5) +#define RCAR_CSI2_INTSTATE_ERRSOTHS (1 << 4) +#define RCAR_CSI2_INTSTATE_ERRSOTSYNCCHS (1 << 3) +#define RCAR_CSI2_INTSTATE_ERRESC (1 << 2) +#define RCAR_CSI2_INTSTATE_ERRSYNCESC (1 << 1) +#define RCAR_CSI2_INTSTATE_ERRCONTROL (1 << 0) + +enum chip_id { + RCAR_GEN3, + RCAR_GEN2, +}; + +enum decoder_input_interface { + DECODER_INPUT_INTERFACE_RGB888, + DECODER_INPUT_INTERFACE_YCBCR422, + DECODER_INPUT_INTERFACE_NONE, +}; + +/** + * struct rcar_csi2_link_config - Describes rcar_csi2 hardware configuration + * @input_colorspace: The input colorspace (RGB, YUV444, YUV422) + */ +struct rcar_csi2_link_config { + enum decoder_input_interface input_interface; + unsigned char lanes; + unsigned long vcdt; + unsigned long vcdt2; +}; + +#define INIT_RCAR_CSI2_LINK_CONFIG(m) \ +{ \ + m.input_interface = DECODER_INPUT_INTERFACE_NONE; \ + m.lanes = 0; \ +} + +struct rcar_csi_irq_counter_log { + unsigned long crc_err; +}; + +struct rcar_csi2 { + struct v4l2_subdev subdev; + struct v4l2_mbus_framefmt *mf; + unsigned int irq; + unsigned long mipi_flags; + void __iomem *base; + struct platform_device *pdev; + struct rcar_csi2_client_config *client; + unsigned long vcdt; + unsigned long vcdt2; + + unsigned int field; + unsigned int code; + unsigned int lanes; + spinlock_t lock; +}; + +#define RCAR_CSI_80MBPS 0 +#define RCAR_CSI_90MBPS 1 +#define RCAR_CSI_100MBPS 2 +#define RCAR_CSI_110MBPS 3 +#define RCAR_CSI_120MBPS 4 +#define RCAR_CSI_130MBPS 5 +#define RCAR_CSI_140MBPS 6 +#define RCAR_CSI_150MBPS 7 +#define RCAR_CSI_160MBPS 8 +#define RCAR_CSI_170MBPS 9 +#define RCAR_CSI_180MBPS 10 +#define RCAR_CSI_190MBPS 11 +#define RCAR_CSI_205MBPS 12 +#define RCAR_CSI_220MBPS 13 +#define RCAR_CSI_235MBPS 14 +#define RCAR_CSI_250MBPS 15 +#define RCAR_CSI_275MBPS 16 +#define RCAR_CSI_300MBPS 17 +#define RCAR_CSI_325MBPS 18 +#define RCAR_CSI_350MBPS 19 +#define RCAR_CSI_400MBPS 20 +#define RCAR_CSI_450MBPS 21 +#define RCAR_CSI_500MBPS 22 +#define RCAR_CSI_550MBPS 23 +#define RCAR_CSI_600MBPS 24 +#define RCAR_CSI_650MBPS 25 +#define RCAR_CSI_700MBPS 26 +#define RCAR_CSI_750MBPS 27 +#define RCAR_CSI_800MBPS 28 +#define RCAR_CSI_850MBPS 29 +#define RCAR_CSI_900MBPS 30 +#define RCAR_CSI_950MBPS 31 +#define RCAR_CSI_1000MBPS 32 +#define RCAR_CSI_1050MBPS 33 +#define RCAR_CSI_1100MBPS 34 +#define RCAR_CSI_1150MBPS 35 +#define RCAR_CSI_1200MBPS 36 +#define RCAR_CSI_1250MBPS 37 +#define RCAR_CSI_1300MBPS 38 +#define RCAR_CSI_1350MBPS 39 +#define RCAR_CSI_1400MBPS 40 +#define RCAR_CSI_1450MBPS 41 +#define RCAR_CSI_1500MBPS 42 + +static int rcar_csi2_set_phy_freq(struct rcar_csi2 *priv) +{ + const uint32_t const hs_freq_range[43] = { + 0x00, 0x10, 0x20, 0x30, 0x01, /* 0-4 */ + 0x11, 0x21, 0x31, 0x02, 0x12, /* 5-9 */ + 0x22, 0x32, 0x03, 0x13, 0x23, /* 10-14 */ + 0x33, 0x04, 0x14, 0x05, 0x15, /* 15-19 */ + 0x25, 0x06, 0x16, 0x07, 0x17, /* 20-24 */ + 0x08, 0x18, 0x09, 0x19, 0x29, /* 25-29 */ + 0x39, 0x0A, 0x1A, 0x2A, 0x3A, /* 30-34 */ + 0x0B, 0x1B, 0x2B, 0x3B, 0x0C, /* 35-39 */ + 0x1C, 0x2C, 0x3C /* 40-42 */ + }; + uint32_t bps_per_lane = RCAR_CSI_190MBPS; + + dev_dbg(&priv->pdev->dev, "Input size (%dx%d%c)\n", + priv->mf->width, priv->mf->height, + (priv->mf->field == V4L2_FIELD_NONE) ? 'p' : 'i'); + + switch (priv->lanes) { + case 1: + bps_per_lane = RCAR_CSI_400MBPS; + break; + case 4: + if (priv->mf->field == V4L2_FIELD_NONE) { + if ((priv->mf->width == 1920) && + (priv->mf->height == 1080)) + bps_per_lane = RCAR_CSI_900MBPS; + else if ((priv->mf->width == 1280) && + (priv->mf->height == 720)) + bps_per_lane = RCAR_CSI_450MBPS; + else if ((priv->mf->width == 720) && + (priv->mf->height == 480)) + bps_per_lane = RCAR_CSI_190MBPS; + else if ((priv->mf->width == 720) && + (priv->mf->height == 576)) + bps_per_lane = RCAR_CSI_190MBPS; + else if ((priv->mf->width == 640) && + (priv->mf->height == 480)) + bps_per_lane = RCAR_CSI_100MBPS; + else + goto error; + } else { + if ((priv->mf->width == 1920) && + (priv->mf->height == 1080)) + bps_per_lane = RCAR_CSI_450MBPS; + else + goto error; + } + break; + default: + dev_err(&priv->pdev->dev, "ERROR: lanes is invalid (%d)\n", + priv->lanes); + return -EINVAL; + } + + dev_dbg(&priv->pdev->dev, "bps_per_lane (%d)\n", bps_per_lane); + + iowrite32((hs_freq_range[bps_per_lane] << 16), + priv->base + RCAR_CSI2_PHYPLL); + return 0; + +error: + dev_err(&priv->pdev->dev, "Not support resolution (%dx%d%c)\n", + priv->mf->width, priv->mf->height, + (priv->mf->field == V4L2_FIELD_NONE) ? 'p' : 'i'); + return -EINVAL; +} + +static irqreturn_t rcar_csi2_irq(int irq, void *data) +{ + struct rcar_csi2 *priv = data; + u32 int_status; + unsigned int handled = 0; + + spin_lock(&priv->lock); + + int_status = ioread32(priv->base + RCAR_CSI2_INTSTATE); + if (!int_status) + goto done; + + /* ack interrupts */ + iowrite32(int_status, priv->base + RCAR_CSI2_INTSTATE); + handled = 1; + +done: + spin_unlock(&priv->lock); + + return IRQ_RETVAL(handled); + +} + +static void rcar_csi2_hwdeinit(struct rcar_csi2 *priv) +{ + iowrite32(0, priv->base + RCAR_CSI2_PHYCNT); + + /* reset CSI2 hardware */ + iowrite32(0x00000001, priv->base + RCAR_CSI2_SRST); + udelay(5); + iowrite32(0x00000000, priv->base + RCAR_CSI2_SRST); +} + +static int rcar_csi2_hwinit(struct rcar_csi2 *priv) +{ + int ret; + __u32 tmp = 0x10; /* Enable MIPI CSI clock lane */ + + /* Reflect registers immediately */ + iowrite32(0x00000001, priv->base + RCAR_CSI2_TREF); + /* reset CSI2 hardware */ + iowrite32(0x00000001, priv->base + RCAR_CSI2_SRST); + udelay(5); + iowrite32(0x00000000, priv->base + RCAR_CSI2_SRST); + + iowrite32(0x00000000, priv->base + RCAR_CSI2_PHTC); + + /* setting HS reception frequency */ + { + switch (priv->lanes) { + case 1: + /* First field number setting */ + iowrite32(0x0001000f, priv->base + RCAR_CSI2_FLD); + tmp |= 0x1; + break; + case 4: + /* First field number setting */ + iowrite32(0x0002000f, priv->base + RCAR_CSI2_FLD); + tmp |= 0xF; + break; + default: + dev_err(&priv->pdev->dev, + "ERROR: lanes is invalid (%d)\n", + priv->lanes); + return -EINVAL; + } + + /* set PHY frequency */ + ret = rcar_csi2_set_phy_freq(priv); + if (ret < 0) + return ret; + + /* Enable lanes */ + iowrite32(tmp, priv->base + RCAR_CSI2_PHYCNT); + + iowrite32(tmp | RCAR_CSI2_PHYCNT_SHUTDOWNZ, + priv->base + RCAR_CSI2_PHYCNT); + iowrite32(tmp | (RCAR_CSI2_PHYCNT_SHUTDOWNZ | + RCAR_CSI2_PHYCNT_RSTZ), + priv->base + RCAR_CSI2_PHYCNT); + } + + iowrite32(0x00000003, priv->base + RCAR_CSI2_CHKSUM); + iowrite32(priv->vcdt, priv->base + RCAR_CSI2_VCDT); + iowrite32(priv->vcdt2, priv->base + RCAR_CSI2_VCDT2); + iowrite32(0x00010000, priv->base + RCAR_CSI2_FRDT); + udelay(10); + iowrite32(0x83000000, priv->base + RCAR_CSI2_LINKCNT); + iowrite32(0x000000e4, priv->base + RCAR_CSI2_LSWAP); + + dev_dbg(&priv->pdev->dev, "CSI2 VCDT: 0x%x\n", + ioread32(priv->base + RCAR_CSI2_VCDT)); + dev_dbg(&priv->pdev->dev, "CSI2 VCDT2: 0x%x\n", + ioread32(priv->base + RCAR_CSI2_VCDT2)); + + /* wait until video decoder power off */ + msleep(10); + { + int timeout = 100; + + /* Read the PHY clock lane monitor register (PHCLM). */ + while (!(ioread32(priv->base + RCAR_CSI2_PHCLM) & 0x01) + && timeout) { + timeout--; + } + if (timeout == 0) + dev_err(&priv->pdev->dev, + "Timeout of reading the PHY clock lane\n"); + else + dev_dbg(&priv->pdev->dev, + "Detected the PHY clock lane\n"); + + timeout = 100; + + /* Read the PHY data lane monitor register (PHDLM). */ + while (!(ioread32(priv->base + RCAR_CSI2_PHDLM) & 0x01) + && timeout) { + timeout--; + } + if (timeout == 0) + dev_err(&priv->pdev->dev, + "Timeout of reading the PHY data lane\n"); + else + dev_dbg(&priv->pdev->dev, + "Detected the PHY data lane\n"); + } + + return 0; +} + +static int rcar_csi2_s_power(struct v4l2_subdev *sd, int on) +{ + struct rcar_csi2 *priv = container_of(sd, struct rcar_csi2, subdev); + struct v4l2_subdev *tmp_sd; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mf = &fmt.format; + int ret = 0; + + if (on) { + v4l2_device_for_each_subdev(tmp_sd, sd->v4l2_dev) { + if (strncmp(tmp_sd->name, CONNECT_SLAVE_NAME, + sizeof(CONNECT_SLAVE_NAME) - 1) == 0) { + v4l2_subdev_call(tmp_sd, pad, get_fmt, + NULL, &fmt); + if (ret < 0) + return ret; + } + } + priv->mf = mf; + pm_runtime_get_sync(&priv->pdev->dev); + ret = rcar_csi2_hwinit(priv); + if (ret < 0) + return ret; + } else { + rcar_csi2_hwdeinit(priv); + pm_runtime_put_sync(&priv->pdev->dev); + } + + return ret; +} + +static struct v4l2_subdev_core_ops rcar_csi2_subdev_core_ops = { + .s_power = rcar_csi2_s_power, +}; + +static struct v4l2_subdev_ops rcar_csi2_subdev_ops = { + .core = &rcar_csi2_subdev_core_ops, +}; + +#ifdef CONFIG_OF +static const struct of_device_id rcar_csi2_of_table[] = { + { .compatible = "renesas,r8a7796-csi2", .data = (void *)RCAR_GEN3 }, + { .compatible = "renesas,r8a7795-csi2", .data = (void *)RCAR_GEN3 }, + { }, +}; +MODULE_DEVICE_TABLE(of, rcar_csi2_of_table); +#endif + +static struct platform_device_id rcar_csi2_id_table[] = { + { "r8a7796-csi2", RCAR_GEN3 }, + { "r8a7795-csi2", RCAR_GEN3 }, + {}, +}; +MODULE_DEVICE_TABLE(platform, rcar_csi2_id_table); + +static int rcar_csi2_parse_dt(struct device_node *np, + struct rcar_csi2_link_config *config) +{ + struct v4l2_of_endpoint bus_cfg; + struct device_node *endpoint; + struct device_node *vc_np, *vc_ch; + const char *str; + char csi_name[9]; + int ret; + int i, ch; + + /* Parse the endpoint. */ + endpoint = of_graph_get_next_endpoint(np, NULL); + if (!endpoint) + return -EINVAL; + + v4l2_of_parse_endpoint(endpoint, &bus_cfg); + of_node_put(endpoint); + + config->lanes = bus_cfg.bus.mipi_csi2.num_data_lanes; + + ret = of_property_read_string(np, "adi,input-interface", &str); + if (ret < 0) + return ret; + + vc_np = of_get_child_by_name(np, "virtual,channel"); + + config->vcdt = 0; + config->vcdt2 = 0; + for (i = 0; i < VC_MAX_CHANNEL; i++) { + sprintf(csi_name, "csi2_vc%d", i); + + vc_ch = of_get_child_by_name(vc_np, csi_name); + if (!vc_ch) + continue; + ret = of_property_read_string(vc_ch, "data,type", &str); + if (ret < 0) + return ret; + ret = of_property_read_u32(vc_ch, "receive,vc", &ch); + if (ret < 0) + return ret; + + if (i < 2) { + if (!strcmp(str, "rgb888")) + config->vcdt |= (0x24 << (i * 16)); + else if (!strcmp(str, "ycbcr422")) + config->vcdt |= (0x1e << (i * 16)); + else + config->vcdt |= 0; + + config->vcdt |= (ch << (8 + (i * 16))); + config->vcdt |= (RCAR_CSI2_VCDT_VCDTN_EN << (i * 16)) | + (RCAR_CSI2_VCDT_SEL_DTN_ON << (i * 16)); + } + if (i >= 2) { + int j = (i - 2); + + if (!strcmp(str, "rgb888")) + config->vcdt2 |= (0x24 << (j * 16)); + else if (!strcmp(str, "ycbcr422")) + config->vcdt2 |= (0x1e << (j * 16)); + else + config->vcdt2 |= 0; + + config->vcdt2 |= (ch << (8 + (j * 16))); + config->vcdt2 |= (RCAR_CSI2_VCDT_VCDTN_EN << (j * 16)) | + (RCAR_CSI2_VCDT_SEL_DTN_ON << (j * 16)); + } + } + + return 0; +} + +static int rcar_csi2_probe(struct platform_device *pdev) +{ + struct resource *res; + unsigned int irq; + int ret; + struct rcar_csi2 *priv; + /* Platform data specify the PHY, lanes, ECC, CRC */ + struct rcar_csi2_pdata *pdata; + struct rcar_csi2_link_config link_config; + + dev_dbg(&pdev->dev, "CSI2 probed.\n"); + + INIT_RCAR_CSI2_LINK_CONFIG(link_config); + + if (pdev->dev.of_node) { + ret = rcar_csi2_parse_dt(pdev->dev.of_node, &link_config); + if (ret) + return ret; + + if (link_config.lanes == 4) + dev_info(&pdev->dev, + "Detected rgb888 in rcar_csi2_parse_dt\n"); + else + dev_info(&pdev->dev, + "Detected YCbCr422 in rcar_csi2_parse_dt\n"); + } else { + pdata = pdev->dev.platform_data; + if (!pdata) + return -EINVAL; + } + + priv = devm_kzalloc(&pdev->dev, sizeof(struct rcar_csi2), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + /* Interrupt unused so far */ + irq = platform_get_irq(pdev, 0); + + if (!res || (int)irq <= 0) { + dev_err(&pdev->dev, "Not enough CSI2 platform resources.\n"); + return -ENODEV; + } + + priv->irq = irq; + + priv->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + ret = devm_request_irq(&pdev->dev, irq, rcar_csi2_irq, IRQF_SHARED, + dev_name(&pdev->dev), priv); + if (ret) + return ret; + + priv->pdev = pdev; + priv->subdev.owner = THIS_MODULE; + priv->subdev.dev = &pdev->dev; + priv->lanes = link_config.lanes; + priv->vcdt = link_config.vcdt; + priv->vcdt2 = link_config.vcdt2; + + platform_set_drvdata(pdev, &priv->subdev); + + v4l2_subdev_init(&priv->subdev, &rcar_csi2_subdev_ops); + v4l2_set_subdevdata(&priv->subdev, &pdev->dev); + + snprintf(priv->subdev.name, V4L2_SUBDEV_NAME_SIZE, "rcar_csi2.%s", + dev_name(&pdev->dev)); + + ret = v4l2_async_register_subdev(&priv->subdev); + if (ret < 0) + return ret; + + spin_lock_init(&priv->lock); + + pm_runtime_enable(&pdev->dev); + + dev_dbg(&pdev->dev, "CSI2 probed.\n"); + + return 0; +} + +static int rcar_csi2_remove(struct platform_device *pdev) +{ + struct v4l2_subdev *subdev = platform_get_drvdata(pdev); + struct rcar_csi2 *priv = container_of(subdev, struct rcar_csi2, subdev); + + v4l2_async_unregister_subdev(&priv->subdev); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int rcar_csi2_suspend(struct device *dev) +{ + /* Empty function for now */ + return 0; +} + +static int rcar_csi2_resume(struct device *dev) +{ + /* Empty function for now */ + return 0; +} + +static SIMPLE_DEV_PM_OPS(rcar_csi2_pm_ops, + rcar_csi2_suspend, rcar_csi2_resume); +#define DEV_PM_OPS (&rcar_csi2_pm_ops) +#else +#define DEV_PM_OPS NULL +#endif /* CONFIG_PM_SLEEP */ + +static struct platform_driver __refdata rcar_csi2_pdrv = { + .remove = rcar_csi2_remove, + .probe = rcar_csi2_probe, + .driver = { + .name = DRV_NAME, + .pm = DEV_PM_OPS, + .of_match_table = of_match_ptr(rcar_csi2_of_table), + }, + .id_table = rcar_csi2_id_table, +}; + +module_platform_driver(rcar_csi2_pdrv); + +MODULE_DESCRIPTION("Renesas R-Car MIPI CSI-2 driver"); +MODULE_AUTHOR("Koji Matsuoka "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:rcar-csi2"); diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c new file mode 100644 index 0000000..400958b --- /dev/null +++ b/drivers/media/platform/soc_camera/rcar_vin.c @@ -0,0 +1,3071 @@ +/* + * SoC-camera host driver for Renesas R-Car VIN unit + * + * Copyright (C) 2015-2016 Renesas Electronics Corporation + * Copyright (C) 2011-2013 Renesas Solutions Corp. + * Copyright (C) 2013 Cogent Embedded, Inc., + * + * Based on V4L2 Driver for SuperH Mobile CEU interface "sh_mobile_ceu_camera.c" + * + * Copyright (C) 2008 Magnus Damm + * + * 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. + */ + +#ifdef CONFIG_VIDEO_RCAR_VIN_LEGACY_DEBUG +#define DEBUG +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "soc_scale_crop.h" + +#define DRV_NAME "rcar_vin" + +/* Register offsets for R-Car VIN */ +#define VNMC_REG 0x00 /* Video n Main Control Register */ +#define VNMS_REG 0x04 /* Video n Module Status Register */ +#define VNFC_REG 0x08 /* Video n Frame Capture Register */ +#define VNSLPRC_REG 0x0C /* Video n Start Line Pre-Clip Register */ +#define VNELPRC_REG 0x10 /* Video n End Line Pre-Clip Register */ +#define VNSPPRC_REG 0x14 /* Video n Start Pixel Pre-Clip Register */ +#define VNEPPRC_REG 0x18 /* Video n End Pixel Pre-Clip Register */ +#define VNSLPOC_REG 0x1C /* Video n Start Line Post-Clip Register */ +#define VNELPOC_REG 0x20 /* Video n End Line Post-Clip Register */ +#define VNSPPOC_REG 0x24 /* Video n Start Pixel Post-Clip Register */ +#define VNEPPOC_REG 0x28 /* Video n End Pixel Post-Clip Register */ +#define VNIS_REG 0x2C /* Video n Image Stride Register */ +#define VNMB_REG(m) (0x30 + ((m) << 2)) /* Video n Memory Base m Register */ +#define VNIE_REG 0x40 /* Video n Interrupt Enable Register */ +#define VNINTS_REG 0x44 /* Video n Interrupt Status Register */ +#define VNSI_REG 0x48 /* Video n Scanline Interrupt Register */ +#define VNMTC_REG 0x4C /* Video n Memory Transfer Control Register */ +#define VNYS_REG 0x50 /* Video n Y Scale Register */ +#define VNXS_REG 0x54 /* Video n X Scale Register */ +#define VNDMR_REG 0x58 /* Video n Data Mode Register */ +#define VNDMR2_REG 0x5C /* Video n Data Mode Register 2 */ +#define VNUVAOF_REG 0x60 /* Video n UV Address Offset Register */ +#define VNC1A_REG 0x80 /* Video n Coefficient Set C1A Register */ +#define VNC1B_REG 0x84 /* Video n Coefficient Set C1B Register */ +#define VNC1C_REG 0x88 /* Video n Coefficient Set C1C Register */ +#define VNC2A_REG 0x90 /* Video n Coefficient Set C2A Register */ +#define VNC2B_REG 0x94 /* Video n Coefficient Set C2B Register */ +#define VNC2C_REG 0x98 /* Video n Coefficient Set C2C Register */ +#define VNC3A_REG 0xA0 /* Video n Coefficient Set C3A Register */ +#define VNC3B_REG 0xA4 /* Video n Coefficient Set C3B Register */ +#define VNC3C_REG 0xA8 /* Video n Coefficient Set C3C Register */ +#define VNC4A_REG 0xB0 /* Video n Coefficient Set C4A Register */ +#define VNC4B_REG 0xB4 /* Video n Coefficient Set C4B Register */ +#define VNC4C_REG 0xB8 /* Video n Coefficient Set C4C Register */ +#define VNC5A_REG 0xC0 /* Video n Coefficient Set C5A Register */ +#define VNC5B_REG 0xC4 /* Video n Coefficient Set C5B Register */ +#define VNC5C_REG 0xC8 /* Video n Coefficient Set C5C Register */ +#define VNC6A_REG 0xD0 /* Video n Coefficient Set C6A Register */ +#define VNC6B_REG 0xD4 /* Video n Coefficient Set C6B Register */ +#define VNC6C_REG 0xD8 /* Video n Coefficient Set C6C Register */ +#define VNC7A_REG 0xE0 /* Video n Coefficient Set C7A Register */ +#define VNC7B_REG 0xE4 /* Video n Coefficient Set C7B Register */ +#define VNC7C_REG 0xE8 /* Video n Coefficient Set C7C Register */ +#define VNC8A_REG 0xF0 /* Video n Coefficient Set C8A Register */ +#define VNC8B_REG 0xF4 /* Video n Coefficient Set C8B Register */ +#define VNC8C_REG 0xF8 /* Video n Coefficient Set C8C Register */ + +/* Register bit fields for R-Car VIN */ +/* Video n Main Control Register bits */ +#define VNMC_DPINE (1 << 27) +#define VNMC_SCLE (1 << 26) +#define VNMC_FOC (1 << 21) +#define VNMC_YCAL (1 << 19) +#define VNMC_INF_YUV8_BT656 (0 << 16) +#define VNMC_INF_YUV8_BT601 (1 << 16) +#define VNMC_INF_YUV10_BT656 (2 << 16) +#define VNMC_INF_YUV10_BT601 (3 << 16) +#define VNMC_INF_YUV16 (5 << 16) +#define VNMC_INF_RGB888 (6 << 16) +#define VNMC_INF_MASK (7 << 16) +#define VNMC_VUP (1 << 10) +#define VNMC_IM_ODD (0 << 3) +#define VNMC_IM_ODD_EVEN (1 << 3) +#define VNMC_IM_EVEN (2 << 3) +#define VNMC_IM_FULL (3 << 3) +#define VNMC_BPS (1 << 1) +#define VNMC_ME (1 << 0) + +/* Video n Module Status Register bits */ +#define VNMS_FBS_MASK (3 << 3) +#define VNMS_FBS_SHIFT 3 +#define VNMS_AV (1 << 1) +#define VNMS_CA (1 << 0) + +/* Video n Frame Capture Register bits */ +#define VNFC_C_FRAME (1 << 1) +#define VNFC_S_FRAME (1 << 0) + +/* Video n Interrupt Enable Register bits */ +#define VNIE_FIE (1 << 4) +#define VNIE_EFE (1 << 1) +#define VNIE_FOE (1 << 0) + +/* Video n Interrupt Status Register bits */ +#define VNINTS_FIS (1 << 4) +#define VNINTS_EFS (1 << 1) +#define VNINTS_FOS (1 << 0) + +/* Video n Data Mode Register bits */ +#define VNDMR_EXRGB (1 << 8) +#define VNDMR_BPSM (1 << 4) +#define VNDMR_DTMD_YCSEP (1 << 1) +#define VNDMR_DTMD_ARGB (1 << 0) +#define VNDMR_DTMD_YCSEP_YCBCR420 (3 << 0) + +/* Video n Data Mode Register 2 bits */ +#define VNDMR2_VPS (1 << 30) +#define VNDMR2_HPS (1 << 29) +#define VNDMR2_FTEV (1 << 17) +#define VNDMR2_VLV(n) ((n & 0xf) << 12) + +/* setting CSI2 on R-Car Gen3*/ +#define VNCSI_IFMD_REG 0x20 /* Video n CSI2 Interface Mode Register */ + +#define VNCSI_IFMD_DES1 (1 << 26) /* CSI20 */ +#define VNCSI_IFMD_DES0 (1 << 25) /* H3:CSI40/41, M3:CSI40 */ + +#define VNCSI_IFMD_CSI_CHSEL(n) (n << 0) +#define VNCSI_IFMD_SEL_NUMBER 5 + +/* UDS */ +#define VNUDS_CTRL_REG 0x80 /* Scaling Control Registers */ +#define VNUDS_CTRL_AMD (1 << 30) +#define VNUDS_CTRL_BC (1 << 20) +#define VNUDS_CTRL_TDIPC (1 << 1) + +#define VNUDS_SCALE_REG 0x84 /* Scaling Factor Register */ +#define VNUDS_PASS_BWIDTH_REG 0x90 /* Passband Registers */ +#define VNUDS_IPC_REG 0x98 /* 2D IPC Setting Register */ +#define VNUDS_CLIP_SIZE_REG 0xA4 /* UDS Output Size Clipping Register */ + +#define TIMEOUT_MS 100 + +#define RCAR_VIN_HSYNC_ACTIVE_LOW (1 << 0) +#define RCAR_VIN_VSYNC_ACTIVE_LOW (1 << 1) +#define RCAR_VIN_BT601 (1 << 2) +#define RCAR_VIN_BT656 (1 << 3) +#define RCAR_VIN_CSI2 (1 << 4) + +static int ifmd0_reg_match[VNCSI_IFMD_SEL_NUMBER]; +static int ifmd4_reg_match[VNCSI_IFMD_SEL_NUMBER]; +static int ifmd0_init = true; +static int ifmd4_init = true; + +enum chip_id { + RCAR_GEN3, + RCAR_M3, + RCAR_H3, + RCAR_GEN2, + RCAR_H1, + RCAR_M1, + RCAR_E1, +}; + +enum csi2_ch { + RCAR_CSI_CH_NONE = -1, + RCAR_CSI40, + RCAR_CSI20, + RCAR_CSI41, + RCAR_CSI21, + RCAR_CSI_MAX, +}; + +enum gen3_vin_ch { + RCAR_VIN_CH_NONE = -1, + RCAR_VIDEO_0, + RCAR_VIDEO_1, + RCAR_VIDEO_2, + RCAR_VIDEO_3, + RCAR_VIDEO_4, + RCAR_VIDEO_5, + RCAR_VIDEO_6, + RCAR_VIDEO_7, + RCAR_VIDEO_MAX, +}; + +enum virtual_ch { + RCAR_VIRTUAL_NONE = -1, + RCAR_VIRTUAL_CH0, + RCAR_VIRTUAL_CH1, + RCAR_VIRTUAL_CH2, + RCAR_VIRTUAL_CH3, + RCAR_VIRTUAL_MAX, +}; + +struct vin_gen3_virtual_sel { + enum csi2_ch csi2_ch; + enum virtual_ch vc; +}; + +struct vin_gen3_ifmd { + unsigned int set_reg; + struct vin_gen3_virtual_sel v_sel[8]; +}; + +static const struct vin_gen3_ifmd vin_h3_vc_ifmd[] = { + { 0x0000, + { + {RCAR_CSI40, RCAR_VIRTUAL_CH0}, + {RCAR_CSI20, RCAR_VIRTUAL_CH0}, + {RCAR_CSI20, RCAR_VIRTUAL_CH1}, + {RCAR_CSI40, RCAR_VIRTUAL_CH1}, + {RCAR_CSI41, RCAR_VIRTUAL_CH0}, + {RCAR_CSI20, RCAR_VIRTUAL_CH0}, + {RCAR_CSI20, RCAR_VIRTUAL_CH1}, + {RCAR_CSI41, RCAR_VIRTUAL_CH1}, + } + }, + { 0x0001, + { + {RCAR_CSI20, RCAR_VIRTUAL_CH0}, + {RCAR_CSI40, RCAR_VIRTUAL_CH1}, + {RCAR_CSI40, RCAR_VIRTUAL_CH0}, + {RCAR_CSI20, RCAR_VIRTUAL_CH1}, + {RCAR_CSI20, RCAR_VIRTUAL_CH0}, + {RCAR_CSI41, RCAR_VIRTUAL_CH1}, + {RCAR_CSI41, RCAR_VIRTUAL_CH0}, + {RCAR_CSI20, RCAR_VIRTUAL_CH1}, + } + }, + { 0x0002, + { + {RCAR_CSI40, RCAR_VIRTUAL_CH1}, + {RCAR_CSI40, RCAR_VIRTUAL_CH0}, + {RCAR_CSI20, RCAR_VIRTUAL_CH0}, + {RCAR_CSI20, RCAR_VIRTUAL_CH1}, + {RCAR_CSI41, RCAR_VIRTUAL_CH1}, + {RCAR_CSI41, RCAR_VIRTUAL_CH0}, + {RCAR_CSI20, RCAR_VIRTUAL_CH0}, + {RCAR_CSI20, RCAR_VIRTUAL_CH1}, + } + }, + { 0x0003, + { + {RCAR_CSI40, RCAR_VIRTUAL_CH0}, + {RCAR_CSI40, RCAR_VIRTUAL_CH1}, + {RCAR_CSI40, RCAR_VIRTUAL_CH2}, + {RCAR_CSI40, RCAR_VIRTUAL_CH3}, + {RCAR_CSI41, RCAR_VIRTUAL_CH0}, + {RCAR_CSI41, RCAR_VIRTUAL_CH1}, + {RCAR_CSI41, RCAR_VIRTUAL_CH2}, + {RCAR_CSI41, RCAR_VIRTUAL_CH3}, + } + }, + { 0x0004, + { + {RCAR_CSI20, RCAR_VIRTUAL_CH0}, + {RCAR_CSI20, RCAR_VIRTUAL_CH1}, + {RCAR_CSI20, RCAR_VIRTUAL_CH2}, + {RCAR_CSI20, RCAR_VIRTUAL_CH3}, + {RCAR_CSI20, RCAR_VIRTUAL_CH0}, + {RCAR_CSI20, RCAR_VIRTUAL_CH1}, + {RCAR_CSI20, RCAR_VIRTUAL_CH2}, + {RCAR_CSI20, RCAR_VIRTUAL_CH3}, + } + }, +}; + +static const struct vin_gen3_ifmd vin_m3_vc_ifmd[] = { + { 0x0000, + { + {RCAR_CSI40, RCAR_VIRTUAL_CH0}, + {RCAR_CSI20, RCAR_VIRTUAL_CH0}, + {RCAR_CSI_CH_NONE, RCAR_VIN_CH_NONE}, + {RCAR_CSI40, RCAR_VIRTUAL_CH1}, + {RCAR_CSI40, RCAR_VIRTUAL_CH0}, + {RCAR_CSI20, RCAR_VIRTUAL_CH0}, + {RCAR_CSI_CH_NONE, RCAR_VIN_CH_NONE}, + {RCAR_CSI40, RCAR_VIRTUAL_CH1}, + } + }, + { 0x0001, + { + {RCAR_CSI20, RCAR_VIRTUAL_CH0}, + {RCAR_CSI_CH_NONE, RCAR_VIN_CH_NONE}, + {RCAR_CSI40, RCAR_VIRTUAL_CH0}, + {RCAR_CSI20, RCAR_VIRTUAL_CH1}, + {RCAR_CSI20, RCAR_VIRTUAL_CH0}, + {RCAR_CSI_CH_NONE, RCAR_VIN_CH_NONE}, + {RCAR_CSI40, RCAR_VIRTUAL_CH0}, + {RCAR_CSI20, RCAR_VIRTUAL_CH1}, + } + }, + { 0x0002, + { + {RCAR_CSI_CH_NONE, RCAR_VIN_CH_NONE}, + {RCAR_CSI40, RCAR_VIRTUAL_CH0}, + {RCAR_CSI20, RCAR_VIRTUAL_CH0}, + {RCAR_CSI_CH_NONE, RCAR_VIN_CH_NONE}, + {RCAR_CSI_CH_NONE, RCAR_VIN_CH_NONE}, + {RCAR_CSI40, RCAR_VIRTUAL_CH0}, + {RCAR_CSI20, RCAR_VIRTUAL_CH0}, + {RCAR_CSI_CH_NONE, RCAR_VIN_CH_NONE}, + } + }, + { 0x0003, + { + {RCAR_CSI40, RCAR_VIRTUAL_CH0}, + {RCAR_CSI40, RCAR_VIRTUAL_CH1}, + {RCAR_CSI40, RCAR_VIRTUAL_CH2}, + {RCAR_CSI40, RCAR_VIRTUAL_CH3}, + {RCAR_CSI40, RCAR_VIRTUAL_CH0}, + {RCAR_CSI40, RCAR_VIRTUAL_CH1}, + {RCAR_CSI40, RCAR_VIRTUAL_CH2}, + {RCAR_CSI40, RCAR_VIRTUAL_CH3}, + } + }, + { 0x0004, + { + {RCAR_CSI20, RCAR_VIRTUAL_CH0}, + {RCAR_CSI20, RCAR_VIRTUAL_CH1}, + {RCAR_CSI20, RCAR_VIRTUAL_CH2}, + {RCAR_CSI20, RCAR_VIRTUAL_CH3}, + {RCAR_CSI20, RCAR_VIRTUAL_CH0}, + {RCAR_CSI20, RCAR_VIRTUAL_CH1}, + {RCAR_CSI20, RCAR_VIRTUAL_CH2}, + {RCAR_CSI20, RCAR_VIRTUAL_CH3}, + } + }, +}; + +enum csi2_fmt { + RCAR_CSI_FMT_NONE = -1, + RCAR_CSI_RGB888, + RCAR_CSI_YCBCR422, +}; + +struct vin_coeff { + unsigned short xs_value; + u32 coeff_set[24]; +}; + +static const struct vin_coeff vin_coeff_set[] = { + { 0x0000, { + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000 }, + }, + { 0x1000, { + 0x000fa400, 0x000fa400, 0x09625902, + 0x000003f8, 0x00000403, 0x3de0d9f0, + 0x001fffed, 0x00000804, 0x3cc1f9c3, + 0x001003de, 0x00000c01, 0x3cb34d7f, + 0x002003d2, 0x00000c00, 0x3d24a92d, + 0x00200bca, 0x00000bff, 0x3df600d2, + 0x002013cc, 0x000007ff, 0x3ed70c7e, + 0x00100fde, 0x00000000, 0x3f87c036 }, + }, + { 0x1200, { + 0x002ffff1, 0x002ffff1, 0x02a0a9c8, + 0x002003e7, 0x001ffffa, 0x000185bc, + 0x002007dc, 0x000003ff, 0x3e52859c, + 0x00200bd4, 0x00000002, 0x3d53996b, + 0x00100fd0, 0x00000403, 0x3d04ad2d, + 0x00000bd5, 0x00000403, 0x3d35ace7, + 0x3ff003e4, 0x00000801, 0x3dc674a1, + 0x3fffe800, 0x00000800, 0x3e76f461 }, + }, + { 0x1400, { + 0x00100be3, 0x00100be3, 0x04d1359a, + 0x00000fdb, 0x002003ed, 0x0211fd93, + 0x00000fd6, 0x002003f4, 0x0002d97b, + 0x000007d6, 0x002ffffb, 0x3e93b956, + 0x3ff003da, 0x001003ff, 0x3db49926, + 0x3fffefe9, 0x00100001, 0x3d655cee, + 0x3fffd400, 0x00000003, 0x3d65f4b6, + 0x000fb421, 0x00000402, 0x3dc6547e }, + }, + { 0x1600, { + 0x00000bdd, 0x00000bdd, 0x06519578, + 0x3ff007da, 0x00000be3, 0x03c24973, + 0x3ff003d9, 0x00000be9, 0x01b30d5f, + 0x3ffff7df, 0x001003f1, 0x0003c542, + 0x000fdfec, 0x001003f7, 0x3ec4711d, + 0x000fc400, 0x002ffffd, 0x3df504f1, + 0x001fa81a, 0x002ffc00, 0x3d957cc2, + 0x002f8c3c, 0x00100000, 0x3db5c891 }, + }, + { 0x1800, { + 0x3ff003dc, 0x3ff003dc, 0x0791e558, + 0x000ff7dd, 0x3ff007de, 0x05328554, + 0x000fe7e3, 0x3ff00be2, 0x03232546, + 0x000fd7ee, 0x000007e9, 0x0143bd30, + 0x001fb800, 0x000007ee, 0x00044511, + 0x002fa015, 0x000007f4, 0x3ef4bcee, + 0x002f8832, 0x001003f9, 0x3e4514c7, + 0x001f7853, 0x001003fd, 0x3de54c9f }, + }, + { 0x1a00, { + 0x000fefe0, 0x000fefe0, 0x08721d3c, + 0x001fdbe7, 0x000ffbde, 0x0652a139, + 0x001fcbf0, 0x000003df, 0x0463292e, + 0x002fb3ff, 0x3ff007e3, 0x0293a91d, + 0x002f9c12, 0x3ff00be7, 0x01241905, + 0x001f8c29, 0x000007ed, 0x3fe470eb, + 0x000f7c46, 0x000007f2, 0x3f04b8ca, + 0x3fef7865, 0x000007f6, 0x3e74e4a8 }, + }, + { 0x1c00, { + 0x001fd3e9, 0x001fd3e9, 0x08f23d26, + 0x002fbff3, 0x001fe3e4, 0x0712ad23, + 0x002fa800, 0x000ff3e0, 0x05631d1b, + 0x001f9810, 0x000ffbe1, 0x03b3890d, + 0x000f8c23, 0x000003e3, 0x0233e8fa, + 0x3fef843b, 0x000003e7, 0x00f430e4, + 0x3fbf8456, 0x3ff00bea, 0x00046cc8, + 0x3f8f8c72, 0x3ff00bef, 0x3f3490ac }, + }, + { 0x1e00, { + 0x001fbbf4, 0x001fbbf4, 0x09425112, + 0x001fa800, 0x002fc7ed, 0x0792b110, + 0x000f980e, 0x001fdbe6, 0x0613110a, + 0x3fff8c20, 0x001fe7e3, 0x04a368fd, + 0x3fcf8c33, 0x000ff7e2, 0x0343b8ed, + 0x3f9f8c4a, 0x000fffe3, 0x0203f8da, + 0x3f5f9c61, 0x000003e6, 0x00e428c5, + 0x3f1fb07b, 0x000003eb, 0x3fe440af }, + }, + { 0x2000, { + 0x000fa400, 0x000fa400, 0x09625902, + 0x3fff980c, 0x001fb7f5, 0x0812b0ff, + 0x3fdf901c, 0x001fc7ed, 0x06b2fcfa, + 0x3faf902d, 0x001fd3e8, 0x055348f1, + 0x3f7f983f, 0x001fe3e5, 0x04038ce3, + 0x3f3fa454, 0x001fefe3, 0x02e3c8d1, + 0x3f0fb86a, 0x001ff7e4, 0x01c3e8c0, + 0x3ecfd880, 0x000fffe6, 0x00c404ac }, + }, + { 0x2200, { + 0x3fdf9c0b, 0x3fdf9c0b, 0x09725cf4, + 0x3fbf9818, 0x3fffa400, 0x0842a8f1, + 0x3f8f9827, 0x000fb3f7, 0x0702f0ec, + 0x3f5fa037, 0x000fc3ef, 0x05d330e4, + 0x3f2fac49, 0x001fcfea, 0x04a364d9, + 0x3effc05c, 0x001fdbe7, 0x038394ca, + 0x3ecfdc6f, 0x001fe7e6, 0x0273b0bb, + 0x3ea00083, 0x001fefe6, 0x0183c0a9 }, + }, + { 0x2400, { + 0x3f9fa014, 0x3f9fa014, 0x098260e6, + 0x3f7f9c23, 0x3fcf9c0a, 0x08629ce5, + 0x3f4fa431, 0x3fefa400, 0x0742d8e1, + 0x3f1fb440, 0x3fffb3f8, 0x062310d9, + 0x3eefc850, 0x000fbbf2, 0x050340d0, + 0x3ecfe062, 0x000fcbec, 0x041364c2, + 0x3ea00073, 0x001fd3ea, 0x03037cb5, + 0x3e902086, 0x001fdfe8, 0x022388a5 }, + }, + { 0x2600, { + 0x3f5fa81e, 0x3f5fa81e, 0x096258da, + 0x3f3fac2b, 0x3f8fa412, 0x088290d8, + 0x3f0fbc38, 0x3fafa408, 0x0772c8d5, + 0x3eefcc47, 0x3fcfa800, 0x0672f4ce, + 0x3ecfe456, 0x3fefaffa, 0x05531cc6, + 0x3eb00066, 0x3fffbbf3, 0x047334bb, + 0x3ea01c77, 0x000fc7ee, 0x039348ae, + 0x3ea04486, 0x000fd3eb, 0x02b350a1 }, + }, + { 0x2800, { + 0x3f2fb426, 0x3f2fb426, 0x094250ce, + 0x3f0fc032, 0x3f4fac1b, 0x086284cd, + 0x3eefd040, 0x3f7fa811, 0x0782acc9, + 0x3ecfe84c, 0x3f9fa807, 0x06a2d8c4, + 0x3eb0005b, 0x3fbfac00, 0x05b2f4bc, + 0x3eb0186a, 0x3fdfb3fa, 0x04c308b4, + 0x3eb04077, 0x3fefbbf4, 0x03f31ca8, + 0x3ec06884, 0x000fbff2, 0x03031c9e }, + }, + { 0x2a00, { + 0x3f0fc42d, 0x3f0fc42d, 0x090240c4, + 0x3eefd439, 0x3f2fb822, 0x08526cc2, + 0x3edfe845, 0x3f4fb018, 0x078294bf, + 0x3ec00051, 0x3f6fac0f, 0x06b2b4bb, + 0x3ec0185f, 0x3f8fac07, 0x05e2ccb4, + 0x3ec0386b, 0x3fafac00, 0x0502e8ac, + 0x3ed05c77, 0x3fcfb3fb, 0x0432f0a3, + 0x3ef08482, 0x3fdfbbf6, 0x0372f898 }, + }, + { 0x2c00, { + 0x3eefdc31, 0x3eefdc31, 0x08e238b8, + 0x3edfec3d, 0x3f0fc828, 0x082258b9, + 0x3ed00049, 0x3f1fc01e, 0x077278b6, + 0x3ed01455, 0x3f3fb815, 0x06c294b2, + 0x3ed03460, 0x3f5fb40d, 0x0602acac, + 0x3ef0506c, 0x3f7fb006, 0x0542c0a4, + 0x3f107476, 0x3f9fb400, 0x0472c89d, + 0x3f309c80, 0x3fbfb7fc, 0x03b2cc94 }, + }, + { 0x2e00, { + 0x3eefec37, 0x3eefec37, 0x088220b0, + 0x3ee00041, 0x3effdc2d, 0x07f244ae, + 0x3ee0144c, 0x3f0fd023, 0x07625cad, + 0x3ef02c57, 0x3f1fc81a, 0x06c274a9, + 0x3f004861, 0x3f3fbc13, 0x060288a6, + 0x3f20686b, 0x3f5fb80c, 0x05529c9e, + 0x3f408c74, 0x3f6fb805, 0x04b2ac96, + 0x3f80ac7e, 0x3f8fb800, 0x0402ac8e }, + }, + { 0x3000, { + 0x3ef0003a, 0x3ef0003a, 0x084210a6, + 0x3ef01045, 0x3effec32, 0x07b228a7, + 0x3f00284e, 0x3f0fdc29, 0x073244a4, + 0x3f104058, 0x3f0fd420, 0x06a258a2, + 0x3f305c62, 0x3f2fc818, 0x0612689d, + 0x3f508069, 0x3f3fc011, 0x05728496, + 0x3f80a072, 0x3f4fc00a, 0x04d28c90, + 0x3fc0c07b, 0x3f6fbc04, 0x04429088 }, + }, + { 0x3200, { + 0x3f00103e, 0x3f00103e, 0x07f1fc9e, + 0x3f102447, 0x3f000035, 0x0782149d, + 0x3f203c4f, 0x3f0ff02c, 0x07122c9c, + 0x3f405458, 0x3f0fe424, 0x06924099, + 0x3f607061, 0x3f1fd41d, 0x06024c97, + 0x3f909068, 0x3f2fcc16, 0x05726490, + 0x3fc0b070, 0x3f3fc80f, 0x04f26c8a, + 0x0000d077, 0x3f4fc409, 0x04627484 }, + }, + { 0x3400, { + 0x3f202040, 0x3f202040, 0x07a1e898, + 0x3f303449, 0x3f100c38, 0x0741fc98, + 0x3f504c50, 0x3f10002f, 0x06e21495, + 0x3f706459, 0x3f1ff028, 0x06722492, + 0x3fa08060, 0x3f1fe421, 0x05f2348f, + 0x3fd09c67, 0x3f1fdc19, 0x05824c89, + 0x0000bc6e, 0x3f2fd014, 0x04f25086, + 0x0040dc74, 0x3f3fcc0d, 0x04825c7f }, + }, + { 0x3600, { + 0x3f403042, 0x3f403042, 0x0761d890, + 0x3f504848, 0x3f301c3b, 0x0701f090, + 0x3f805c50, 0x3f200c33, 0x06a2008f, + 0x3fa07458, 0x3f10002b, 0x06520c8d, + 0x3fd0905e, 0x3f1ff424, 0x05e22089, + 0x0000ac65, 0x3f1fe81d, 0x05823483, + 0x0030cc6a, 0x3f2fdc18, 0x04f23c81, + 0x0080e871, 0x3f2fd412, 0x0482407c }, + }, + { 0x3800, { + 0x3f604043, 0x3f604043, 0x0721c88a, + 0x3f80544a, 0x3f502c3c, 0x06d1d88a, + 0x3fb06851, 0x3f301c35, 0x0681e889, + 0x3fd08456, 0x3f30082f, 0x0611fc88, + 0x00009c5d, 0x3f200027, 0x05d20884, + 0x0030b863, 0x3f2ff421, 0x05621880, + 0x0070d468, 0x3f2fe81b, 0x0502247c, + 0x00c0ec6f, 0x3f2fe015, 0x04a22877 }, + }, + { 0x3a00, { + 0x3f904c44, 0x3f904c44, 0x06e1b884, + 0x3fb0604a, 0x3f70383e, 0x0691c885, + 0x3fe07451, 0x3f502c36, 0x0661d483, + 0x00009055, 0x3f401831, 0x0601ec81, + 0x0030a85b, 0x3f300c2a, 0x05b1f480, + 0x0070c061, 0x3f300024, 0x0562047a, + 0x00b0d867, 0x3f3ff41e, 0x05020c77, + 0x00f0f46b, 0x3f2fec19, 0x04a21474 }, + }, + { 0x3c00, { + 0x3fb05c43, 0x3fb05c43, 0x06c1b07e, + 0x3fe06c4b, 0x3f902c3f, 0x0681c081, + 0x0000844f, 0x3f703838, 0x0631cc7d, + 0x00309855, 0x3f602433, 0x05d1d47e, + 0x0060b459, 0x3f50142e, 0x0581e47b, + 0x00a0c85f, 0x3f400828, 0x0531f078, + 0x00e0e064, 0x3f300021, 0x0501fc73, + 0x00b0fc6a, 0x3f3ff41d, 0x04a20873 }, + }, + { 0x3e00, { + 0x3fe06444, 0x3fe06444, 0x0681a07a, + 0x00007849, 0x3fc0503f, 0x0641b07a, + 0x0020904d, 0x3fa0403a, 0x05f1c07a, + 0x0060a453, 0x3f803034, 0x05c1c878, + 0x0090b858, 0x3f70202f, 0x0571d477, + 0x00d0d05d, 0x3f501829, 0x0531e073, + 0x0110e462, 0x3f500825, 0x04e1e471, + 0x01510065, 0x3f40001f, 0x04a1f06d }, + }, + { 0x4000, { + 0x00007044, 0x00007044, 0x06519476, + 0x00208448, 0x3fe05c3f, 0x0621a476, + 0x0050984d, 0x3fc04c3a, 0x05e1b075, + 0x0080ac52, 0x3fa03c35, 0x05a1b875, + 0x00c0c056, 0x3f803030, 0x0561c473, + 0x0100d45b, 0x3f70202b, 0x0521d46f, + 0x0140e860, 0x3f601427, 0x04d1d46e, + 0x01810064, 0x3f500822, 0x0491dc6b }, + }, + { 0x5000, { + 0x0110a442, 0x0110a442, 0x0551545e, + 0x0140b045, 0x00e0983f, 0x0531585f, + 0x0160c047, 0x00c08c3c, 0x0511645e, + 0x0190cc4a, 0x00908039, 0x04f1685f, + 0x01c0dc4c, 0x00707436, 0x04d1705e, + 0x0200e850, 0x00506833, 0x04b1785b, + 0x0230f453, 0x00305c30, 0x0491805a, + 0x02710056, 0x0010542d, 0x04718059 }, + }, + { 0x6000, { + 0x01c0bc40, 0x01c0bc40, 0x04c13052, + 0x01e0c841, 0x01a0b43d, 0x04c13851, + 0x0210cc44, 0x0180a83c, 0x04a13453, + 0x0230d845, 0x0160a03a, 0x04913c52, + 0x0260e047, 0x01409838, 0x04714052, + 0x0280ec49, 0x01208c37, 0x04514c50, + 0x02b0f44b, 0x01008435, 0x04414c50, + 0x02d1004c, 0x00e07c33, 0x0431544f }, + }, + { 0x7000, { + 0x0230c83e, 0x0230c83e, 0x04711c4c, + 0x0250d03f, 0x0210c43c, 0x0471204b, + 0x0270d840, 0x0200b83c, 0x0451244b, + 0x0290dc42, 0x01e0b43a, 0x0441244c, + 0x02b0e443, 0x01c0b038, 0x0441284b, + 0x02d0ec44, 0x01b0a438, 0x0421304a, + 0x02f0f445, 0x0190a036, 0x04213449, + 0x0310f847, 0x01709c34, 0x04213848 }, + }, + { 0x8000, { + 0x0280d03d, 0x0280d03d, 0x04310c48, + 0x02a0d43e, 0x0270c83c, 0x04311047, + 0x02b0dc3e, 0x0250c83a, 0x04311447, + 0x02d0e040, 0x0240c03a, 0x04211446, + 0x02e0e840, 0x0220bc39, 0x04111847, + 0x0300e842, 0x0210b438, 0x04012445, + 0x0310f043, 0x0200b037, 0x04012045, + 0x0330f444, 0x01e0ac36, 0x03f12445 }, + }, + { 0xefff, { + 0x0340dc3a, 0x0340dc3a, 0x03b0ec40, + 0x0340e03a, 0x0330e039, 0x03c0f03e, + 0x0350e03b, 0x0330dc39, 0x03c0ec3e, + 0x0350e43a, 0x0320dc38, 0x03c0f43e, + 0x0360e43b, 0x0320d839, 0x03b0f03e, + 0x0360e83b, 0x0310d838, 0x03c0fc3b, + 0x0370e83b, 0x0310d439, 0x03a0f83d, + 0x0370e83c, 0x0300d438, 0x03b0fc3c }, + } +}; + +enum rcar_vin_state { + STOPPED = 0, + RUNNING, + STOPPING, +}; + +struct rcar_vin_async_client { + struct v4l2_async_subdev *sensor; + struct v4l2_async_notifier notifier; + struct platform_device *pdev; + struct list_head list; /* needed for clean up */ +}; + +struct soc_of_info { + struct soc_camera_async_subdev sasd; + struct rcar_vin_async_client sasc; + struct v4l2_async_subdev *subdev; +}; + +struct rcar_vin_priv { + void __iomem *base; + spinlock_t lock; + int sequence; + /* State of the VIN module in capturing mode */ + enum rcar_vin_state state; + struct soc_camera_host ici; + struct list_head capture; +#define MAX_BUFFER_NUM 3 + struct vb2_v4l2_buffer *queue_buf[MAX_BUFFER_NUM]; + enum v4l2_field field; + unsigned int pdata_flags; + unsigned int vb_count; + unsigned int nr_hw_slots; + bool request_to_stop; + struct completion capture_stop; + enum chip_id chip; + unsigned int max_width; + unsigned int max_height; + unsigned int ratio_h; + unsigned int ratio_v; + bool error_flag; + enum csi2_ch csi_ch; + enum csi2_fmt csi_fmt; + enum virtual_ch vc; + bool csi_sync; + + struct rcar_vin_async_client *async_client; + /* Asynchronous CSI2 linking */ + struct v4l2_subdev *csi2_sd; + /* Synchronous probing compatibility */ + struct platform_device *csi2_pdev; + + unsigned int index; +}; + +#define is_continuous_transfer(priv) (priv->vb_count > MAX_BUFFER_NUM) + +struct rcar_vin_buffer { + struct vb2_v4l2_buffer vb; + struct list_head list; +}; + +#define to_buf_list(vb2_buffer) (&container_of(vb2_buffer, \ + struct rcar_vin_buffer, \ + vb)->list) + +struct rcar_vin_cam { + /* VIN offsets within the camera output, before the VIN scaler */ + unsigned int vin_left; + unsigned int vin_top; + /* Client output, as seen by the VIN */ + unsigned int width; + unsigned int height; + /* User window from S_FMT */ + unsigned int out_width; + unsigned int out_height; + /* + * User window from S_SELECTION / G_SELECTION, produced by client cropping and + * scaling, VIN scaling and VIN cropping, mapped back onto the client + * input window + */ + struct v4l2_rect subrect; + /* Camera cropping rectangle */ + struct v4l2_rect rect; + const struct soc_mbus_pixelfmt *extra_fmt; +}; + +#define VIN_UT_IRQ 0x01 + +static unsigned int vin_debug; +module_param_named(debug, vin_debug, int, 0600); +static int overflow_video[RCAR_VIDEO_MAX]; +module_param_array(overflow_video, int, NULL, 0600); + +#ifdef CONFIG_VIDEO_RCAR_VIN_LEGACY_DEBUG +#define VIN_IRQ_DEBUG(fmt, args...) \ + do { \ + if (unlikely(vin_debug & VIN_UT_IRQ)) \ + vin_ut_debug_printk(__func__, fmt, ##args); \ + } while (0) +#else +#define VIN_IRQ_DEBUG(fmt, args...) +#endif + +void vin_ut_debug_printk(const char *function_name, const char *format, ...) +{ + struct va_format vaf; + va_list args; + + va_start(args, format); + vaf.fmt = format; + vaf.va = &args; + + pr_debug("[" DRV_NAME ":%s] %pV", function_name, &vaf); + + va_end(args); +} + +static void rcar_vin_cpg_enable_for_ifmd(unsigned int ch, bool enable) +{ + void __iomem *smstpcr8; + + smstpcr8 = ioremap(0xE6150990, 0x04); + + if (enable) { + if (ch < RCAR_VIDEO_4) + iowrite32((ioread32(smstpcr8) & 0xFFFFF7FF), smstpcr8); + else + iowrite32((ioread32(smstpcr8) & 0xFFFFFF7F), smstpcr8); + } else { + if (ch < RCAR_VIDEO_4) + iowrite32((ioread32(smstpcr8) | 0x00000800), smstpcr8); + else + iowrite32((ioread32(smstpcr8) | 0x00000080), smstpcr8); + } + + iounmap(smstpcr8); +} + +static inline int is_scaling(struct rcar_vin_cam *cam) +{ + struct v4l2_rect *cam_subrect = &cam->subrect; + + if ((cam_subrect->width != cam->out_width) || + (cam_subrect->height != cam->out_height)) + return 1; + + return 0; +} + +/* + * .queue_setup() is called to check whether the driver can accept the requested + * number of buffers and to fill in plane sizes for the current frame format if + * required + */ +static int rcar_vin_videobuf_setup(struct vb2_queue *vq, + unsigned int *count, + unsigned int *num_planes, + unsigned int sizes[], struct device *alloc_devs[]) +{ + struct soc_camera_device *icd = soc_camera_from_vb2q(vq); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct rcar_vin_priv *priv = ici->priv; + struct rcar_vin_cam *cam = icd->host_priv; + + if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3) { + if ((priv->ratio_h > 0x10000) || (priv->ratio_v > 0x10000)) { + dev_err(icd->parent, "Scaling rate parameter error\n"); + return -EINVAL; + } + if (is_scaling(cam) && (cam->out_width % 32)) { + dev_err(icd->parent, "Scaling parameter error\n"); + return -EINVAL; + } + if (!is_scaling(cam) && (cam->out_width % 16)) { + dev_err(icd->parent, "Image stride parameter error\n"); + return -EINVAL; + } + } + + if (!vq->num_buffers) + priv->sequence = 0; + + if (!*count) + *count = 2; + priv->vb_count = *count; + + /* Number of hardware slots */ + if (is_continuous_transfer(priv)) + priv->nr_hw_slots = MAX_BUFFER_NUM; + else + priv->nr_hw_slots = 1; + + if (*num_planes) + return sizes[0] < icd->sizeimage ? -EINVAL : 0; + + sizes[0] = icd->sizeimage; + *num_planes = 1; + + dev_dbg(icd->parent, "count=%d, size=%u\n", *count, sizes[0]); + + return 0; +} + +static int rcar_vin_setup(struct rcar_vin_priv *priv) +{ + struct soc_camera_device *icd = priv->ici.icd; + struct rcar_vin_cam *cam = icd->host_priv; + u32 vnmc, dmr, interrupts; + bool progressive = false, output_is_yuv = false, input_is_yuv = false; + + switch (priv->field) { + case V4L2_FIELD_TOP: + vnmc = VNMC_IM_ODD; + break; + case V4L2_FIELD_BOTTOM: + vnmc = VNMC_IM_EVEN; + break; + case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_INTERLACED_TB: + vnmc = VNMC_IM_FULL; + break; + case V4L2_FIELD_INTERLACED_BT: + vnmc = VNMC_IM_FULL | VNMC_FOC; + break; + case V4L2_FIELD_NONE: + if (is_continuous_transfer(priv)) { + vnmc = VNMC_IM_ODD_EVEN; + progressive = true; + } else { + vnmc = VNMC_IM_ODD; + } + break; + default: + vnmc = VNMC_IM_ODD; + break; + } + + /* input interface */ + switch (icd->current_fmt->code) { + case MEDIA_BUS_FMT_YUYV8_1X16: + /* BT.601/BT.1358 16bit YCbCr422 */ + vnmc |= VNMC_INF_YUV16; + input_is_yuv = true; + break; + case MEDIA_BUS_FMT_YUYV8_2X8: + /* BT.656 8bit YCbCr422 or BT.601 8bit YCbCr422 */ + vnmc |= priv->pdata_flags & RCAR_VIN_BT656 ? + VNMC_INF_YUV8_BT656 : VNMC_INF_YUV8_BT601; + input_is_yuv = true; + break; + case MEDIA_BUS_FMT_RGB888_1X24: + vnmc |= VNMC_INF_RGB888; + break; + case MEDIA_BUS_FMT_YUYV10_2X10: + /* BT.656 10bit YCbCr422 or BT.601 10bit YCbCr422 */ + vnmc |= priv->pdata_flags & RCAR_VIN_BT656 ? + VNMC_INF_YUV10_BT656 : VNMC_INF_YUV10_BT601; + input_is_yuv = true; + break; + default: + break; + } + + /* output format */ + switch (icd->current_fmt->host_fmt->fourcc) { + case V4L2_PIX_FMT_NV12: + if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3) { + iowrite32(ALIGN((cam->out_width * cam->out_height), + 0x80), priv->base + VNUVAOF_REG); + dmr = VNDMR_DTMD_YCSEP_YCBCR420; + output_is_yuv = true; + } else { + dev_warn(icd->parent, "Not support format\n"); + return -EINVAL; + } + break; + case V4L2_PIX_FMT_NV16: + iowrite32(ALIGN((cam->out_width * cam->out_height), 0x80), + priv->base + VNUVAOF_REG); + dmr = VNDMR_DTMD_YCSEP; + output_is_yuv = true; + break; + case V4L2_PIX_FMT_YUYV: + dmr = VNDMR_BPSM; + output_is_yuv = true; + break; + case V4L2_PIX_FMT_UYVY: + dmr = 0; + output_is_yuv = true; + break; + case V4L2_PIX_FMT_ARGB555: + dmr = VNDMR_DTMD_ARGB; + break; + case V4L2_PIX_FMT_RGB565: + dmr = 0; + break; + case V4L2_PIX_FMT_XBGR32: + if (priv->chip != RCAR_H3 && priv->chip != RCAR_M3 && + priv->chip != RCAR_GEN2 && priv->chip != RCAR_H1 && + priv->chip != RCAR_E1) + goto e_format; + + dmr = VNDMR_EXRGB; + break; + case V4L2_PIX_FMT_ABGR32: + if (priv->chip != RCAR_H3 && priv->chip != RCAR_M3) + goto e_format; + + dmr = VNDMR_EXRGB | VNDMR_DTMD_ARGB; + break; + default: + goto e_format; + } + + /* Always update on field change */ + vnmc |= VNMC_VUP; + + /* If input and output use the same colorspace, use bypass mode */ + if (input_is_yuv == output_is_yuv) + vnmc |= VNMC_BPS; + + if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3) { + if (priv->pdata_flags & RCAR_VIN_CSI2) + vnmc &= ~VNMC_DPINE; + else + vnmc |= VNMC_DPINE; + + if ((icd->current_fmt->host_fmt->fourcc != V4L2_PIX_FMT_NV12) + && is_scaling(cam)) + vnmc |= VNMC_SCLE; + } + + /* progressive or interlaced mode */ + interrupts = progressive ? VNIE_FIE : VNIE_EFE; + + /* Enable Overflow */ + if (vin_debug) + interrupts |= VNIE_FOE; + + /* ack interrupts */ + iowrite32(interrupts, priv->base + VNINTS_REG); + /* enable interrupts */ + iowrite32(interrupts, priv->base + VNIE_REG); + /* start capturing */ + iowrite32(dmr, priv->base + VNDMR_REG); + iowrite32(vnmc | VNMC_ME, priv->base + VNMC_REG); + + return 0; + +e_format: + dev_warn(icd->parent, "Invalid fourcc format (0x%x)\n", + icd->current_fmt->host_fmt->fourcc); + return -EINVAL; +} + +static void rcar_vin_capture(struct rcar_vin_priv *priv) +{ + if (is_continuous_transfer(priv)) + /* Continuous Frame Capture Mode */ + iowrite32(VNFC_C_FRAME, priv->base + VNFC_REG); + else + /* Single Frame Capture Mode */ + iowrite32(VNFC_S_FRAME, priv->base + VNFC_REG); +} + +static void rcar_vin_request_capture_stop(struct rcar_vin_priv *priv) +{ + priv->state = STOPPING; + + /* set continuous & single transfer off */ + iowrite32(0, priv->base + VNFC_REG); + /* disable capture (release DMA buffer), reset */ + iowrite32(ioread32(priv->base + VNMC_REG) & ~VNMC_ME, + priv->base + VNMC_REG); + + /* update the status if stopped already */ + if (!(ioread32(priv->base + VNMS_REG) & VNMS_CA)) + priv->state = STOPPED; +} + +static int rcar_vin_get_free_hw_slot(struct rcar_vin_priv *priv) +{ + int slot; + + for (slot = 0; slot < priv->nr_hw_slots; slot++) + if (priv->queue_buf[slot] == NULL) + return slot; + + return -1; +} + +static int rcar_vin_hw_ready(struct rcar_vin_priv *priv) +{ + /* Ensure all HW slots are filled */ + return rcar_vin_get_free_hw_slot(priv) < 0 ? 1 : 0; +} + +/* Moves a buffer from the queue to the HW slots */ +static int rcar_vin_fill_hw_slot(struct rcar_vin_priv *priv) +{ + struct vb2_v4l2_buffer *vbuf; + dma_addr_t phys_addr_top; + int slot; + + if (list_empty(&priv->capture)) + return 0; + + /* Find a free HW slot */ + slot = rcar_vin_get_free_hw_slot(priv); + if (slot < 0) + return 0; + + vbuf = &list_entry(priv->capture.next, + struct rcar_vin_buffer, list)->vb; + list_del_init(to_buf_list(vbuf)); + priv->queue_buf[slot] = vbuf; + phys_addr_top = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0); + iowrite32(phys_addr_top, priv->base + VNMB_REG(slot)); + + return 1; +} + +static void rcar_vin_videobuf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct rcar_vin_priv *priv = ici->priv; + unsigned long size; + + size = icd->sizeimage; + + if (vb2_plane_size(vb, 0) < size) { + dev_err(icd->parent, "Buffer #%d too small (%lu < %lu)\n", + vb->index, vb2_plane_size(vb, 0), size); + goto error; + } + + vb2_set_plane_payload(vb, 0, size); + + dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, + vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); + + spin_lock_irq(&priv->lock); + + list_add_tail(to_buf_list(vbuf), &priv->capture); + rcar_vin_fill_hw_slot(priv); + + /* If we weren't running, and have enough buffers, start capturing! */ + if (priv->state != RUNNING && rcar_vin_hw_ready(priv)) { + if (rcar_vin_setup(priv)) { + /* Submit error */ + list_del_init(to_buf_list(vbuf)); + spin_unlock_irq(&priv->lock); + goto error; + } + priv->request_to_stop = false; + init_completion(&priv->capture_stop); + priv->state = RUNNING; + rcar_vin_capture(priv); + } + + spin_unlock_irq(&priv->lock); + + return; + +error: + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); +} + +/* + * Wait for capture to stop and all in-flight buffers to be finished with by + * the video hardware. This must be called under &priv->lock + * + */ +static void rcar_vin_wait_stop_streaming(struct rcar_vin_priv *priv) +{ + while (priv->state != STOPPED) { + /* issue stop if running */ + if (priv->state == RUNNING) + rcar_vin_request_capture_stop(priv); + + /* wait until capturing has been stopped */ + if (priv->state == STOPPING) { + priv->request_to_stop = true; + spin_unlock_irq(&priv->lock); + if (!wait_for_completion_timeout( + &priv->capture_stop, + msecs_to_jiffies(TIMEOUT_MS))) + priv->state = STOPPED; + spin_lock_irq(&priv->lock); + } + } +} + +static void rcar_vin_stop_streaming(struct vb2_queue *vq) +{ + struct soc_camera_device *icd = soc_camera_from_vb2q(vq); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct rcar_vin_priv *priv = ici->priv; + struct list_head *buf_head, *tmp; + int i; + + spin_lock_irq(&priv->lock); + rcar_vin_wait_stop_streaming(priv); + + for (i = 0; i < MAX_BUFFER_NUM; i++) { + if (priv->queue_buf[i]) { + vb2_buffer_done(&priv->queue_buf[i]->vb2_buf, + VB2_BUF_STATE_ERROR); + priv->queue_buf[i] = NULL; + } + } + + list_for_each_safe(buf_head, tmp, &priv->capture) { + vb2_buffer_done(&list_entry(buf_head, + struct rcar_vin_buffer, list)->vb.vb2_buf, + VB2_BUF_STATE_ERROR); + list_del_init(buf_head); + } + spin_unlock_irq(&priv->lock); +} + +static const struct vb2_ops rcar_vin_vb2_ops = { + .queue_setup = rcar_vin_videobuf_setup, + .buf_queue = rcar_vin_videobuf_queue, + .stop_streaming = rcar_vin_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +static irqreturn_t rcar_vin_irq(int irq, void *data) +{ + struct rcar_vin_priv *priv = data; + u32 int_status; + bool can_run = false, hw_stopped; + int slot; + unsigned int handled = 0; + int vin_ovr_cnt = 0; + + spin_lock(&priv->lock); + + int_status = ioread32(priv->base + VNINTS_REG); + if (!int_status) + goto done; + + /* ack interrupts */ + iowrite32(int_status, priv->base + VNINTS_REG); + handled = 1; + + /* overflow occurs */ + if (vin_debug && (int_status & VNINTS_FOS)) { + vin_ovr_cnt = ++overflow_video[priv->index]; + VIN_IRQ_DEBUG("overflow occurrs num[%d] at VIN (%s)\n", + vin_ovr_cnt, dev_name(priv->ici.v4l2_dev.dev)); + } + + /* nothing to do if capture status is 'STOPPED' */ + if (priv->state == STOPPED) + goto done; + + hw_stopped = !(ioread32(priv->base + VNMS_REG) & VNMS_CA); + + if (!priv->request_to_stop) { + if (is_continuous_transfer(priv)) + slot = (ioread32(priv->base + VNMS_REG) & + VNMS_FBS_MASK) >> VNMS_FBS_SHIFT; + else + slot = 0; + + if (!is_continuous_transfer(priv) || ((priv->state == RUNNING) + && !list_empty(&priv->capture))) { + priv->queue_buf[slot]->field = priv->field; + priv->queue_buf[slot]->sequence = priv->sequence++; + priv->queue_buf[slot]->vb2_buf.timestamp = + ktime_get_ns(); + vb2_buffer_done(&priv->queue_buf[slot]->vb2_buf, + VB2_BUF_STATE_DONE); + priv->queue_buf[slot] = NULL; + + can_run = rcar_vin_fill_hw_slot(priv); + } + + if (is_continuous_transfer(priv)) { + if (hw_stopped) + priv->state = STOPPED; + else if (list_empty(&priv->capture) && + priv->state == RUNNING) + /* + * The continuous capturing requires an + * explicit stop operation when there is no + * buffer to be set into the VnMBm registers. + */ + rcar_vin_request_capture_stop(priv); + } else { + if (can_run) + rcar_vin_capture(priv); + else + priv->state = STOPPED; + } + } else if (hw_stopped) { + priv->state = STOPPED; + priv->request_to_stop = false; + complete(&priv->capture_stop); + } + +done: + spin_unlock(&priv->lock); + + return IRQ_RETVAL(handled); +} + +static struct v4l2_subdev *find_csi2(struct rcar_vin_priv *pcdev) +{ + struct v4l2_subdev *sd; + char name[] = "rcar_csi2"; + + v4l2_device_for_each_subdev(sd, &pcdev->ici.v4l2_dev) { + if (!strncmp(name, sd->name, sizeof(name) - 1)) { + pcdev->csi2_sd = sd; + return sd; + } + } + + return NULL; +} + +static int rcar_vin_add_device(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct rcar_vin_priv *priv = ici->priv; + int i; + + for (i = 0; i < MAX_BUFFER_NUM; i++) + priv->queue_buf[i] = NULL; + + pm_runtime_get_sync(ici->v4l2_dev.dev); + + if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3) { + struct v4l2_subdev *csi2_sd = find_csi2(priv); + int ret; + + if (csi2_sd) { + csi2_sd->grp_id = soc_camera_grp_id(icd); + v4l2_set_subdev_hostdata(csi2_sd, icd); + + ret = v4l2_subdev_call(csi2_sd, core, s_power, 1); + priv->csi_sync = true; + + if (ret < 0 && ret != -EINVAL) + priv->csi_sync = false; + + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) + return ret; + } + /* + * -ENODEV is special: + * either csi2_sd == NULL or the CSI-2 driver + * has not found this soc-camera device among its clients + */ + if (csi2_sd && ret == -ENODEV) + csi2_sd->grp_id = 0; + + dev_dbg(icd->parent, + "R-Car VIN/CSI-2 driver attached to camera %d\n", + icd->devnum); + + } else + dev_dbg(icd->parent, "R-Car VIN driver attached to camera %d\n", + icd->devnum); + + priv->error_flag = false; + + return 0; +} + +static void rcar_vin_remove_device(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct rcar_vin_priv *priv = ici->priv; + struct vb2_v4l2_buffer *vbuf; + struct v4l2_subdev *csi2_sd = find_csi2(priv); + int i; + + /* disable capture, disable interrupts */ + iowrite32(ioread32(priv->base + VNMC_REG) & ~VNMC_ME, + priv->base + VNMC_REG); + iowrite32(0, priv->base + VNIE_REG); + + priv->state = STOPPED; + priv->request_to_stop = false; + priv->error_flag = false; + + /* make sure active buffer is cancelled */ + spin_lock_irq(&priv->lock); + for (i = 0; i < MAX_BUFFER_NUM; i++) { + vbuf = priv->queue_buf[i]; + if (vbuf) { + list_del_init(to_buf_list(vbuf)); + vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_ERROR); + } + } + spin_unlock_irq(&priv->lock); + + pm_runtime_put(ici->v4l2_dev.dev); + + if ((csi2_sd) && (priv->csi_sync)) + v4l2_subdev_call(csi2_sd, core, s_power, 0); + + dev_dbg(icd->parent, "R-Car VIN driver detached from camera %d\n", + icd->devnum); +} + +struct rcar_vin_uds_regs { + unsigned long ctrl; + unsigned long scale; + unsigned long pass_bwidth; + unsigned long clip_size; +}; + +static unsigned long rcar_vin_get_bwidth(unsigned long ratio) +{ + unsigned long bwidth; + unsigned long mant, frac; + + mant = (ratio & 0xF000) >> 12; + frac = ratio & 0x0FFF; + if (mant) + bwidth = 64 * 4096 * mant / (4096 * mant + frac); + else + bwidth = 64; + + return bwidth; +} + +static unsigned long rcar_vin_compute_ratio(unsigned int input, + unsigned int output) +{ + return ((input * 4096 / output) == 0x10000) ? + 0xFFFF : (input * 4096 / output); +} + +int rcar_vin_uds_set(struct rcar_vin_priv *priv, struct rcar_vin_cam *cam) +{ + struct rcar_vin_uds_regs regs; + unsigned long ratio_h, ratio_v; + unsigned long bwidth_h, bwidth_v; + unsigned long ctrl; + unsigned long clip_size; + struct v4l2_rect *cam_subrect = &cam->subrect; + u32 vnmc; + + ratio_h = rcar_vin_compute_ratio(cam_subrect->width, cam->out_width); + ratio_v = rcar_vin_compute_ratio(cam_subrect->height, cam->out_height); + + priv->ratio_h = ratio_h; + priv->ratio_v = ratio_v; + + bwidth_h = rcar_vin_get_bwidth(ratio_h); + bwidth_v = rcar_vin_get_bwidth(ratio_v); + + ctrl = VNUDS_CTRL_AMD; + + if (priv->field == V4L2_FIELD_NONE) + clip_size = (cam->out_width << 16) | (cam->out_height); + else + clip_size = (cam->out_width << 16) | (cam->out_height / 2); + + regs.ctrl = ctrl; + regs.scale = (ratio_h << 16) | ratio_v; + regs.pass_bwidth = (bwidth_h << 16) | bwidth_v; + regs.clip_size = clip_size; + + vnmc = ioread32(priv->base + VNMC_REG); + iowrite32(vnmc | VNMC_SCLE, priv->base + VNMC_REG); + iowrite32(regs.ctrl, priv->base + VNUDS_CTRL_REG); + iowrite32(regs.scale, priv->base + VNUDS_SCALE_REG); + iowrite32(regs.pass_bwidth, priv->base + VNUDS_PASS_BWIDTH_REG); + iowrite32(regs.clip_size, priv->base + VNUDS_CLIP_SIZE_REG); + + return 0; +} + +static void set_coeff(struct rcar_vin_priv *priv, unsigned short xs) +{ + int i; + const struct vin_coeff *p_prev_set = NULL; + const struct vin_coeff *p_set = NULL; + + /* Look for suitable coefficient values */ + for (i = 0; i < ARRAY_SIZE(vin_coeff_set); i++) { + p_prev_set = p_set; + p_set = &vin_coeff_set[i]; + + if (xs < p_set->xs_value) + break; + } + + /* Use previous value if its XS value is closer */ + if (p_prev_set && p_set && + xs - p_prev_set->xs_value < p_set->xs_value - xs) + p_set = p_prev_set; + + /* Set coefficient registers */ + iowrite32(p_set->coeff_set[0], priv->base + VNC1A_REG); + iowrite32(p_set->coeff_set[1], priv->base + VNC1B_REG); + iowrite32(p_set->coeff_set[2], priv->base + VNC1C_REG); + + iowrite32(p_set->coeff_set[3], priv->base + VNC2A_REG); + iowrite32(p_set->coeff_set[4], priv->base + VNC2B_REG); + iowrite32(p_set->coeff_set[5], priv->base + VNC2C_REG); + + iowrite32(p_set->coeff_set[6], priv->base + VNC3A_REG); + iowrite32(p_set->coeff_set[7], priv->base + VNC3B_REG); + iowrite32(p_set->coeff_set[8], priv->base + VNC3C_REG); + + iowrite32(p_set->coeff_set[9], priv->base + VNC4A_REG); + iowrite32(p_set->coeff_set[10], priv->base + VNC4B_REG); + iowrite32(p_set->coeff_set[11], priv->base + VNC4C_REG); + + iowrite32(p_set->coeff_set[12], priv->base + VNC5A_REG); + iowrite32(p_set->coeff_set[13], priv->base + VNC5B_REG); + iowrite32(p_set->coeff_set[14], priv->base + VNC5C_REG); + + iowrite32(p_set->coeff_set[15], priv->base + VNC6A_REG); + iowrite32(p_set->coeff_set[16], priv->base + VNC6B_REG); + iowrite32(p_set->coeff_set[17], priv->base + VNC6C_REG); + + iowrite32(p_set->coeff_set[18], priv->base + VNC7A_REG); + iowrite32(p_set->coeff_set[19], priv->base + VNC7B_REG); + iowrite32(p_set->coeff_set[20], priv->base + VNC7C_REG); + + iowrite32(p_set->coeff_set[21], priv->base + VNC8A_REG); + iowrite32(p_set->coeff_set[22], priv->base + VNC8B_REG); + iowrite32(p_set->coeff_set[23], priv->base + VNC8C_REG); +} + +/* rect is guaranteed to not exceed the scaled camera rectangle */ +static int rcar_vin_set_rect(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct rcar_vin_cam *cam = icd->host_priv; + struct rcar_vin_priv *priv = ici->priv; + unsigned int left_offset, top_offset; + unsigned char dsize = 0; + struct v4l2_rect *cam_subrect = &cam->subrect; + u32 value; + int ret = 0; + + dev_dbg(icd->parent, "Crop %ux%u@%u:%u\n", + icd->user_width, icd->user_height, cam->vin_left, cam->vin_top); + + left_offset = cam->vin_left; + top_offset = cam->vin_top; + + if (icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_XBGR32 && + priv->chip == RCAR_E1) + dsize = 1; + + dev_dbg(icd->parent, "Cam %ux%u@%u:%u\n", + cam->width, cam->height, cam->vin_left, cam->vin_top); + dev_dbg(icd->parent, "Cam subrect %ux%u@%u:%u\n", + cam_subrect->width, cam_subrect->height, + cam_subrect->left, cam_subrect->top); + + /* Set Start/End Pixel/Line Pre-Clip */ + iowrite32(left_offset << dsize, priv->base + VNSPPRC_REG); + iowrite32((left_offset + cam_subrect->width - 1) << dsize, + priv->base + VNEPPRC_REG); + switch (priv->field) { + case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_INTERLACED_BT: + iowrite32(top_offset / 2, priv->base + VNSLPRC_REG); + iowrite32((top_offset + cam_subrect->height) / 2 - 1, + priv->base + VNELPRC_REG); + break; + default: + iowrite32(top_offset, priv->base + VNSLPRC_REG); + iowrite32(top_offset + cam_subrect->height - 1, + priv->base + VNELPRC_REG); + break; + } + + if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3) { + if ((icd->current_fmt->host_fmt->fourcc != V4L2_PIX_FMT_NV12) + && is_scaling(cam)) { + ret = rcar_vin_uds_set(priv, cam); + if (ret < 0) + return ret; + } + if (is_scaling(cam) || + (icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_NV16) || + (icd->current_fmt->host_fmt->fourcc == V4L2_PIX_FMT_NV12)) + iowrite32(ALIGN(cam->out_width, 0x20), + priv->base + VNIS_REG); + else + iowrite32(ALIGN(cam->out_width, 0x10), + priv->base + VNIS_REG); + } else { + /* Set scaling coefficient */ + value = 0; + if (cam_subrect->height != cam->out_height) + value = (4096 * cam_subrect->height) / cam->out_height; + dev_dbg(icd->parent, "YS Value: %x\n", value); + iowrite32(value, priv->base + VNYS_REG); + + value = 0; + if (cam_subrect->width != cam->out_width) + value = (4096 * cam_subrect->width) / cam->out_width; + + /* Horizontal upscaling is up to double size */ + if (value < 2048) + value = 2048; + + dev_dbg(icd->parent, "XS Value: %x\n", value); + iowrite32(value, priv->base + VNXS_REG); + + /* Horizontal upscaling is carried out */ + /* by scaling down from double size */ + if (value < 4096) + value *= 2; + + set_coeff(priv, value); + + /* Set Start/End Pixel/Line Post-Clip */ + iowrite32(0, priv->base + VNSPPOC_REG); + iowrite32(0, priv->base + VNSLPOC_REG); + iowrite32((cam->out_width - 1) << dsize, + priv->base + VNEPPOC_REG); + switch (priv->field) { + case V4L2_FIELD_INTERLACED: + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_INTERLACED_BT: + iowrite32(cam->out_height / 2 - 1, + priv->base + VNELPOC_REG); + break; + default: + iowrite32(cam->out_height - 1, + priv->base + VNELPOC_REG); + break; + } + + iowrite32(ALIGN(cam->out_width, 0x10), priv->base + VNIS_REG); + } + + return ret; +} + +static void capture_stop_preserve(struct rcar_vin_priv *priv, u32 *vnmc) +{ + *vnmc = ioread32(priv->base + VNMC_REG); + /* module disable */ + iowrite32(*vnmc & ~VNMC_ME, priv->base + VNMC_REG); +} + +static void capture_restore(struct rcar_vin_priv *priv, u32 vnmc) +{ + unsigned long timeout = jiffies + 10 * HZ; + + /* + * Wait until the end of the current frame. It can take a long time, + * but if it has been aborted by a MRST1 reset, it should exit sooner. + */ + while ((ioread32(priv->base + VNMS_REG) & VNMS_AV) && + time_before(jiffies, timeout)) + msleep(1); + + if (time_after(jiffies, timeout)) { + dev_err(priv->ici.v4l2_dev.dev, + "Timeout waiting for frame end! Interface problem?\n"); + return; + } + + iowrite32(vnmc, priv->base + VNMC_REG); +} + +#define VIN_MBUS_FLAGS (V4L2_MBUS_MASTER | \ + V4L2_MBUS_PCLK_SAMPLE_RISING | \ + V4L2_MBUS_HSYNC_ACTIVE_HIGH | \ + V4L2_MBUS_HSYNC_ACTIVE_LOW | \ + V4L2_MBUS_VSYNC_ACTIVE_HIGH | \ + V4L2_MBUS_VSYNC_ACTIVE_LOW | \ + V4L2_MBUS_DATA_ACTIVE_HIGH) + +static int rcar_vin_set_bus_param(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct rcar_vin_priv *priv = ici->priv; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct v4l2_mbus_config cfg; + unsigned long common_flags; + u32 vnmc; + u32 val; + int ret; + + capture_stop_preserve(priv, &vnmc); + + ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); + if (!ret) { + common_flags = soc_mbus_config_compatible(&cfg, VIN_MBUS_FLAGS); + if (!common_flags) { + dev_warn(icd->parent, + "MBUS flags incompatible: camera 0x%x, host 0x%x\n", + cfg.flags, VIN_MBUS_FLAGS); + return -EINVAL; + } + } else if (ret != -ENOIOCTLCMD) { + return ret; + } else { + common_flags = VIN_MBUS_FLAGS; + } + + /* Make choises, based on platform preferences */ + if ((common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) && + (common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) { + if (priv->pdata_flags & RCAR_VIN_HSYNC_ACTIVE_LOW) + common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_HIGH; + else + common_flags &= ~V4L2_MBUS_HSYNC_ACTIVE_LOW; + } + + if ((common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) && + (common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) { + if (priv->pdata_flags & RCAR_VIN_VSYNC_ACTIVE_LOW) + common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_HIGH; + else + common_flags &= ~V4L2_MBUS_VSYNC_ACTIVE_LOW; + } + + cfg.flags = common_flags; + ret = v4l2_subdev_call(sd, video, s_mbus_config, &cfg); + if (ret < 0 && ret != -ENOIOCTLCMD) + return ret; + + if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3) { + if (cfg.type == V4L2_MBUS_CSI2) + vnmc &= ~VNMC_DPINE; + else + vnmc |= VNMC_DPINE; + } + + if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3) + val = VNDMR2_FTEV; + else + val = VNDMR2_FTEV | VNDMR2_VLV(1); + if (!(common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)) + val |= VNDMR2_VPS; + if (!(common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)) + val |= VNDMR2_HPS; + iowrite32(val, priv->base + VNDMR2_REG); + + ret = rcar_vin_set_rect(icd); + if (ret < 0) + return ret; + + capture_restore(priv, vnmc); + + return 0; +} + +static int rcar_vin_try_bus_param(struct soc_camera_device *icd, + unsigned char buswidth) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct v4l2_mbus_config cfg; + int ret; + + ret = v4l2_subdev_call(sd, video, g_mbus_config, &cfg); + if (ret == -ENOIOCTLCMD) + return 0; + else if (ret) + return ret; + + if (buswidth > 24) + return -EINVAL; + + /* check is there common mbus flags */ + ret = soc_mbus_config_compatible(&cfg, VIN_MBUS_FLAGS); + if (ret) + return 0; + + dev_warn(icd->parent, + "MBUS flags incompatible: camera 0x%x, host 0x%x\n", + cfg.flags, VIN_MBUS_FLAGS); + + return -EINVAL; +} + +static bool rcar_vin_packing_supported(const struct soc_mbus_pixelfmt *fmt) +{ + return fmt->packing == SOC_MBUS_PACKING_NONE || + (fmt->bits_per_sample > 8 && + fmt->packing == SOC_MBUS_PACKING_EXTEND16); +} + +static const struct soc_mbus_pixelfmt rcar_vin_formats[] = { + { + .fourcc = V4L2_PIX_FMT_NV12, + .name = "NV12", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_1_5X8, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PLANAR_2Y_C, + }, + { + .fourcc = V4L2_PIX_FMT_NV16, + .name = "NV16", + .bits_per_sample = 8, + .packing = SOC_MBUS_PACKING_2X8_PADHI, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PLANAR_Y_C, + }, + { + .fourcc = V4L2_PIX_FMT_YUYV, + .name = "YUYV", + .bits_per_sample = 16, + .packing = SOC_MBUS_PACKING_NONE, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, + { + .fourcc = V4L2_PIX_FMT_UYVY, + .name = "UYVY", + .bits_per_sample = 16, + .packing = SOC_MBUS_PACKING_NONE, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, + { + .fourcc = V4L2_PIX_FMT_RGB565, + .name = "RGB565", + .bits_per_sample = 16, + .packing = SOC_MBUS_PACKING_NONE, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, + { + .fourcc = V4L2_PIX_FMT_ARGB555, + .name = "ARGB1555", + .bits_per_sample = 16, + .packing = SOC_MBUS_PACKING_NONE, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, + { + .fourcc = V4L2_PIX_FMT_XBGR32, + .name = "RGB888", + .bits_per_sample = 32, + .packing = SOC_MBUS_PACKING_NONE, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, + { + .fourcc = V4L2_PIX_FMT_ABGR32, + .name = "ARGB8888", + .bits_per_sample = 32, + .packing = SOC_MBUS_PACKING_NONE, + .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, + }, +}; + +static int rcar_vin_get_formats(struct soc_camera_device *icd, unsigned int idx, + struct soc_camera_format_xlate *xlate) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct device *dev = icd->parent; + int ret, k, n; + int formats = 0; + struct rcar_vin_cam *cam; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct rcar_vin_priv *priv = ici->priv; + struct v4l2_subdev_mbus_code_enum code = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .index = idx, + }; + const struct soc_mbus_pixelfmt *fmt; + + ret = v4l2_subdev_call(sd, pad, enum_mbus_code, NULL, &code); + if (ret < 0) + return 0; + + fmt = soc_mbus_get_fmtdesc(code.code); + if (!fmt) { + dev_warn(dev, "unsupported format code #%u: %d\n", idx, code.code); + return 0; + } + + ret = rcar_vin_try_bus_param(icd, fmt->bits_per_sample); + if (ret < 0) + return 0; + + if (!icd->host_priv) { + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mf = &fmt.format; + struct v4l2_rect rect; + struct device *dev = icd->parent; + int shift; + + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); + if (ret < 0) + return ret; + + /* Cache current client geometry */ + ret = soc_camera_client_g_rect(sd, &rect); + if (ret == -ENOIOCTLCMD) { + /* Sensor driver doesn't support cropping */ + rect.left = 0; + rect.top = 0; + rect.width = mf->width; + rect.height = mf->height; + } else if (ret < 0) { + return ret; + } + + /* + * If sensor proposes too large format then try smaller ones: + * 1280x960, 640x480, 320x240 + */ + for (shift = 0; shift < 3; shift++) { + if (mf->width <= priv->max_width && + mf->height <= priv->max_height) + break; + + mf->width = 1280 >> shift; + mf->height = 960 >> shift; + ret = v4l2_device_call_until_err(sd->v4l2_dev, + soc_camera_grp_id(icd), + pad, set_fmt, NULL, + &fmt); + if (ret < 0) + return ret; + } + + if (shift == 3) { + dev_err(dev, + "Failed to configure the client below %ux%u\n", + mf->width, mf->height); + return -EIO; + } + + dev_dbg(dev, "camera fmt %ux%u\n", mf->width, mf->height); + + cam = kzalloc(sizeof(*cam), GFP_KERNEL); + if (!cam) + return -ENOMEM; + /* + * We are called with current camera crop, + * initialise subrect with it + */ + cam->rect = rect; + cam->subrect = rect; + cam->width = mf->width; + cam->height = mf->height; + cam->out_width = mf->width; + cam->out_height = mf->height; + + icd->host_priv = cam; + } else { + cam = icd->host_priv; + } + + /* Beginning of a pass */ + if (!idx) + cam->extra_fmt = NULL; + + switch (code.code) { + case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_YUYV8_2X8: + case MEDIA_BUS_FMT_YUYV10_2X10: + case MEDIA_BUS_FMT_RGB888_1X24: + if (cam->extra_fmt) + break; + + /* Add all our formats that can be generated by VIN */ + cam->extra_fmt = rcar_vin_formats; + + n = ARRAY_SIZE(rcar_vin_formats); + formats += n; + for (k = 0; xlate && k < n; k++, xlate++) { + xlate->host_fmt = &rcar_vin_formats[k]; + xlate->code = code.code; + dev_dbg(dev, "Providing format %s using code %d\n", + rcar_vin_formats[k].name, code.code); + } + break; + default: + if (!rcar_vin_packing_supported(fmt)) + return 0; + + dev_dbg(dev, "Providing format %s in pass-through mode\n", + fmt->name); + break; + } + + /* Generic pass-through */ + formats++; + if (xlate) { + xlate->host_fmt = fmt; + xlate->code = code.code; + xlate++; + } + + return formats; +} + +static void rcar_vin_put_formats(struct soc_camera_device *icd) +{ + kfree(icd->host_priv); + icd->host_priv = NULL; +} + +static int rcar_vin_set_selection(struct soc_camera_device *icd, + struct v4l2_selection *sel) +{ + const struct v4l2_rect *rect = &sel->r; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct rcar_vin_priv *priv = ici->priv; + struct v4l2_selection cam_sel; + struct rcar_vin_cam *cam = icd->host_priv; + struct v4l2_rect *cam_rect = &cam_sel.r; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct device *dev = icd->parent; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mf = &fmt.format; + u32 vnmc; + int ret, i; + + dev_dbg(dev, "S_SELECTION(%ux%u@%u:%u)\n", rect->width, rect->height, + rect->left, rect->top); + + /* During camera cropping its output window can change too, stop VIN */ + capture_stop_preserve(priv, &vnmc); + dev_dbg(dev, "VNMC_REG 0x%x\n", vnmc); + + /* Apply iterative camera S_SELECTION for new input window. */ + ret = soc_camera_client_s_selection(sd, sel, &cam_sel, + &cam->rect, &cam->subrect); + if (ret < 0) + return ret; + + dev_dbg(dev, "camera cropped to %ux%u@%u:%u\n", + cam_rect->width, cam_rect->height, + cam_rect->left, cam_rect->top); + + /* On success cam_crop contains current camera crop */ + + /* Retrieve camera output window */ + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); + if (ret < 0) + return ret; + + if (mf->width > priv->max_width || mf->height > priv->max_height) + return -EINVAL; + + /* Cache camera output window */ + cam->width = mf->width; + cam->height = mf->height; + + cam->vin_left = rect->left; + cam->vin_top = rect->top; + + /* Use VIN cropping to crop to the new window. */ + ret = rcar_vin_set_rect(icd); + if (ret < 0) + return ret; + + dev_dbg(dev, "VIN cropped to %ux%u@%u:%u\n", + icd->user_width, icd->user_height, + cam->vin_left, cam->vin_top); + + /* Restore capture */ + for (i = 0; i < MAX_BUFFER_NUM; i++) { + if (priv->queue_buf[i] && priv->state == STOPPED) { + vnmc |= VNMC_ME; + break; + } + } + capture_restore(priv, vnmc); + + /* Even if only camera cropping succeeded */ + return ret; +} + +static int rcar_vin_get_selection(struct soc_camera_device *icd, + struct v4l2_selection *sel) +{ + struct rcar_vin_cam *cam = icd->host_priv; + + sel->r = cam->subrect; + + return 0; +} + +/* Similar to set_crop multistage iterative algorithm */ +static int rcar_vin_set_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct rcar_vin_priv *priv = ici->priv; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct rcar_vin_cam *cam = icd->host_priv; + struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_mbus_framefmt mf; + struct device *dev = icd->parent; + __u32 pixfmt = pix->pixelformat; + const struct soc_camera_format_xlate *xlate; + unsigned int vin_sub_width = 0, vin_sub_height = 0; + int ret; + bool can_scale; + enum v4l2_field field; + v4l2_std_id std; + + dev_dbg(dev, "S_FMT(pix=0x%x, %ux%u)\n", + pixfmt, pix->width, pix->height); + + /* At the time of NV16 capture format, the user has to specify */ + /* the width of the multiple of 32 for H/W specification. */ + if (priv->error_flag == false) + priv->error_flag = true; + else { + if (((pixfmt == V4L2_PIX_FMT_NV16) || + (pixfmt == V4L2_PIX_FMT_NV12)) && + (pix->width & 0x1F)) { + dev_dbg(icd->parent, + "specify width of 32 multiple in separate format.\n"); + return -EINVAL; + } + } + + switch (pix->field) { + default: + pix->field = V4L2_FIELD_NONE; + /* fall-through */ + case V4L2_FIELD_NONE: + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_INTERLACED_BT: + field = pix->field; + break; + case V4L2_FIELD_INTERLACED: + /* Query for standard if not explicitly mentioned _TB/_BT */ + ret = v4l2_subdev_call(sd, video, querystd, &std); + if (ret == -ENOIOCTLCMD) { + field = V4L2_FIELD_NONE; + } else if (ret < 0) { + return ret; + } else { + field = std & V4L2_STD_625_50 ? + V4L2_FIELD_INTERLACED_TB : + V4L2_FIELD_INTERLACED_BT; + } + break; + } + + xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); + if (!xlate) { + dev_warn(dev, "Format %x not found\n", pixfmt); + return -EINVAL; + } + /* Calculate client output geometry */ + soc_camera_calc_client_output(icd, &cam->rect, &cam->subrect, pix, &mf, + 12); + mf.field = pix->field; + mf.colorspace = pix->colorspace; + mf.code = xlate->code; + + switch (pixfmt) { + case V4L2_PIX_FMT_XBGR32: + can_scale = priv->chip != RCAR_E1; + break; + case V4L2_PIX_FMT_ABGR32: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_ARGB555: + case V4L2_PIX_FMT_NV16: + can_scale = true; + break; + case V4L2_PIX_FMT_NV12: + default: + can_scale = false; + break; + } + + dev_dbg(dev, "request camera output %ux%u\n", mf.width, mf.height); + + ret = soc_camera_client_scale(icd, &cam->rect, &cam->subrect, + &mf, &vin_sub_width, &vin_sub_height, + can_scale, 12); + + /* Done with the camera. Now see if we can improve the result */ + dev_dbg(dev, "Camera %d fmt %ux%u, requested %ux%u\n", + ret, mf.width, mf.height, pix->width, pix->height); + + if (ret == -ENOIOCTLCMD) + dev_dbg(dev, "Sensor doesn't support scaling\n"); + else if (ret < 0) + return ret; + + if (mf.code != xlate->code) + return -EINVAL; + + /* Prepare VIN crop */ + cam->width = mf.width; + cam->height = mf.height; + + /* Use VIN scaling to scale to the requested user window. */ + + /* We cannot scale up */ + if (pix->width > vin_sub_width) + vin_sub_width = pix->width; + + if (pix->height > vin_sub_height) + vin_sub_height = pix->height; + + pix->colorspace = mf.colorspace; + + if (!can_scale) { + pix->width = vin_sub_width; + pix->height = vin_sub_height; + } + + /* + * We have calculated CFLCR, the actual configuration will be performed + * in rcar_vin_set_bus_param() + */ + + dev_dbg(dev, "W: %u : %u, H: %u : %u\n", + vin_sub_width, pix->width, vin_sub_height, pix->height); + + cam->out_width = pix->width; + cam->out_height = pix->height; + + icd->current_fmt = xlate; + + priv->field = field; + + return 0; +} + +static int rcar_vin_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + const struct soc_camera_format_xlate *xlate; + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct rcar_vin_priv *priv = ici->priv; + struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct v4l2_subdev_pad_config pad_cfg; + struct v4l2_subdev_format format = { + .which = V4L2_SUBDEV_FORMAT_TRY, + }; + struct v4l2_mbus_framefmt *mf = &format.format; + __u32 pixfmt = pix->pixelformat; + int width, height; + int ret; + + xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); + if (!xlate) { + xlate = icd->current_fmt; + dev_dbg(icd->parent, "Format %x not found, keeping %x\n", + pixfmt, xlate->host_fmt->fourcc); + pixfmt = xlate->host_fmt->fourcc; + pix->pixelformat = pixfmt; + pix->colorspace = icd->colorspace; + } + + /* When performing a YCbCr-422 format output, even if it performs */ + /* odd number clipping by pixel post clip processing, */ + /* it is outputted to a memory per even pixels. */ + if ((pixfmt == V4L2_PIX_FMT_NV16) || (pixfmt == V4L2_PIX_FMT_NV12) || + (pixfmt == V4L2_PIX_FMT_YUYV) || (pixfmt == V4L2_PIX_FMT_UYVY)) + v4l_bound_align_image(&pix->width, 5, priv->max_width, 1, + &pix->height, 2, priv->max_height, 0, 0); + else + v4l_bound_align_image(&pix->width, 5, priv->max_width, 0, + &pix->height, 2, priv->max_height, 0, 0); + + width = pix->width; + height = pix->height; + + /* let soc-camera calculate these values */ + pix->bytesperline = 0; + pix->sizeimage = 0; + + /* limit to sensor capabilities */ + mf->width = pix->width; + mf->height = pix->height; + mf->field = pix->field; + mf->code = xlate->code; + mf->colorspace = pix->colorspace; + + ret = v4l2_device_call_until_err(sd->v4l2_dev, soc_camera_grp_id(icd), + pad, set_fmt, &pad_cfg, &format); + if (ret < 0) + return ret; + + if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3) { + /* Adjust max scaling size for Gen3 */ + if (pix->width > 4096) + pix->width = priv->max_width; + if (pix->height > 4096) + pix->height = priv->max_height; + } else { + /* Adjust only if VIN cannot scale */ + if (pix->width > mf->width * 2) + pix->width = mf->width * 2; + if (pix->height > mf->height * 3) + pix->height = mf->height * 3; + } + + pix->field = mf->field; + pix->colorspace = mf->colorspace; + + if (pixfmt == V4L2_PIX_FMT_NV16) { + /* FIXME: check against rect_max after converting soc-camera */ + /* We can scale precisely, need a bigger image from camera */ + if (pix->width < width || pix->height < height) { + /* + * We presume, the sensor behaves sanely, i.e. if + * requested a bigger rectangle, it will not return a + * smaller one. + */ + mf->width = priv->max_width; + mf->height = priv->max_height; + ret = v4l2_device_call_until_err(sd->v4l2_dev, + soc_camera_grp_id(icd), + pad, set_fmt, &pad_cfg, + &format); + if (ret < 0) { + dev_err(icd->parent, + "client try_fmt() = %d\n", ret); + return ret; + } + } + /* We will scale exactly */ + if (mf->width > width) + pix->width = width; + if (mf->height > height) + pix->height = height; + } + + return ret; +} + +static unsigned int rcar_vin_poll(struct file *file, poll_table *pt) +{ + struct soc_camera_device *icd = file->private_data; + + return vb2_poll(&icd->vb2_vidq, file, pt); +} + +static int rcar_vin_querycap(struct soc_camera_host *ici, + struct v4l2_capability *cap) +{ + strlcpy(cap->card, "R_Car_VIN", sizeof(cap->card)); + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s%d", DRV_NAME, ici->nr); + + return 0; +} + +static int rcar_vin_init_videobuf2(struct vb2_queue *vq, + struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + + vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; + vq->drv_priv = icd; + vq->ops = &rcar_vin_vb2_ops; + vq->mem_ops = &vb2_dma_contig_memops; + vq->buf_struct_size = sizeof(struct rcar_vin_buffer); + vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + vq->lock = &ici->host_lock; + vq->dev = ici->v4l2_dev.dev; + + return vb2_queue_init(vq); +} + +#if 0 +static int rcar_vin_get_selection(struct soc_camera_device *icd, + struct v4l2_selection *sel) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mf = &fmt.format; + int ret; + + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); + if (ret < 0) + return ret; + + if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + sel->r.left = sel->r.top = 0; + sel->r.width = mf->width; + sel->r.height = mf->height; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int rcar_vin_cropcap(struct soc_camera_device *icd, + struct v4l2_cropcap *crop) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + struct v4l2_mbus_framefmt *mf = &fmt.format; + int ret; + + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt); + if (ret < 0) + return ret; + + crop->bounds.left = 0; + crop->bounds.top = 0; + crop->bounds.width = mf->width; + crop->bounds.height = mf->height; + + /* default cropping rectangle */ + crop->defrect = crop->bounds; + crop->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + return 0; +} +#endif + +static struct soc_camera_host_ops rcar_vin_host_ops = { + .owner = THIS_MODULE, + .add = rcar_vin_add_device, + .remove = rcar_vin_remove_device, + .get_formats = rcar_vin_get_formats, + .put_formats = rcar_vin_put_formats, + .get_selection = rcar_vin_get_selection, + .set_selection = rcar_vin_set_selection, + .try_fmt = rcar_vin_try_fmt, + .set_fmt = rcar_vin_set_fmt, + .poll = rcar_vin_poll, + .querycap = rcar_vin_querycap, + .set_bus_param = rcar_vin_set_bus_param, + .init_videobuf2 = rcar_vin_init_videobuf2, +#if 0 + .get_selection = rcar_vin_get_selection, + .cropcap = rcar_vin_cropcap, +#endif +}; + +#ifdef CONFIG_OF +static const struct of_device_id rcar_vin_of_table[] = { + { .compatible = "renesas,vin-r8a7796", .data = (void *)RCAR_M3 }, + { .compatible = "renesas,vin-r8a7795", .data = (void *)RCAR_H3 }, + { .compatible = "renesas,vin-r8a7794", .data = (void *)RCAR_GEN2 }, + { .compatible = "renesas,vin-r8a7793", .data = (void *)RCAR_GEN2 }, + { .compatible = "renesas,vin-r8a7791", .data = (void *)RCAR_GEN2 }, + { .compatible = "renesas,vin-r8a7790", .data = (void *)RCAR_GEN2 }, + { .compatible = "renesas,vin-r8a7779", .data = (void *)RCAR_H1 }, + { .compatible = "renesas,vin-r8a7778", .data = (void *)RCAR_M1 }, + { .compatible = "renesas,rcar-gen3-vin", .data = (void *)RCAR_GEN3 }, + { .compatible = "renesas,rcar-gen2-vin", .data = (void *)RCAR_GEN2 }, + { }, +}; +MODULE_DEVICE_TABLE(of, rcar_vin_of_table); +#endif + +#define MAP_MAX_NUM 32 +static DECLARE_BITMAP(device_map, MAP_MAX_NUM); +static DEFINE_MUTEX(list_lock); + +static int rcar_vin_dyn_pdev(struct soc_camera_desc *sdesc, + struct rcar_vin_async_client *sasc) +{ + struct platform_device *pdev; + int ret, i; + + mutex_lock(&list_lock); + i = find_first_zero_bit(device_map, MAP_MAX_NUM); + if (i < MAP_MAX_NUM) + set_bit(i, device_map); + mutex_unlock(&list_lock); + if (i >= MAP_MAX_NUM) + return -ENOMEM; + + pdev = platform_device_alloc("soc-camera-pdrv", ((2 * i) + 1)); + if (!pdev) + return -ENOMEM; + + ret = platform_device_add_data(pdev, sdesc, sizeof(*sdesc)); + if (ret < 0) { + platform_device_put(pdev); + return ret; + } + + sasc->pdev = pdev; + + return 0; +} + +static int rcar_vin_async_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_subdev *asd) +{ + /* None. */ + return 0; +} + +static void rcar_vin_async_unbind(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *sd, + struct v4l2_async_subdev *asd) +{ + /* None. */ +} + +static int rcar_vin_async_probe(struct soc_camera_host *ici, + struct soc_camera_device *icd) +{ + struct soc_camera_desc *sdesc = to_soc_camera_desc(icd); + struct soc_camera_host_desc *shd = &sdesc->host_desc; + struct device *control = NULL; + int ret; + + ret = v4l2_ctrl_handler_init(&icd->ctrl_handler, 16); + if (ret < 0) + return ret; + + if (shd->module_name) + ret = request_module(shd->module_name); + + ret = shd->add_device(icd); + + control = to_soc_camera_control(icd); + if (!control || !control->driver || !dev_get_drvdata(control) || + !try_module_get(control->driver->owner)) { + shd->del_device(icd); + ret = -ENODEV; + } + + return ret; +} + +static int rcar_vin_async_complete(struct v4l2_async_notifier *notifier) +{ + struct rcar_vin_async_client *sasc = container_of(notifier, + struct rcar_vin_async_client, notifier); + struct soc_camera_device *icd = platform_get_drvdata(sasc->pdev); + + if (to_soc_camera_control(icd)) { + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + int ret; + + mutex_lock(&list_lock); + ret = rcar_vin_async_probe(ici, icd); + mutex_unlock(&list_lock); + if (ret < 0) + return ret; + } + + return 0; +} + +static struct soc_camera_device *rcar_vin_add_pdev( + struct rcar_vin_async_client *sasc) +{ + struct platform_device *pdev = sasc->pdev; + int ret; + + ret = platform_device_add(pdev); + + if (ret < 0 || !pdev->dev.driver) + return NULL; + + return platform_get_drvdata(pdev); +} + +static int rcar_vin_soc_of_bind(struct rcar_vin_priv *priv, + struct soc_camera_host *ici, + struct device_node *ep, + struct device_node *remote) +{ + struct soc_camera_device *icd; + struct soc_camera_desc sdesc = {.host_desc.bus_id = ici->nr,}; + struct rcar_vin_async_client *sasc; + struct soc_of_info *info; + struct i2c_client *client; + char clk_name[V4L2_SUBDEV_NAME_SIZE]; + int ret; + + /* allocate a new subdev and add match info to it */ + info = devm_kzalloc(ici->v4l2_dev.dev, sizeof(struct soc_of_info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->sasd.asd.match.of.node = remote; + info->sasd.asd.match_type = V4L2_ASYNC_MATCH_OF; + info->subdev = &info->sasd.asd; + + /* Or shall this be managed by the soc-camera device? */ + sasc = &info->sasc; + + ret = rcar_vin_dyn_pdev(&sdesc, sasc); + if (ret < 0) + goto eallocpdev; + + sasc->sensor = &info->sasd.asd; + + icd = rcar_vin_add_pdev(sasc); + if (!icd) { + ret = -ENOMEM; + goto eaddpdev; + } + + sasc->notifier.subdevs = &info->subdev; + sasc->notifier.num_subdevs = 1; + sasc->notifier.bound = rcar_vin_async_bound; + sasc->notifier.unbind = rcar_vin_async_unbind; + sasc->notifier.complete = rcar_vin_async_complete; + + priv->async_client = sasc; + + client = of_find_i2c_device_by_node(remote); + + if (client) + snprintf(clk_name, sizeof(clk_name), "%d-%04x", + client->adapter->nr, client->addr); + else + snprintf(clk_name, sizeof(clk_name), "of-%s", + of_node_full_name(remote)); + + ret = v4l2_async_notifier_register(&ici->v4l2_dev, &sasc->notifier); + if (!ret) + return 0; + + platform_device_del(sasc->pdev); +eaddpdev: + platform_device_put(sasc->pdev); +eallocpdev: + devm_kfree(ici->v4l2_dev.dev, info); + dev_err(ici->v4l2_dev.dev, "group probe failed: %d\n", ret); + + return ret; +} + +static int rcar_vin_probe(struct platform_device *pdev) +{ + const struct of_device_id *match = NULL; + struct rcar_vin_priv *priv; + struct v4l2_of_endpoint ep; + struct device_node *np; + struct resource *mem; + unsigned int pdata_flags; + int irq, ret; + const char *str; + unsigned int i; + struct device_node *epn = NULL, *ren = NULL; + bool csi_use = false; + + match = of_match_device(of_match_ptr(rcar_vin_of_table), &pdev->dev); + + np = of_graph_get_next_endpoint(pdev->dev.of_node, NULL); + if (!np) { + dev_err(&pdev->dev, "could not find endpoint\n"); + return -EINVAL; + } + + for (i = 0; ; i++) { + epn = of_graph_get_next_endpoint(pdev->dev.of_node, + epn); + if (!epn) + break; + + ren = of_graph_get_remote_port(epn); + if (!ren) { + dev_notice(&pdev->dev, "no remote for %s\n", + of_node_full_name(epn)); + continue; + } + + /* so we now have a remote node to connect */ + dev_dbg(&pdev->dev, "node name:%s\n", + of_node_full_name(ren->parent)); + + if (strcmp(ren->parent->name, "csi2") == 0) + csi_use = true; + + of_node_put(ren); + + if (i) + break; + } + + ret = v4l2_of_parse_endpoint(np, &ep); + if (ret) { + dev_err(&pdev->dev, "could not parse endpoint\n"); + return ret; + } + + if (ep.bus_type == V4L2_MBUS_BT656) + pdata_flags = RCAR_VIN_BT656; + else if (ep.bus_type == V4L2_MBUS_CSI2) + pdata_flags = RCAR_VIN_BT656 | RCAR_VIN_CSI2; + else { + pdata_flags = 0; + if (ep.bus.parallel.flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) + pdata_flags |= RCAR_VIN_HSYNC_ACTIVE_LOW; + if (ep.bus.parallel.flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) + pdata_flags |= RCAR_VIN_VSYNC_ACTIVE_LOW; + } + + of_node_put(np); + + dev_dbg(&pdev->dev, "pdata_flags = %08x\n", pdata_flags); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (mem == NULL) + return -EINVAL; + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) + return -EINVAL; + + priv = devm_kzalloc(&pdev->dev, sizeof(struct rcar_vin_priv), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + ret = devm_request_irq(&pdev->dev, irq, rcar_vin_irq, IRQF_SHARED, + dev_name(&pdev->dev), priv); + if (ret) + return ret; + + priv->ici.priv = priv; + priv->ici.v4l2_dev.dev = &pdev->dev; + priv->ici.drv_name = dev_name(&pdev->dev); + priv->ici.ops = &rcar_vin_host_ops; + priv->csi_sync = false; + + priv->pdata_flags = pdata_flags; + if (!match) { + priv->ici.nr = pdev->id; + priv->chip = pdev->id_entry->driver_data; + } else { + priv->ici.nr = of_alias_get_id(pdev->dev.of_node, "vin"); + priv->chip = (enum chip_id)match->data; + } + + if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3) { + priv->max_width = 4096; + priv->max_height = 4096; + } else { + priv->max_width = 2048; + priv->max_height = 2048; + } + + if (priv->chip == RCAR_H3 || priv->chip == RCAR_M3) { + u32 ifmd = 0; + bool match_flag = false; + const struct vin_gen3_ifmd *gen3_ifmd_table = NULL; + int vc, num; + + num = VNCSI_IFMD_SEL_NUMBER; + + if (strcmp(dev_name(priv->ici.v4l2_dev.dev), + "e6ef0000.video") == 0) + priv->index = RCAR_VIDEO_0; + else if (strcmp(dev_name(priv->ici.v4l2_dev.dev), + "e6ef1000.video") == 0) + priv->index = RCAR_VIDEO_1; + else if (strcmp(dev_name(priv->ici.v4l2_dev.dev), + "e6ef2000.video") == 0) + priv->index = RCAR_VIDEO_2; + else if (strcmp(dev_name(priv->ici.v4l2_dev.dev), + "e6ef3000.video") == 0) + priv->index = RCAR_VIDEO_3; + else if (strcmp(dev_name(priv->ici.v4l2_dev.dev), + "e6ef4000.video") == 0) + priv->index = RCAR_VIDEO_4; + else if (strcmp(dev_name(priv->ici.v4l2_dev.dev), + "e6ef5000.video") == 0) + priv->index = RCAR_VIDEO_5; + else if (strcmp(dev_name(priv->ici.v4l2_dev.dev), + "e6ef6000.video") == 0) + priv->index = RCAR_VIDEO_6; + else if (strcmp(dev_name(priv->ici.v4l2_dev.dev), + "e6ef7000.video") == 0) + priv->index = RCAR_VIDEO_7; + else + priv->index = RCAR_VIN_CH_NONE; + + ret = of_property_read_string(np, "csi,select", &str); + if (ret) { + dev_err(&pdev->dev, "could not parse csi,select\n"); + return ret; + } + + if (strcmp(str, "csi40") == 0) + priv->csi_ch = RCAR_CSI40; + else if (strcmp(str, "csi20") == 0) + priv->csi_ch = RCAR_CSI20; + else if (strcmp(str, "csi41") == 0) + priv->csi_ch = RCAR_CSI41; + else if (strcmp(str, "csi21") == 0) + priv->csi_ch = RCAR_CSI21; + else + priv->csi_ch = RCAR_CSI_CH_NONE; + + ret = of_property_read_u32(np, "virtual,channel", &vc); + if (ret) { + dev_err(&pdev->dev, + "could not parse virtual,channel\n"); + return ret; + } + + if (vc == 0) + priv->vc = RCAR_VIRTUAL_CH0; + else if (vc == 1) + priv->vc = RCAR_VIRTUAL_CH1; + else if (vc == 2) + priv->vc = RCAR_VIRTUAL_CH2; + else if (vc == 3) + priv->vc = RCAR_VIRTUAL_CH3; + else + priv->vc = RCAR_VIRTUAL_NONE; + + dev_dbg(&pdev->dev, "csi_ch:%d, vc:%d\n", + priv->csi_ch, priv->vc); + + ifmd = VNCSI_IFMD_DES1 | VNCSI_IFMD_DES0; + + if (priv->chip == RCAR_H3) + gen3_ifmd_table = vin_h3_vc_ifmd; + else if (priv->chip == RCAR_M3) + gen3_ifmd_table = vin_m3_vc_ifmd; + + for (i = 0; i < num; i++) { + if ((gen3_ifmd_table[i].v_sel[priv->index].csi2_ch + == priv->csi_ch) && + (gen3_ifmd_table[i].v_sel[priv->index].vc + == priv->vc)) { + if (priv->index < RCAR_VIDEO_4) { + if (ifmd0_init) { + ifmd0_reg_match[i] = true; + match_flag = true; + } else if (ifmd0_reg_match[i]) + match_flag = true; + } else { + if (ifmd4_init) { + ifmd4_reg_match[i] = true; + match_flag = true; + } else if (ifmd4_reg_match[i]) + match_flag = true; + } + } else { + if (priv->index < RCAR_VIDEO_4) + ifmd0_reg_match[i] = false; + else + ifmd4_reg_match[i] = false; + } + } + if (priv->index < RCAR_VIDEO_4) + ifmd0_init = false; + else + ifmd4_init = false; + + if (!match_flag) { + dev_err(&pdev->dev, + "Not match, virtual channel pattern error.\n"); + return -EINVAL; + } + + rcar_vin_cpg_enable_for_ifmd(priv->index, true); + + if (priv->index < RCAR_VIDEO_4) { + void __iomem *ifmd0_mem; + + for (i = 0; i < num; i++) { + if (ifmd0_reg_match[i]) { + ifmd |= gen3_ifmd_table[i].set_reg; + break; + } + } + + ifmd0_mem = ioremap(0xe6ef0000 + VNCSI_IFMD_REG, 0x04); + iowrite32(ifmd, ifmd0_mem); + iounmap(ifmd0_mem); + } else { + void __iomem *ifmd4_mem; + + for (i = 0; i < num; i++) { + if (ifmd4_reg_match[i]) { + ifmd |= gen3_ifmd_table[i].set_reg; + break; + } + } + + ifmd4_mem = ioremap(0xe6ef4000 + VNCSI_IFMD_REG, 0x04); + iowrite32(ifmd, ifmd4_mem); + iounmap(ifmd4_mem); + } + + rcar_vin_cpg_enable_for_ifmd(priv->index, false); + } + + spin_lock_init(&priv->lock); + INIT_LIST_HEAD(&priv->capture); + + priv->state = STOPPED; + + pm_suspend_ignore_children(&pdev->dev, true); + pm_runtime_enable(&pdev->dev); + + ret = soc_camera_host_register(&priv->ici); + if (ret) + goto cleanup; + + if (csi_use) { + ret = rcar_vin_soc_of_bind(priv, &priv->ici, epn, ren->parent); + if (ret) + goto cleanup; + } + + vin_debug = 0; + + return 0; + +cleanup: + pm_runtime_disable(&pdev->dev); + + return ret; +} + +static int rcar_vin_remove(struct platform_device *pdev) +{ + struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); + struct rcar_vin_priv *priv = container_of(soc_host, + struct rcar_vin_priv, ici); + + platform_device_del(priv->async_client->pdev); + platform_device_put(priv->async_client->pdev); + + v4l2_async_notifier_unregister(&priv->async_client->notifier); + + soc_camera_host_unregister(soc_host); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int rcar_vin_suspend(struct device *dev) +{ + /* Empty function for now */ + return 0; +} + +static int rcar_vin_resume(struct device *dev) +{ + u32 ifmd = 0; + bool match_flag = false; + const struct vin_gen3_ifmd *gen3_ifmd_table = NULL; + int num; + unsigned int i; + struct soc_camera_host *soc_host = to_soc_camera_host(dev); + struct rcar_vin_priv *priv = container_of(soc_host, + struct rcar_vin_priv, ici); + num = VNCSI_IFMD_SEL_NUMBER; + ifmd0_init = true; + ifmd4_init = true; + + if (priv->chip == RCAR_H3) { + ifmd = VNCSI_IFMD_DES1 | VNCSI_IFMD_DES0; + gen3_ifmd_table = vin_h3_vc_ifmd; + } else if (priv->chip == RCAR_M3) { + ifmd = VNCSI_IFMD_DES1; + gen3_ifmd_table = vin_m3_vc_ifmd; + } + + for (i = 0; i < num; i++) { + if ((gen3_ifmd_table[i].v_sel[priv->index].csi2_ch + == priv->csi_ch) && + (gen3_ifmd_table[i].v_sel[priv->index].vc + == priv->vc)) { + if (priv->index < RCAR_VIDEO_4) { + if (ifmd0_init) { + ifmd0_reg_match[i] = true; + match_flag = true; + } else if (ifmd0_reg_match[i]) + match_flag = true; + } else { + if (ifmd4_init) { + ifmd4_reg_match[i] = true; + match_flag = true; + } else if (ifmd4_reg_match[i]) + match_flag = true; + } + } else { + if (priv->index < RCAR_VIDEO_4) + ifmd0_reg_match[i] = false; + else + ifmd4_reg_match[i] = false; + } + } + if (priv->index < RCAR_VIDEO_4) + ifmd0_init = false; + else + ifmd4_init = false; + + if (priv->index < RCAR_VIDEO_4) { + void __iomem *ifmd0_mem; + + for (i = 0; i < num; i++) { + if (ifmd0_reg_match[i]) { + ifmd |= gen3_ifmd_table[i].set_reg; + break; + } + } + + ifmd0_mem = ioremap(0xe6ef0000 + VNCSI_IFMD_REG, 0x04); + iowrite32(ifmd, ifmd0_mem); + iounmap(ifmd0_mem); + } else { + void __iomem *ifmd4_mem; + + for (i = 0; i < num; i++) { + if (ifmd4_reg_match[i]) { + ifmd |= gen3_ifmd_table[i].set_reg; + break; + } + } + + ifmd4_mem = ioremap(0xe6ef4000 + VNCSI_IFMD_REG, 0x04); + iowrite32(ifmd, ifmd4_mem); + iounmap(ifmd4_mem); + } + + return 0; +} + +static SIMPLE_DEV_PM_OPS(rcar_vin_pm_ops, + rcar_vin_suspend, rcar_vin_resume); +#define DEV_PM_OPS (&rcar_vin_pm_ops) +#else +#define DEV_PM_OPS NULL +#endif /* CONFIG_PM_SLEEP */ + +static struct platform_driver rcar_vin_driver = { + .probe = rcar_vin_probe, + .remove = rcar_vin_remove, + .driver = { + .name = DRV_NAME, + .pm = DEV_PM_OPS, + .of_match_table = of_match_ptr(rcar_vin_of_table), + }, +}; + +module_platform_driver(rcar_vin_driver); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rcar_vin"); +MODULE_DESCRIPTION("Renesas R-Car VIN camera host driver"); +MODULE_AUTHOR("Koji Matsuoka "); diff --git a/include/media/rcar_csi2.h b/include/media/rcar_csi2.h new file mode 100644 index 0000000..1a040fa --- /dev/null +++ b/include/media/rcar_csi2.h @@ -0,0 +1,66 @@ +/* + * include/media/rcar_csi2.h + * This file is the driver header + * for the Renesas R-Car MIPI CSI-2 unit. + * + * Copyright (C) 2015 Renesas Electronics Corporation + * + * This file is based on the include/media/sh_mobile_csi2.h + * + * Driver header for the SH-Mobile MIPI CSI-2 unit + * + * Copyright (C) 2010, Guennadi Liakhovetski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef RCAR_MIPI_CSI +#define RCAR_MIPI_CSI + +#include + +enum rcar_csi2_phy { + RCAR_CSI2_PHY_CSI40, /* CSI0 */ + RCAR_CSI2_PHY_CSI20, /* CSI1 */ + RCAR_CSI2_PHY_CSI41, /* CSI2 */ + RCAR_CSI2_PHY_CSI21, /* CSI3 */ +}; + +enum rcar_csi2_link { + RCAR_CSI2_LINK_CSI40, + RCAR_CSI2_LINK_CSI20, + RCAR_CSI2_LINK_CSI41, + RCAR_CSI2_LINK_CSI21, +}; + +enum rcar_csi2_type { + RCAR_CSI2_CSI4X, + RCAR_CSI2_CSI2X, +}; + +#define RCAR_CSI2_CRC (1 << 0) +#define RCAR_CSI2_ECC (1 << 1) + +struct platform_device; + +struct rcar_csi2_client_config { + enum rcar_csi2_phy phy; + enum rcar_csi2_link link; + unsigned char lanes; /* bitmask[3:0] */ + unsigned char channel; /* 0..3 */ + struct platform_device *pdev; /* client platform device */ + const char *name; /* async matching: client name */ +}; + +struct v4l2_device; + +struct rcar_csi2_pdata { + enum rcar_csi2_type type; + unsigned int flags; + struct rcar_csi2_client_config *clients; + int num_clients; +}; + +#endif -- 1.9.1