aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2018-03-08 19:22:30 -0500
committerDave Airlie <airlied@redhat.com>2018-03-08 19:22:30 -0500
commit2ec360bbc319903f878135adeed85bb648ef95cf (patch)
tree797b19e8d05c6afa29690d9445c057105b0b6eef
parentf073d78eeb8efd85718e611c15f9a78647751dea (diff)
parent77f59f895da2fe5526073181c74c3fb85a7c80d1 (diff)
Merge branch 'drm/next/du' of git://linuxtv.org/pinchartl/media into drm-next
- Convert LVDS support to a drm_bridge driver - Add DT bindings for the R8A77995 SoC - Add DT bindings and driver support for the R8A77970 SoC Note that the LVDS conversion depends on a patch series from Frank Rowand that will make it upstream through Rob Herring's tree. Frank has provided a stable branch based on v4.16-rc1 with the patches, and both Rob and I have merged it into our trees. This should thus generate no conflict when reaching -next. * 'drm/next/du' of git://linuxtv.org/pinchartl/media: dt-bindings: display: renesas: lvds: Document r8a77995 bindings dt-bindings: display: renesas: du: Document r8a77995 bindings drm: rcar-du: lvds: Add R8A77970 support drm: rcar-du: Add R8A77970 support dt-bindings: display: renesas: lvds: Document R8A77970 bindings dt-bindings: display: renesas: du: Document R8A77970 bindings drm: rcar-du: Convert LVDS encoder code to bridge driver drm: rcar-du: Fix legacy DT to create LVDS encoder nodes dt-bindings: display: renesas: Deprecate LVDS support in the DU bindings dt-bindings: display: renesas: Add R-Car LVDS encoder DT bindings of: improve reporting invalid overlay target path of: convert unittest overlay devicetree source to sugar syntax of: Documentation: of_overlay_apply() replaced by of_overlay_fdt_apply() of: change overlay apply input data from unflattened to FDT x86: devicetree: fix config option around x86_flattree_get_config()
-rw-r--r--Documentation/devicetree/bindings/display/bridge/renesas,lvds.txt58
-rw-r--r--Documentation/devicetree/bindings/display/renesas,du.txt35
-rw-r--r--Documentation/devicetree/overlay-notes.txt4
-rw-r--r--MAINTAINERS1
-rw-r--r--arch/x86/kernel/devicetree.c2
-rw-r--r--drivers/gpu/drm/rcar-du/Kconfig6
-rw-r--r--drivers/gpu/drm/rcar-du/Makefile10
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_drv.c42
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_drv.h5
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_encoder.c175
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_encoder.h12
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_kms.c14
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c93
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h24
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c238
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h64
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_of.c322
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_of.h20
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7790.dts76
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7791.dts50
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7793.dts50
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7795.dts50
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7796.dts50
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_lvds.c540
-rw-r--r--drivers/of/Kconfig1
-rw-r--r--drivers/of/overlay.c134
-rw-r--r--drivers/of/resolver.c6
-rw-r--r--drivers/of/unittest-data/Makefile28
-rw-r--r--drivers/of/unittest-data/overlay.dts101
-rw-r--r--drivers/of/unittest-data/overlay_0.dts14
-rw-r--r--drivers/of/unittest-data/overlay_1.dts14
-rw-r--r--drivers/of/unittest-data/overlay_10.dts27
-rw-r--r--drivers/of/unittest-data/overlay_11.dts28
-rw-r--r--drivers/of/unittest-data/overlay_12.dts14
-rw-r--r--drivers/of/unittest-data/overlay_13.dts14
-rw-r--r--drivers/of/unittest-data/overlay_15.dts30
-rw-r--r--drivers/of/unittest-data/overlay_2.dts9
-rw-r--r--drivers/of/unittest-data/overlay_3.dts9
-rw-r--r--drivers/of/unittest-data/overlay_4.dts18
-rw-r--r--drivers/of/unittest-data/overlay_5.dts9
-rw-r--r--drivers/of/unittest-data/overlay_6.dts10
-rw-r--r--drivers/of/unittest-data/overlay_7.dts10
-rw-r--r--drivers/of/unittest-data/overlay_8.dts10
-rw-r--r--drivers/of/unittest-data/overlay_9.dts10
-rw-r--r--drivers/of/unittest-data/overlay_bad_phandle.dts23
-rw-r--r--drivers/of/unittest-data/overlay_bad_symbol.dts25
-rw-r--r--drivers/of/unittest-data/tests-overlay.dtsi217
-rw-r--r--drivers/of/unittest.c300
-rw-r--r--include/linux/of.h6
49 files changed, 1884 insertions, 1124 deletions
diff --git a/Documentation/devicetree/bindings/display/bridge/renesas,lvds.txt b/Documentation/devicetree/bindings/display/bridge/renesas,lvds.txt
new file mode 100644
index 000000000000..4f0ab3ed3b6f
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/bridge/renesas,lvds.txt
@@ -0,0 +1,58 @@
1Renesas R-Car LVDS Encoder
2==========================
3
4These DT bindings describe the LVDS encoder embedded in the Renesas R-Car
5Gen2, R-Car Gen3 and RZ/G SoCs.
6
7Required properties:
8
9- compatible : Shall contain one of
10 - "renesas,r8a7743-lvds" for R8A7743 (RZ/G1M) compatible LVDS encoders
11 - "renesas,r8a7790-lvds" for R8A7790 (R-Car H2) compatible LVDS encoders
12 - "renesas,r8a7791-lvds" for R8A7791 (R-Car M2-W) compatible LVDS encoders
13 - "renesas,r8a7793-lvds" for R8A7793 (R-Car M2-N) compatible LVDS encoders
14 - "renesas,r8a7795-lvds" for R8A7795 (R-Car H3) compatible LVDS encoders
15 - "renesas,r8a7796-lvds" for R8A7796 (R-Car M3-W) compatible LVDS encoders
16 - "renesas,r8a77970-lvds" for R8A77970 (R-Car V3M) compatible LVDS encoders
17 - "renesas,r8a77995-lvds" for R8A77995 (R-Car D3) compatible LVDS encoders
18
19- reg: Base address and length for the memory-mapped registers
20- clocks: A phandle + clock-specifier pair for the functional clock
21- resets: A phandle + reset specifier for the module reset
22
23Required nodes:
24
25The LVDS encoder has two video ports. Their connections are modelled using the
26OF graph bindings specified in Documentation/devicetree/bindings/graph.txt.
27
28- Video port 0 corresponds to the parallel RGB input
29- Video port 1 corresponds to the LVDS output
30
31Each port shall have a single endpoint.
32
33
34Example:
35
36 lvds0: lvds@feb90000 {
37 compatible = "renesas,r8a7790-lvds";
38 reg = <0 0xfeb90000 0 0x1c>;
39 clocks = <&cpg CPG_MOD 726>;
40 resets = <&cpg 726>;
41
42 ports {
43 #address-cells = <1>;
44 #size-cells = <0>;
45
46 port@0 {
47 reg = <0>;
48 lvds0_in: endpoint {
49 remote-endpoint = <&du_out_lvds0>;
50 };
51 };
52 port@1 {
53 reg = <1>;
54 lvds0_out: endpoint {
55 };
56 };
57 };
58 };
diff --git a/Documentation/devicetree/bindings/display/renesas,du.txt b/Documentation/devicetree/bindings/display/renesas,du.txt
index cd48aba3bc8c..c9cd17f99702 100644
--- a/Documentation/devicetree/bindings/display/renesas,du.txt
+++ b/Documentation/devicetree/bindings/display/renesas,du.txt
@@ -13,13 +13,10 @@ Required Properties:
13 - "renesas,du-r8a7794" for R8A7794 (R-Car E2) compatible DU 13 - "renesas,du-r8a7794" for R8A7794 (R-Car E2) compatible DU
14 - "renesas,du-r8a7795" for R8A7795 (R-Car H3) compatible DU 14 - "renesas,du-r8a7795" for R8A7795 (R-Car H3) compatible DU
15 - "renesas,du-r8a7796" for R8A7796 (R-Car M3-W) compatible DU 15 - "renesas,du-r8a7796" for R8A7796 (R-Car M3-W) compatible DU
16 - "renesas,du-r8a77970" for R8A77970 (R-Car V3M) compatible DU
17 - "renesas,du-r8a77995" for R8A77995 (R-Car D3) compatible DU
16 18
17 - reg: A list of base address and length of each memory resource, one for 19 - reg: the memory-mapped I/O registers base address and length
18 each entry in the reg-names property.
19 - reg-names: Name of the memory resources. The DU requires one memory
20 resource for the DU core (named "du") and one memory resource for each
21 LVDS encoder (named "lvds.x" with "x" being the LVDS controller numerical
22 index).
23 20
24 - interrupt-parent: phandle of the parent interrupt controller. 21 - interrupt-parent: phandle of the parent interrupt controller.
25 - interrupts: Interrupt specifiers for the DU interrupts. 22 - interrupts: Interrupt specifiers for the DU interrupts.
@@ -29,14 +26,13 @@ Required Properties:
29 - clock-names: Name of the clocks. This property is model-dependent. 26 - clock-names: Name of the clocks. This property is model-dependent.
30 - R8A7779 uses a single functional clock. The clock doesn't need to be 27 - R8A7779 uses a single functional clock. The clock doesn't need to be
31 named. 28 named.
32 - All other DU instances use one functional clock per channel and one 29 - All other DU instances use one functional clock per channel The
33 clock per LVDS encoder (if available). The functional clocks must be 30 functional clocks must be named "du.x" with "x" being the channel
34 named "du.x" with "x" being the channel numerical index. The LVDS clocks 31 numerical index.
35 must be named "lvds.x" with "x" being the LVDS encoder numerical index. 32 - In addition to the functional clocks, all DU versions also support
36 - In addition to the functional and encoder clocks, all DU versions also 33 externally supplied pixel clocks. Those clocks are optional. When
37 support externally supplied pixel clocks. Those clocks are optional. 34 supplied they must be named "dclkin.x" with "x" being the input clock
38 When supplied they must be named "dclkin.x" with "x" being the input 35 numerical index.
39 clock numerical index.
40 36
41 - vsps: A list of phandle and channel index tuples to the VSPs that handle 37 - vsps: A list of phandle and channel index tuples to the VSPs that handle
42 the memory interfaces for the DU channels. The phandle identifies the VSP 38 the memory interfaces for the DU channels. The phandle identifies the VSP
@@ -63,15 +59,15 @@ corresponding to each DU output.
63 R8A7794 (R-Car E2) DPAD 0 DPAD 1 - - 59 R8A7794 (R-Car E2) DPAD 0 DPAD 1 - -
64 R8A7795 (R-Car H3) DPAD 0 HDMI 0 HDMI 1 LVDS 0 60 R8A7795 (R-Car H3) DPAD 0 HDMI 0 HDMI 1 LVDS 0
65 R8A7796 (R-Car M3-W) DPAD 0 HDMI 0 LVDS 0 - 61 R8A7796 (R-Car M3-W) DPAD 0 HDMI 0 LVDS 0 -
62 R8A77970 (R-Car V3M) DPAD 0 LVDS 0 - -
63 R8A77995 (R-Car D3) DPAD 0 LVDS 0 LVDS 1 -
66 64
67 65
68Example: R8A7795 (R-Car H3) ES2.0 DU 66Example: R8A7795 (R-Car H3) ES2.0 DU
69 67
70 du: display@feb00000 { 68 du: display@feb00000 {
71 compatible = "renesas,du-r8a7795"; 69 compatible = "renesas,du-r8a7795";
72 reg = <0 0xfeb00000 0 0x80000>, 70 reg = <0 0xfeb00000 0 0x80000>;
73 <0 0xfeb90000 0 0x14>;
74 reg-names = "du", "lvds.0";
75 interrupts = <GIC_SPI 256 IRQ_TYPE_LEVEL_HIGH>, 71 interrupts = <GIC_SPI 256 IRQ_TYPE_LEVEL_HIGH>,
76 <GIC_SPI 268 IRQ_TYPE_LEVEL_HIGH>, 72 <GIC_SPI 268 IRQ_TYPE_LEVEL_HIGH>,
77 <GIC_SPI 269 IRQ_TYPE_LEVEL_HIGH>, 73 <GIC_SPI 269 IRQ_TYPE_LEVEL_HIGH>,
@@ -79,9 +75,8 @@ Example: R8A7795 (R-Car H3) ES2.0 DU
79 clocks = <&cpg CPG_MOD 724>, 75 clocks = <&cpg CPG_MOD 724>,
80 <&cpg CPG_MOD 723>, 76 <&cpg CPG_MOD 723>,
81 <&cpg CPG_MOD 722>, 77 <&cpg CPG_MOD 722>,
82 <&cpg CPG_MOD 721>, 78 <&cpg CPG_MOD 721>;
83 <&cpg CPG_MOD 727>; 79 clock-names = "du.0", "du.1", "du.2", "du.3";
84 clock-names = "du.0", "du.1", "du.2", "du.3", "lvds.0";
85 vsps = <&vspd0 0>, <&vspd1 0>, <&vspd2 0>, <&vspd0 1>; 80 vsps = <&vspd0 0>, <&vspd1 0>, <&vspd2 0>, <&vspd0 1>;
86 81
87 ports { 82 ports {
diff --git a/Documentation/devicetree/overlay-notes.txt b/Documentation/devicetree/overlay-notes.txt
index c4aa0adf13ec..5175a24d387e 100644
--- a/Documentation/devicetree/overlay-notes.txt
+++ b/Documentation/devicetree/overlay-notes.txt
@@ -87,8 +87,8 @@ Overlay in-kernel API
87 87
88The API is quite easy to use. 88The API is quite easy to use.
89 89
901. Call of_overlay_apply() to create and apply an overlay changeset. The return 901. Call of_overlay_fdt_apply() to create and apply an overlay changeset. The
91value is an error or a cookie identifying this overlay. 91return value is an error or a cookie identifying this overlay.
92 92
932. Call of_overlay_remove() to remove and cleanup the overlay changeset 932. Call of_overlay_remove() to remove and cleanup the overlay changeset
94previously created via the call to of_overlay_apply(). Removal of an overlay 94previously created via the call to of_overlay_apply(). Removal of an overlay
diff --git a/MAINTAINERS b/MAINTAINERS
index 2afba909724c..13c8ec11135a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4744,6 +4744,7 @@ F: drivers/gpu/drm/rcar-du/
4744F: drivers/gpu/drm/shmobile/ 4744F: drivers/gpu/drm/shmobile/
4745F: include/linux/platform_data/shmob_drm.h 4745F: include/linux/platform_data/shmob_drm.h
4746F: Documentation/devicetree/bindings/display/bridge/renesas,dw-hdmi.txt 4746F: Documentation/devicetree/bindings/display/bridge/renesas,dw-hdmi.txt
4747F: Documentation/devicetree/bindings/display/bridge/renesas,lvds.txt
4747F: Documentation/devicetree/bindings/display/renesas,du.txt 4748F: Documentation/devicetree/bindings/display/renesas,du.txt
4748 4749
4749DRM DRIVERS FOR ROCKCHIP 4750DRM DRIVERS FOR ROCKCHIP
diff --git a/arch/x86/kernel/devicetree.c b/arch/x86/kernel/devicetree.c
index 25de5f6ca997..45416826f6ee 100644
--- a/arch/x86/kernel/devicetree.c
+++ b/arch/x86/kernel/devicetree.c
@@ -259,7 +259,7 @@ static void __init dtb_apic_setup(void)
259 dtb_ioapic_setup(); 259 dtb_ioapic_setup();
260} 260}
261 261
262#ifdef CONFIG_OF_FLATTREE 262#ifdef CONFIG_OF_EARLY_FLATTREE
263static void __init x86_flattree_get_config(void) 263static void __init x86_flattree_get_config(void)
264{ 264{
265 u32 size, map_len; 265 u32 size, map_len;
diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig
index 5d0b4b7119af..edde8d4b87a3 100644
--- a/drivers/gpu/drm/rcar-du/Kconfig
+++ b/drivers/gpu/drm/rcar-du/Kconfig
@@ -19,9 +19,11 @@ config DRM_RCAR_DW_HDMI
19 Enable support for R-Car Gen3 internal HDMI encoder. 19 Enable support for R-Car Gen3 internal HDMI encoder.
20 20
21config DRM_RCAR_LVDS 21config DRM_RCAR_LVDS
22 bool "R-Car DU LVDS Encoder Support" 22 tristate "R-Car DU LVDS Encoder Support"
23 depends on DRM_RCAR_DU 23 depends on DRM && DRM_BRIDGE && OF
24 select DRM_PANEL 24 select DRM_PANEL
25 select OF_FLATTREE
26 select OF_OVERLAY
25 help 27 help
26 Enable support for the R-Car Display Unit embedded LVDS encoders. 28 Enable support for the R-Car Display Unit embedded LVDS encoders.
27 29
diff --git a/drivers/gpu/drm/rcar-du/Makefile b/drivers/gpu/drm/rcar-du/Makefile
index 0cf5c11030e8..3e58ed93d5b1 100644
--- a/drivers/gpu/drm/rcar-du/Makefile
+++ b/drivers/gpu/drm/rcar-du/Makefile
@@ -4,12 +4,16 @@ rcar-du-drm-y := rcar_du_crtc.o \
4 rcar_du_encoder.o \ 4 rcar_du_encoder.o \
5 rcar_du_group.o \ 5 rcar_du_group.o \
6 rcar_du_kms.o \ 6 rcar_du_kms.o \
7 rcar_du_lvdscon.o \
8 rcar_du_plane.o 7 rcar_du_plane.o
9 8
10rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_lvdsenc.o 9rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_of.o \
11 10 rcar_du_of_lvds_r8a7790.dtb.o \
11 rcar_du_of_lvds_r8a7791.dtb.o \
12 rcar_du_of_lvds_r8a7793.dtb.o \
13 rcar_du_of_lvds_r8a7795.dtb.o \
14 rcar_du_of_lvds_r8a7796.dtb.o
12rcar-du-drm-$(CONFIG_DRM_RCAR_VSP) += rcar_du_vsp.o 15rcar-du-drm-$(CONFIG_DRM_RCAR_VSP) += rcar_du_vsp.o
13 16
14obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o 17obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o
15obj-$(CONFIG_DRM_RCAR_DW_HDMI) += rcar_dw_hdmi.o 18obj-$(CONFIG_DRM_RCAR_DW_HDMI) += rcar_dw_hdmi.o
19obj-$(CONFIG_DRM_RCAR_LVDS) += rcar_lvds.o
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
index 6e02c762a557..3917d839c04c 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c
@@ -29,6 +29,7 @@
29 29
30#include "rcar_du_drv.h" 30#include "rcar_du_drv.h"
31#include "rcar_du_kms.h" 31#include "rcar_du_kms.h"
32#include "rcar_du_of.h"
32#include "rcar_du_regs.h" 33#include "rcar_du_regs.h"
33 34
34/* ----------------------------------------------------------------------------- 35/* -----------------------------------------------------------------------------
@@ -74,7 +75,6 @@ static const struct rcar_du_device_info rzg1_du_r8a7745_info = {
74 .port = 1, 75 .port = 1,
75 }, 76 },
76 }, 77 },
77 .num_lvds = 0,
78}; 78};
79 79
80static const struct rcar_du_device_info rcar_du_r8a7779_info = { 80static const struct rcar_du_device_info rcar_du_r8a7779_info = {
@@ -95,14 +95,13 @@ static const struct rcar_du_device_info rcar_du_r8a7779_info = {
95 .port = 1, 95 .port = 1,
96 }, 96 },
97 }, 97 },
98 .num_lvds = 0,
99}; 98};
100 99
101static const struct rcar_du_device_info rcar_du_r8a7790_info = { 100static const struct rcar_du_device_info rcar_du_r8a7790_info = {
102 .gen = 2, 101 .gen = 2,
103 .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK 102 .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
104 | RCAR_DU_FEATURE_EXT_CTRL_REGS, 103 | RCAR_DU_FEATURE_EXT_CTRL_REGS,
105 .quirks = RCAR_DU_QUIRK_ALIGN_128B | RCAR_DU_QUIRK_LVDS_LANES, 104 .quirks = RCAR_DU_QUIRK_ALIGN_128B,
106 .num_crtcs = 3, 105 .num_crtcs = 3,
107 .routes = { 106 .routes = {
108 /* 107 /*
@@ -164,7 +163,6 @@ static const struct rcar_du_device_info rcar_du_r8a7792_info = {
164 .port = 1, 163 .port = 1,
165 }, 164 },
166 }, 165 },
167 .num_lvds = 0,
168}; 166};
169 167
170static const struct rcar_du_device_info rcar_du_r8a7794_info = { 168static const struct rcar_du_device_info rcar_du_r8a7794_info = {
@@ -186,7 +184,6 @@ static const struct rcar_du_device_info rcar_du_r8a7794_info = {
186 .port = 1, 184 .port = 1,
187 }, 185 },
188 }, 186 },
189 .num_lvds = 0,
190}; 187};
191 188
192static const struct rcar_du_device_info rcar_du_r8a7795_info = { 189static const struct rcar_du_device_info rcar_du_r8a7795_info = {
@@ -249,6 +246,26 @@ static const struct rcar_du_device_info rcar_du_r8a7796_info = {
249 .dpll_ch = BIT(1), 246 .dpll_ch = BIT(1),
250}; 247};
251 248
249static const struct rcar_du_device_info rcar_du_r8a77970_info = {
250 .gen = 3,
251 .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK
252 | RCAR_DU_FEATURE_EXT_CTRL_REGS
253 | RCAR_DU_FEATURE_VSP1_SOURCE,
254 .num_crtcs = 1,
255 .routes = {
256 /* R8A77970 has one RGB output and one LVDS output. */
257 [RCAR_DU_OUTPUT_DPAD0] = {
258 .possible_crtcs = BIT(0),
259 .port = 0,
260 },
261 [RCAR_DU_OUTPUT_LVDS0] = {
262 .possible_crtcs = BIT(0),
263 .port = 1,
264 },
265 },
266 .num_lvds = 1,
267};
268
252static const struct of_device_id rcar_du_of_table[] = { 269static const struct of_device_id rcar_du_of_table[] = {
253 { .compatible = "renesas,du-r8a7743", .data = &rzg1_du_r8a7743_info }, 270 { .compatible = "renesas,du-r8a7743", .data = &rzg1_du_r8a7743_info },
254 { .compatible = "renesas,du-r8a7745", .data = &rzg1_du_r8a7745_info }, 271 { .compatible = "renesas,du-r8a7745", .data = &rzg1_du_r8a7745_info },
@@ -260,6 +277,7 @@ static const struct of_device_id rcar_du_of_table[] = {
260 { .compatible = "renesas,du-r8a7794", .data = &rcar_du_r8a7794_info }, 277 { .compatible = "renesas,du-r8a7794", .data = &rcar_du_r8a7794_info },
261 { .compatible = "renesas,du-r8a7795", .data = &rcar_du_r8a7795_info }, 278 { .compatible = "renesas,du-r8a7795", .data = &rcar_du_r8a7795_info },
262 { .compatible = "renesas,du-r8a7796", .data = &rcar_du_r8a7796_info }, 279 { .compatible = "renesas,du-r8a7796", .data = &rcar_du_r8a7796_info },
280 { .compatible = "renesas,du-r8a77970", .data = &rcar_du_r8a77970_info },
263 { } 281 { }
264}; 282};
265 283
@@ -434,7 +452,19 @@ static struct platform_driver rcar_du_platform_driver = {
434 }, 452 },
435}; 453};
436 454
437module_platform_driver(rcar_du_platform_driver); 455static int __init rcar_du_init(void)
456{
457 rcar_du_of_init(rcar_du_of_table);
458
459 return platform_driver_register(&rcar_du_platform_driver);
460}
461module_init(rcar_du_init);
462
463static void __exit rcar_du_exit(void)
464{
465 platform_driver_unregister(&rcar_du_platform_driver);
466}
467module_exit(rcar_du_exit);
438 468
439MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); 469MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
440MODULE_DESCRIPTION("Renesas R-Car Display Unit DRM Driver"); 470MODULE_DESCRIPTION("Renesas R-Car Display Unit DRM Driver");
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h
index f400fde65a0c..5c7ec15818c7 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h
@@ -26,14 +26,12 @@ struct device;
26struct drm_device; 26struct drm_device;
27struct drm_fbdev_cma; 27struct drm_fbdev_cma;
28struct rcar_du_device; 28struct rcar_du_device;
29struct rcar_du_lvdsenc;
30 29
31#define RCAR_DU_FEATURE_CRTC_IRQ_CLOCK (1 << 0) /* Per-CRTC IRQ and clock */ 30#define RCAR_DU_FEATURE_CRTC_IRQ_CLOCK (1 << 0) /* Per-CRTC IRQ and clock */
32#define RCAR_DU_FEATURE_EXT_CTRL_REGS (1 << 1) /* Has extended control registers */ 31#define RCAR_DU_FEATURE_EXT_CTRL_REGS (1 << 1) /* Has extended control registers */
33#define RCAR_DU_FEATURE_VSP1_SOURCE (1 << 2) /* Has inputs from VSP1 */ 32#define RCAR_DU_FEATURE_VSP1_SOURCE (1 << 2) /* Has inputs from VSP1 */
34 33
35#define RCAR_DU_QUIRK_ALIGN_128B (1 << 0) /* Align pitches to 128 bytes */ 34#define RCAR_DU_QUIRK_ALIGN_128B (1 << 0) /* Align pitches to 128 bytes */
36#define RCAR_DU_QUIRK_LVDS_LANES (1 << 1) /* LVDS lanes 1 and 3 inverted */
37 35
38/* 36/*
39 * struct rcar_du_output_routing - Output routing specification 37 * struct rcar_du_output_routing - Output routing specification
@@ -70,7 +68,6 @@ struct rcar_du_device_info {
70 68
71#define RCAR_DU_MAX_CRTCS 4 69#define RCAR_DU_MAX_CRTCS 4
72#define RCAR_DU_MAX_GROUPS DIV_ROUND_UP(RCAR_DU_MAX_CRTCS, 2) 70#define RCAR_DU_MAX_GROUPS DIV_ROUND_UP(RCAR_DU_MAX_CRTCS, 2)
73#define RCAR_DU_MAX_LVDS 2
74#define RCAR_DU_MAX_VSPS 4 71#define RCAR_DU_MAX_VSPS 4
75 72
76struct rcar_du_device { 73struct rcar_du_device {
@@ -96,8 +93,6 @@ struct rcar_du_device {
96 93
97 unsigned int dpad0_source; 94 unsigned int dpad0_source;
98 unsigned int vspd1_sink; 95 unsigned int vspd1_sink;
99
100 struct rcar_du_lvdsenc *lvds[RCAR_DU_MAX_LVDS];
101}; 96};
102 97
103static inline bool rcar_du_has(struct rcar_du_device *rcdu, 98static inline bool rcar_du_has(struct rcar_du_device *rcdu,
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
index ba8d2804c1d1..f9c933d3bae6 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
@@ -21,134 +21,22 @@
21#include "rcar_du_drv.h" 21#include "rcar_du_drv.h"
22#include "rcar_du_encoder.h" 22#include "rcar_du_encoder.h"
23#include "rcar_du_kms.h" 23#include "rcar_du_kms.h"
24#include "rcar_du_lvdscon.h"
25#include "rcar_du_lvdsenc.h"
26 24
27/* ----------------------------------------------------------------------------- 25/* -----------------------------------------------------------------------------
28 * Encoder 26 * Encoder
29 */ 27 */
30 28
31static void rcar_du_encoder_disable(struct drm_encoder *encoder)
32{
33 struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
34
35 if (renc->connector && renc->connector->panel) {
36 drm_panel_disable(renc->connector->panel);
37 drm_panel_unprepare(renc->connector->panel);
38 }
39
40 if (renc->lvds)
41 rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, false);
42}
43
44static void rcar_du_encoder_enable(struct drm_encoder *encoder)
45{
46 struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
47
48 if (renc->lvds)
49 rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, true);
50
51 if (renc->connector && renc->connector->panel) {
52 drm_panel_prepare(renc->connector->panel);
53 drm_panel_enable(renc->connector->panel);
54 }
55}
56
57static int rcar_du_encoder_atomic_check(struct drm_encoder *encoder,
58 struct drm_crtc_state *crtc_state,
59 struct drm_connector_state *conn_state)
60{
61 struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
62 struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
63 const struct drm_display_mode *mode = &crtc_state->mode;
64 struct drm_connector *connector = conn_state->connector;
65 struct drm_device *dev = encoder->dev;
66
67 /*
68 * Only panel-related encoder types require validation here, everything
69 * else is handled by the bridge drivers.
70 */
71 if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
72 const struct drm_display_mode *panel_mode;
73
74 if (list_empty(&connector->modes)) {
75 dev_dbg(dev->dev, "encoder: empty modes list\n");
76 return -EINVAL;
77 }
78
79 panel_mode = list_first_entry(&connector->modes,
80 struct drm_display_mode, head);
81
82 /* We're not allowed to modify the resolution. */
83 if (mode->hdisplay != panel_mode->hdisplay ||
84 mode->vdisplay != panel_mode->vdisplay)
85 return -EINVAL;
86
87 /*
88 * The flat panel mode is fixed, just copy it to the adjusted
89 * mode.
90 */
91 drm_mode_copy(adjusted_mode, panel_mode);
92 }
93
94 if (renc->lvds)
95 rcar_du_lvdsenc_atomic_check(renc->lvds, adjusted_mode);
96
97 return 0;
98}
99
100static void rcar_du_encoder_mode_set(struct drm_encoder *encoder, 29static void rcar_du_encoder_mode_set(struct drm_encoder *encoder,
101 struct drm_crtc_state *crtc_state, 30 struct drm_crtc_state *crtc_state,
102 struct drm_connector_state *conn_state) 31 struct drm_connector_state *conn_state)
103{ 32{
104 struct rcar_du_encoder *renc = to_rcar_encoder(encoder); 33 struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
105 struct drm_display_info *info = &conn_state->connector->display_info;
106 enum rcar_lvds_mode mode;
107 34
108 rcar_du_crtc_route_output(crtc_state->crtc, renc->output); 35 rcar_du_crtc_route_output(crtc_state->crtc, renc->output);
109
110 if (!renc->lvds) {
111 /*
112 * The DU driver creates connectors only for the outputs of the
113 * internal LVDS encoders.
114 */
115 renc->connector = NULL;
116 return;
117 }
118
119 renc->connector = to_rcar_connector(conn_state->connector);
120
121 if (!info->num_bus_formats || !info->bus_formats) {
122 dev_err(encoder->dev->dev, "no LVDS bus format reported\n");
123 return;
124 }
125
126 switch (info->bus_formats[0]) {
127 case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
128 case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
129 mode = RCAR_LVDS_MODE_JEIDA;
130 break;
131 case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
132 mode = RCAR_LVDS_MODE_VESA;
133 break;
134 default:
135 dev_err(encoder->dev->dev,
136 "unsupported LVDS bus format 0x%04x\n",
137 info->bus_formats[0]);
138 return;
139 }
140
141 if (info->bus_flags & DRM_BUS_FLAG_DATA_LSB_TO_MSB)
142 mode |= RCAR_LVDS_MODE_MIRROR;
143
144 rcar_du_lvdsenc_set_mode(renc->lvds, mode);
145} 36}
146 37
147static const struct drm_encoder_helper_funcs encoder_helper_funcs = { 38static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
148 .atomic_mode_set = rcar_du_encoder_mode_set, 39 .atomic_mode_set = rcar_du_encoder_mode_set,
149 .disable = rcar_du_encoder_disable,
150 .enable = rcar_du_encoder_enable,
151 .atomic_check = rcar_du_encoder_atomic_check,
152}; 40};
153 41
154static const struct drm_encoder_funcs encoder_funcs = { 42static const struct drm_encoder_funcs encoder_funcs = {
@@ -172,33 +60,14 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
172 renc->output = output; 60 renc->output = output;
173 encoder = rcar_encoder_to_drm_encoder(renc); 61 encoder = rcar_encoder_to_drm_encoder(renc);
174 62
175 switch (output) { 63 dev_dbg(rcdu->dev, "initializing encoder %pOF for output %u\n",
176 case RCAR_DU_OUTPUT_LVDS0: 64 enc_node, output);
177 renc->lvds = rcdu->lvds[0];
178 break;
179 65
180 case RCAR_DU_OUTPUT_LVDS1: 66 /* Locate the DRM bridge from the encoder DT node. */
181 renc->lvds = rcdu->lvds[1]; 67 bridge = of_drm_find_bridge(enc_node);
182 break; 68 if (!bridge) {
183 69 ret = -EPROBE_DEFER;
184 default: 70 goto done;
185 break;
186 }
187
188 if (enc_node) {
189 dev_dbg(rcdu->dev, "initializing encoder %pOF for output %u\n",
190 enc_node, output);
191
192 /* Locate the DRM bridge from the encoder DT node. */
193 bridge = of_drm_find_bridge(enc_node);
194 if (!bridge) {
195 ret = -EPROBE_DEFER;
196 goto done;
197 }
198 } else {
199 dev_dbg(rcdu->dev,
200 "initializing internal encoder for output %u\n",
201 output);
202 } 71 }
203 72
204 ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs, 73 ret = drm_encoder_init(rcdu->ddev, encoder, &encoder_funcs,
@@ -208,28 +77,14 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu,
208 77
209 drm_encoder_helper_add(encoder, &encoder_helper_funcs); 78 drm_encoder_helper_add(encoder, &encoder_helper_funcs);
210 79
211 if (bridge) { 80 /*
212 /* 81 * Attach the bridge to the encoder. The bridge will create the
213 * Attach the bridge to the encoder. The bridge will create the 82 * connector.
214 * connector. 83 */
215 */ 84 ret = drm_bridge_attach(encoder, bridge, NULL);
216 ret = drm_bridge_attach(encoder, bridge, NULL); 85 if (ret) {
217 if (ret) { 86 drm_encoder_cleanup(encoder);
218 drm_encoder_cleanup(encoder); 87 return ret;
219 return ret;
220 }
221 } else {
222 /* There's no bridge, create the connector manually. */
223 switch (output) {
224 case RCAR_DU_OUTPUT_LVDS0:
225 case RCAR_DU_OUTPUT_LVDS1:
226 ret = rcar_du_lvds_connector_init(rcdu, renc, con_node);
227 break;
228
229 default:
230 ret = -EINVAL;
231 break;
232 }
233 } 88 }
234 89
235done: 90done:
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
index 5422fa4df272..2d2abcacd169 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
@@ -19,13 +19,10 @@
19 19
20struct drm_panel; 20struct drm_panel;
21struct rcar_du_device; 21struct rcar_du_device;
22struct rcar_du_lvdsenc;
23 22
24struct rcar_du_encoder { 23struct rcar_du_encoder {
25 struct drm_encoder base; 24 struct drm_encoder base;
26 enum rcar_du_output output; 25 enum rcar_du_output output;
27 struct rcar_du_connector *connector;
28 struct rcar_du_lvdsenc *lvds;
29}; 26};
30 27
31#define to_rcar_encoder(e) \ 28#define to_rcar_encoder(e) \
@@ -33,15 +30,6 @@ struct rcar_du_encoder {
33 30
34#define rcar_encoder_to_drm_encoder(e) (&(e)->base) 31#define rcar_encoder_to_drm_encoder(e) (&(e)->base)
35 32
36struct rcar_du_connector {
37 struct drm_connector connector;
38 struct rcar_du_encoder *encoder;
39 struct drm_panel *panel;
40};
41
42#define to_rcar_connector(c) \
43 container_of(c, struct rcar_du_connector, connector)
44
45int rcar_du_encoder_init(struct rcar_du_device *rcdu, 33int rcar_du_encoder_init(struct rcar_du_device *rcdu,
46 enum rcar_du_output output, 34 enum rcar_du_output output,
47 struct device_node *enc_node, 35 struct device_node *enc_node,
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
index 566d1a948c8f..0329b354bfa0 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
@@ -27,7 +27,6 @@
27#include "rcar_du_drv.h" 27#include "rcar_du_drv.h"
28#include "rcar_du_encoder.h" 28#include "rcar_du_encoder.h"
29#include "rcar_du_kms.h" 29#include "rcar_du_kms.h"
30#include "rcar_du_lvdsenc.h"
31#include "rcar_du_regs.h" 30#include "rcar_du_regs.h"
32#include "rcar_du_vsp.h" 31#include "rcar_du_vsp.h"
33 32
@@ -341,11 +340,10 @@ static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu,
341 of_node_put(entity_ep_node); 340 of_node_put(entity_ep_node);
342 341
343 if (!encoder) { 342 if (!encoder) {
344 /* 343 dev_warn(rcdu->dev,
345 * If no encoder has been found the entity must be the 344 "no encoder found for endpoint %pOF, skipping\n",
346 * connector. 345 ep->local_node);
347 */ 346 return -ENODEV;
348 connector = entity;
349 } 347 }
350 348
351 ret = rcar_du_encoder_init(rcdu, output, encoder, connector); 349 ret = rcar_du_encoder_init(rcdu, output, encoder, connector);
@@ -595,10 +593,6 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
595 } 593 }
596 594
597 /* Initialize the encoders. */ 595 /* Initialize the encoders. */
598 ret = rcar_du_lvdsenc_init(rcdu);
599 if (ret < 0)
600 return ret;
601
602 ret = rcar_du_encoders_init(rcdu); 596 ret = rcar_du_encoders_init(rcdu);
603 if (ret < 0) 597 if (ret < 0)
604 return ret; 598 return ret;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
deleted file mode 100644
index e96f2df0c305..000000000000
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
+++ /dev/null
@@ -1,93 +0,0 @@
1/*
2 * rcar_du_lvdscon.c -- R-Car Display Unit LVDS Connector
3 *
4 * Copyright (C) 2013-2014 Renesas Electronics Corporation
5 *
6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#include <drm/drmP.h>
15#include <drm/drm_atomic_helper.h>
16#include <drm/drm_crtc.h>
17#include <drm/drm_crtc_helper.h>
18#include <drm/drm_panel.h>
19
20#include <video/display_timing.h>
21#include <video/of_display_timing.h>
22#include <video/videomode.h>
23
24#include "rcar_du_drv.h"
25#include "rcar_du_encoder.h"
26#include "rcar_du_kms.h"
27#include "rcar_du_lvdscon.h"
28
29static int rcar_du_lvds_connector_get_modes(struct drm_connector *connector)
30{
31 struct rcar_du_connector *rcon = to_rcar_connector(connector);
32
33 return drm_panel_get_modes(rcon->panel);
34}
35
36static const struct drm_connector_helper_funcs connector_helper_funcs = {
37 .get_modes = rcar_du_lvds_connector_get_modes,
38};
39
40static void rcar_du_lvds_connector_destroy(struct drm_connector *connector)
41{
42 struct rcar_du_connector *rcon = to_rcar_connector(connector);
43
44 drm_panel_detach(rcon->panel);
45 drm_connector_cleanup(connector);
46}
47
48static const struct drm_connector_funcs connector_funcs = {
49 .reset = drm_atomic_helper_connector_reset,
50 .fill_modes = drm_helper_probe_single_connector_modes,
51 .destroy = rcar_du_lvds_connector_destroy,
52 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
53 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
54};
55
56int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
57 struct rcar_du_encoder *renc,
58 const struct device_node *np)
59{
60 struct drm_encoder *encoder = rcar_encoder_to_drm_encoder(renc);
61 struct rcar_du_connector *rcon;
62 struct drm_connector *connector;
63 int ret;
64
65 rcon = devm_kzalloc(rcdu->dev, sizeof(*rcon), GFP_KERNEL);
66 if (rcon == NULL)
67 return -ENOMEM;
68
69 connector = &rcon->connector;
70
71 rcon->panel = of_drm_find_panel(np);
72 if (!rcon->panel)
73 return -EPROBE_DEFER;
74
75 ret = drm_connector_init(rcdu->ddev, connector, &connector_funcs,
76 DRM_MODE_CONNECTOR_LVDS);
77 if (ret < 0)
78 return ret;
79
80 drm_connector_helper_add(connector, &connector_helper_funcs);
81
82 ret = drm_mode_connector_attach_encoder(connector, encoder);
83 if (ret < 0)
84 return ret;
85
86 ret = drm_panel_attach(rcon->panel, connector);
87 if (ret < 0)
88 return ret;
89
90 rcon->encoder = renc;
91
92 return 0;
93}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h
deleted file mode 100644
index 639071dd235c..000000000000
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.h
+++ /dev/null
@@ -1,24 +0,0 @@
1/*
2 * rcar_du_lvdscon.h -- R-Car Display Unit LVDS Connector
3 *
4 * Copyright (C) 2013-2014 Renesas Electronics Corporation
5 *
6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#ifndef __RCAR_DU_LVDSCON_H__
15#define __RCAR_DU_LVDSCON_H__
16
17struct rcar_du_device;
18struct rcar_du_encoder;
19
20int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
21 struct rcar_du_encoder *renc,
22 const struct device_node *np);
23
24#endif /* __RCAR_DU_LVDSCON_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c
deleted file mode 100644
index 4defa8123eb2..000000000000
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c
+++ /dev/null
@@ -1,238 +0,0 @@
1/*
2 * rcar_du_lvdsenc.c -- R-Car Display Unit LVDS Encoder
3 *
4 * Copyright (C) 2013-2014 Renesas Electronics Corporation
5 *
6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#include <linux/clk.h>
15#include <linux/delay.h>
16#include <linux/io.h>
17#include <linux/platform_device.h>
18#include <linux/slab.h>
19
20#include "rcar_du_drv.h"
21#include "rcar_du_encoder.h"
22#include "rcar_du_lvdsenc.h"
23#include "rcar_lvds_regs.h"
24
25struct rcar_du_lvdsenc {
26 struct rcar_du_device *dev;
27
28 unsigned int index;
29 void __iomem *mmio;
30 struct clk *clock;
31 bool enabled;
32
33 enum rcar_lvds_input input;
34 enum rcar_lvds_mode mode;
35};
36
37static void rcar_lvds_write(struct rcar_du_lvdsenc *lvds, u32 reg, u32 data)
38{
39 iowrite32(data, lvds->mmio + reg);
40}
41
42static u32 rcar_lvds_lvdpllcr_gen2(unsigned int freq)
43{
44 if (freq < 39000)
45 return LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_38M;
46 else if (freq < 61000)
47 return LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_60M;
48 else if (freq < 121000)
49 return LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_121M;
50 else
51 return LVDPLLCR_PLLDLYCNT_150M;
52}
53
54static u32 rcar_lvds_lvdpllcr_gen3(unsigned int freq)
55{
56 if (freq < 42000)
57 return LVDPLLCR_PLLDIVCNT_42M;
58 else if (freq < 85000)
59 return LVDPLLCR_PLLDIVCNT_85M;
60 else if (freq < 128000)
61 return LVDPLLCR_PLLDIVCNT_128M;
62 else
63 return LVDPLLCR_PLLDIVCNT_148M;
64}
65
66static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds,
67 struct rcar_du_crtc *rcrtc)
68{
69 const struct drm_display_mode *mode = &rcrtc->crtc.mode;
70 u32 lvdpllcr;
71 u32 lvdhcr;
72 u32 lvdcr0;
73 int ret;
74
75 if (lvds->enabled)
76 return 0;
77
78 ret = clk_prepare_enable(lvds->clock);
79 if (ret < 0)
80 return ret;
81
82 /*
83 * Hardcode the channels and control signals routing for now.
84 *
85 * HSYNC -> CTRL0
86 * VSYNC -> CTRL1
87 * DISP -> CTRL2
88 * 0 -> CTRL3
89 */
90 rcar_lvds_write(lvds, LVDCTRCR, LVDCTRCR_CTR3SEL_ZERO |
91 LVDCTRCR_CTR2SEL_DISP | LVDCTRCR_CTR1SEL_VSYNC |
92 LVDCTRCR_CTR0SEL_HSYNC);
93
94 if (rcar_du_needs(lvds->dev, RCAR_DU_QUIRK_LVDS_LANES))
95 lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 3)
96 | LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 1);
97 else
98 lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 1)
99 | LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 3);
100
101 rcar_lvds_write(lvds, LVDCHCR, lvdhcr);
102
103 /* PLL clock configuration. */
104 if (lvds->dev->info->gen < 3)
105 lvdpllcr = rcar_lvds_lvdpllcr_gen2(mode->clock);
106 else
107 lvdpllcr = rcar_lvds_lvdpllcr_gen3(mode->clock);
108 rcar_lvds_write(lvds, LVDPLLCR, lvdpllcr);
109
110 /* Set the LVDS mode and select the input. */
111 lvdcr0 = lvds->mode << LVDCR0_LVMD_SHIFT;
112 if (rcrtc->index == 2)
113 lvdcr0 |= LVDCR0_DUSEL;
114 rcar_lvds_write(lvds, LVDCR0, lvdcr0);
115
116 /* Turn all the channels on. */
117 rcar_lvds_write(lvds, LVDCR1,
118 LVDCR1_CHSTBY(3) | LVDCR1_CHSTBY(2) |
119 LVDCR1_CHSTBY(1) | LVDCR1_CHSTBY(0) | LVDCR1_CLKSTBY);
120
121 if (lvds->dev->info->gen < 3) {
122 /* Enable LVDS operation and turn the bias circuitry on. */
123 lvdcr0 |= LVDCR0_BEN | LVDCR0_LVEN;
124 rcar_lvds_write(lvds, LVDCR0, lvdcr0);
125 }
126
127 /* Turn the PLL on. */
128 lvdcr0 |= LVDCR0_PLLON;
129 rcar_lvds_write(lvds, LVDCR0, lvdcr0);
130
131 if (lvds->dev->info->gen > 2) {
132 /* Set LVDS normal mode. */
133 lvdcr0 |= LVDCR0_PWD;
134 rcar_lvds_write(lvds, LVDCR0, lvdcr0);
135 }
136
137 /* Wait for the startup delay. */
138 usleep_range(100, 150);
139
140 /* Turn the output on. */
141 lvdcr0 |= LVDCR0_LVRES;
142 rcar_lvds_write(lvds, LVDCR0, lvdcr0);
143
144 lvds->enabled = true;
145
146 return 0;
147}
148
149static void rcar_du_lvdsenc_stop(struct rcar_du_lvdsenc *lvds)
150{
151 if (!lvds->enabled)
152 return;
153
154 rcar_lvds_write(lvds, LVDCR0, 0);
155 rcar_lvds_write(lvds, LVDCR1, 0);
156
157 clk_disable_unprepare(lvds->clock);
158
159 lvds->enabled = false;
160}
161
162int rcar_du_lvdsenc_enable(struct rcar_du_lvdsenc *lvds, struct drm_crtc *crtc,
163 bool enable)
164{
165 if (!enable) {
166 rcar_du_lvdsenc_stop(lvds);
167 return 0;
168 } else if (crtc) {
169 struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
170 return rcar_du_lvdsenc_start(lvds, rcrtc);
171 } else
172 return -EINVAL;
173}
174
175void rcar_du_lvdsenc_atomic_check(struct rcar_du_lvdsenc *lvds,
176 struct drm_display_mode *mode)
177{
178 /*
179 * The internal LVDS encoder has a restricted clock frequency operating
180 * range (31MHz to 148.5MHz). Clamp the clock accordingly.
181 */
182 mode->clock = clamp(mode->clock, 31000, 148500);
183}
184
185void rcar_du_lvdsenc_set_mode(struct rcar_du_lvdsenc *lvds,
186 enum rcar_lvds_mode mode)
187{
188 lvds->mode = mode;
189}
190
191static int rcar_du_lvdsenc_get_resources(struct rcar_du_lvdsenc *lvds,
192 struct platform_device *pdev)
193{
194 struct resource *mem;
195 char name[7];
196
197 sprintf(name, "lvds.%u", lvds->index);
198
199 mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
200 lvds->mmio = devm_ioremap_resource(&pdev->dev, mem);
201 if (IS_ERR(lvds->mmio))
202 return PTR_ERR(lvds->mmio);
203
204 lvds->clock = devm_clk_get(&pdev->dev, name);
205 if (IS_ERR(lvds->clock)) {
206 dev_err(&pdev->dev, "failed to get clock for %s\n", name);
207 return PTR_ERR(lvds->clock);
208 }
209
210 return 0;
211}
212
213int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu)
214{
215 struct platform_device *pdev = to_platform_device(rcdu->dev);
216 struct rcar_du_lvdsenc *lvds;
217 unsigned int i;
218 int ret;
219
220 for (i = 0; i < rcdu->info->num_lvds; ++i) {
221 lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL);
222 if (lvds == NULL)
223 return -ENOMEM;
224
225 lvds->dev = rcdu;
226 lvds->index = i;
227 lvds->input = i ? RCAR_LVDS_INPUT_DU1 : RCAR_LVDS_INPUT_DU0;
228 lvds->enabled = false;
229
230 ret = rcar_du_lvdsenc_get_resources(lvds, pdev);
231 if (ret < 0)
232 return ret;
233
234 rcdu->lvds[i] = lvds;
235 }
236
237 return 0;
238}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h
deleted file mode 100644
index 7218ac89333e..000000000000
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h
+++ /dev/null
@@ -1,64 +0,0 @@
1/*
2 * rcar_du_lvdsenc.h -- R-Car Display Unit LVDS Encoder
3 *
4 * Copyright (C) 2013-2014 Renesas Electronics Corporation
5 *
6 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#ifndef __RCAR_DU_LVDSENC_H__
15#define __RCAR_DU_LVDSENC_H__
16
17#include <linux/io.h>
18#include <linux/module.h>
19
20struct rcar_drm_crtc;
21struct rcar_du_lvdsenc;
22
23enum rcar_lvds_input {
24 RCAR_LVDS_INPUT_DU0,
25 RCAR_LVDS_INPUT_DU1,
26 RCAR_LVDS_INPUT_DU2,
27};
28
29/* Keep in sync with the LVDCR0.LVMD hardware register values. */
30enum rcar_lvds_mode {
31 RCAR_LVDS_MODE_JEIDA = 0,
32 RCAR_LVDS_MODE_MIRROR = 1,
33 RCAR_LVDS_MODE_VESA = 4,
34};
35
36#if IS_ENABLED(CONFIG_DRM_RCAR_LVDS)
37int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu);
38void rcar_du_lvdsenc_set_mode(struct rcar_du_lvdsenc *lvds,
39 enum rcar_lvds_mode mode);
40int rcar_du_lvdsenc_enable(struct rcar_du_lvdsenc *lvds,
41 struct drm_crtc *crtc, bool enable);
42void rcar_du_lvdsenc_atomic_check(struct rcar_du_lvdsenc *lvds,
43 struct drm_display_mode *mode);
44#else
45static inline int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu)
46{
47 return 0;
48}
49static inline void rcar_du_lvdsenc_set_mode(struct rcar_du_lvdsenc *lvds,
50 enum rcar_lvds_mode mode)
51{
52}
53static inline int rcar_du_lvdsenc_enable(struct rcar_du_lvdsenc *lvds,
54 struct drm_crtc *crtc, bool enable)
55{
56 return 0;
57}
58static inline void rcar_du_lvdsenc_atomic_check(struct rcar_du_lvdsenc *lvds,
59 struct drm_display_mode *mode)
60{
61}
62#endif
63
64#endif /* __RCAR_DU_LVDSENC_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_of.c b/drivers/gpu/drm/rcar-du/rcar_du_of.c
new file mode 100644
index 000000000000..68a0b82cb17e
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_of.c
@@ -0,0 +1,322 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * rcar_du_of.c - Legacy DT bindings compatibility
4 *
5 * Copyright (C) 2018 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
6 *
7 * Based on work from Jyri Sarha <jsarha@ti.com>
8 * Copyright (C) 2015 Texas Instruments
9 */
10
11#include <linux/init.h>
12#include <linux/kernel.h>
13#include <linux/of.h>
14#include <linux/of_address.h>
15#include <linux/of_fdt.h>
16#include <linux/of_graph.h>
17#include <linux/slab.h>
18
19#include "rcar_du_crtc.h"
20#include "rcar_du_drv.h"
21
22/* -----------------------------------------------------------------------------
23 * Generic Overlay Handling
24 */
25
26struct rcar_du_of_overlay {
27 const char *compatible;
28 void *begin;
29 void *end;
30};
31
32#define RCAR_DU_OF_DTB(type, soc) \
33 extern char __dtb_rcar_du_of_##type##_##soc##_begin[]; \
34 extern char __dtb_rcar_du_of_##type##_##soc##_end[]
35
36#define RCAR_DU_OF_OVERLAY(type, soc) \
37 { \
38 .compatible = "renesas,du-" #soc, \
39 .begin = __dtb_rcar_du_of_##type##_##soc##_begin, \
40 .end = __dtb_rcar_du_of_##type##_##soc##_end, \
41 }
42
43static int __init rcar_du_of_apply_overlay(const struct rcar_du_of_overlay *dtbs,
44 const char *compatible)
45{
46 const struct rcar_du_of_overlay *dtb = NULL;
47 unsigned int i;
48 int ovcs_id;
49
50 for (i = 0; dtbs[i].compatible; ++i) {
51 if (!strcmp(dtbs[i].compatible, compatible)) {
52 dtb = &dtbs[i];
53 break;
54 }
55 }
56
57 if (!dtb)
58 return -ENODEV;
59
60 ovcs_id = 0;
61 return of_overlay_fdt_apply(dtb->begin, dtb->end - dtb->begin,
62 &ovcs_id);
63}
64
65static int __init rcar_du_of_add_property(struct of_changeset *ocs,
66 struct device_node *np,
67 const char *name, const void *value,
68 int length)
69{
70 struct property *prop;
71 int ret = -ENOMEM;
72
73 prop = kzalloc(sizeof(*prop), GFP_KERNEL);
74 if (!prop)
75 return -ENOMEM;
76
77 prop->name = kstrdup(name, GFP_KERNEL);
78 if (!prop->name)
79 goto out_err;
80
81 prop->value = kmemdup(value, length, GFP_KERNEL);
82 if (!prop->value)
83 goto out_err;
84
85 of_property_set_flag(prop, OF_DYNAMIC);
86
87 prop->length = length;
88
89 ret = of_changeset_add_property(ocs, np, prop);
90 if (!ret)
91 return 0;
92
93out_err:
94 kfree(prop->value);
95 kfree(prop->name);
96 kfree(prop);
97 return ret;
98}
99
100/* -----------------------------------------------------------------------------
101 * LVDS Overlays
102 */
103
104RCAR_DU_OF_DTB(lvds, r8a7790);
105RCAR_DU_OF_DTB(lvds, r8a7791);
106RCAR_DU_OF_DTB(lvds, r8a7793);
107RCAR_DU_OF_DTB(lvds, r8a7795);
108RCAR_DU_OF_DTB(lvds, r8a7796);
109
110static const struct rcar_du_of_overlay rcar_du_lvds_overlays[] __initconst = {
111 RCAR_DU_OF_OVERLAY(lvds, r8a7790),
112 RCAR_DU_OF_OVERLAY(lvds, r8a7791),
113 RCAR_DU_OF_OVERLAY(lvds, r8a7793),
114 RCAR_DU_OF_OVERLAY(lvds, r8a7795),
115 RCAR_DU_OF_OVERLAY(lvds, r8a7796),
116 { /* Sentinel */ },
117};
118
119static struct of_changeset rcar_du_lvds_changeset;
120
121static void __init rcar_du_of_lvds_patch_one(struct device_node *lvds,
122 const struct of_phandle_args *clk,
123 struct device_node *local,
124 struct device_node *remote)
125{
126 unsigned int psize;
127 unsigned int i;
128 __be32 value[4];
129 int ret;
130
131 /*
132 * Set the LVDS clocks property. This can't be performed by the overlay
133 * as the structure of the clock specifier has changed over time, and we
134 * don't know at compile time which binding version the system we will
135 * run on uses.
136 */
137 if (clk->args_count >= ARRAY_SIZE(value) - 1)
138 return;
139
140 of_changeset_init(&rcar_du_lvds_changeset);
141
142 value[0] = cpu_to_be32(clk->np->phandle);
143 for (i = 0; i < clk->args_count; ++i)
144 value[i + 1] = cpu_to_be32(clk->args[i]);
145
146 psize = (clk->args_count + 1) * 4;
147 ret = rcar_du_of_add_property(&rcar_du_lvds_changeset, lvds,
148 "clocks", value, psize);
149 if (ret < 0)
150 goto done;
151
152 /*
153 * Insert the node in the OF graph: patch the LVDS ports remote-endpoint
154 * properties to point to the endpoints of the sibling nodes in the
155 * graph. This can't be performed by the overlay: on the input side the
156 * overlay would contain a phandle for the DU LVDS output port that
157 * would clash with the system DT, and on the output side the connection
158 * is board-specific.
159 */
160 value[0] = cpu_to_be32(local->phandle);
161 value[1] = cpu_to_be32(remote->phandle);
162
163 for (i = 0; i < 2; ++i) {
164 struct device_node *endpoint;
165
166 endpoint = of_graph_get_endpoint_by_regs(lvds, i, 0);
167 if (!endpoint) {
168 ret = -EINVAL;
169 goto done;
170 }
171
172 ret = rcar_du_of_add_property(&rcar_du_lvds_changeset,
173 endpoint, "remote-endpoint",
174 &value[i], sizeof(value[i]));
175 of_node_put(endpoint);
176 if (ret < 0)
177 goto done;
178 }
179
180 ret = of_changeset_apply(&rcar_du_lvds_changeset);
181
182done:
183 if (ret < 0)
184 of_changeset_destroy(&rcar_du_lvds_changeset);
185}
186
187struct lvds_of_data {
188 struct resource res;
189 struct of_phandle_args clkspec;
190 struct device_node *local;
191 struct device_node *remote;
192};
193
194static void __init rcar_du_of_lvds_patch(const struct of_device_id *of_ids)
195{
196 const struct rcar_du_device_info *info;
197 const struct of_device_id *match;
198 struct lvds_of_data lvds_data[2] = { };
199 struct device_node *lvds_node;
200 struct device_node *soc_node;
201 struct device_node *du_node;
202 char compatible[22];
203 const char *soc_name;
204 unsigned int i;
205 int ret;
206
207 /* Get the DU node and exit if not present or disabled. */
208 du_node = of_find_matching_node_and_match(NULL, of_ids, &match);
209 if (!du_node || !of_device_is_available(du_node)) {
210 of_node_put(du_node);
211 return;
212 }
213
214 info = match->data;
215 soc_node = of_get_parent(du_node);
216
217 if (WARN_ON(info->num_lvds > ARRAY_SIZE(lvds_data)))
218 goto done;
219
220 /*
221 * Skip if the LVDS nodes already exists.
222 *
223 * The nodes are searched based on the compatible string, which we
224 * construct from the SoC name found in the DU compatible string. As a
225 * match has been found we know the compatible string matches the
226 * expected format and can thus skip some of the string manipulation
227 * normal safety checks.
228 */
229 soc_name = strchr(match->compatible, '-') + 1;
230 sprintf(compatible, "renesas,%s-lvds", soc_name);
231 lvds_node = of_find_compatible_node(NULL, NULL, compatible);
232 if (lvds_node) {
233 of_node_put(lvds_node);
234 return;
235 }
236
237 /*
238 * Parse the DU node and store the register specifier, the clock
239 * specifier and the local and remote endpoint of the LVDS link for
240 * later use.
241 */
242 for (i = 0; i < info->num_lvds; ++i) {
243 struct lvds_of_data *lvds = &lvds_data[i];
244 unsigned int port;
245 char name[7];
246 int index;
247
248 sprintf(name, "lvds.%u", i);
249 index = of_property_match_string(du_node, "clock-names", name);
250 if (index < 0)
251 continue;
252
253 ret = of_parse_phandle_with_args(du_node, "clocks",
254 "#clock-cells", index,
255 &lvds->clkspec);
256 if (ret < 0)
257 continue;
258
259 port = info->routes[RCAR_DU_OUTPUT_LVDS0 + i].port;
260
261 lvds->local = of_graph_get_endpoint_by_regs(du_node, port, 0);
262 if (!lvds->local)
263 continue;
264
265 lvds->remote = of_graph_get_remote_endpoint(lvds->local);
266 if (!lvds->remote)
267 continue;
268
269 index = of_property_match_string(du_node, "reg-names", name);
270 if (index < 0)
271 continue;
272
273 of_address_to_resource(du_node, index, &lvds->res);
274 }
275
276 /* Parse and apply the overlay. This will resolve phandles. */
277 ret = rcar_du_of_apply_overlay(rcar_du_lvds_overlays,
278 match->compatible);
279 if (ret < 0)
280 goto done;
281
282 /* Patch the newly created LVDS encoder nodes. */
283 for_each_child_of_node(soc_node, lvds_node) {
284 struct resource res;
285
286 if (!of_device_is_compatible(lvds_node, compatible))
287 continue;
288
289 /* Locate the lvds_data entry based on the resource start. */
290 ret = of_address_to_resource(lvds_node, 0, &res);
291 if (ret < 0)
292 continue;
293
294 for (i = 0; i < ARRAY_SIZE(lvds_data); ++i) {
295 if (lvds_data[i].res.start == res.start)
296 break;
297 }
298
299 if (i == ARRAY_SIZE(lvds_data))
300 continue;
301
302 /* Patch the LVDS encoder. */
303 rcar_du_of_lvds_patch_one(lvds_node, &lvds_data[i].clkspec,
304 lvds_data[i].local,
305 lvds_data[i].remote);
306 }
307
308done:
309 for (i = 0; i < info->num_lvds; ++i) {
310 of_node_put(lvds_data[i].clkspec.np);
311 of_node_put(lvds_data[i].local);
312 of_node_put(lvds_data[i].remote);
313 }
314
315 of_node_put(soc_node);
316 of_node_put(du_node);
317}
318
319void __init rcar_du_of_init(const struct of_device_id *of_ids)
320{
321 rcar_du_of_lvds_patch(of_ids);
322}
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_of.h b/drivers/gpu/drm/rcar-du/rcar_du_of.h
new file mode 100644
index 000000000000..c2e65a727e91
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_of.h
@@ -0,0 +1,20 @@
1/* SPDX-License-Identifier: GPL-2.0 */
2/*
3 * rcar_du_of.h - Legacy DT bindings compatibility
4 *
5 * Copyright (C) 2018 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
6 */
7#ifndef __RCAR_DU_OF_H__
8#define __RCAR_DU_OF_H__
9
10#include <linux/init.h>
11
12struct of_device_id;
13
14#ifdef CONFIG_DRM_RCAR_LVDS
15void __init rcar_du_of_init(const struct of_device_id *of_ids);
16#else
17static inline void rcar_du_of_init(const struct of_device_id *of_ids) { }
18#endif /* CONFIG_DRM_RCAR_LVDS */
19
20#endif /* __RCAR_DU_OF_H__ */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7790.dts b/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7790.dts
new file mode 100644
index 000000000000..579753e04f3b
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7790.dts
@@ -0,0 +1,76 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * rcar_du_of_lvds_r8a7790.dts - Legacy LVDS DT bindings conversion for R8A7790
4 *
5 * Copyright (C) 2018 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
6 */
7
8/dts-v1/;
9/plugin/;
10/ {
11 fragment@0 {
12 target-path = "/";
13 __overlay__ {
14 #address-cells = <2>;
15 #size-cells = <2>;
16
17 lvds@feb90000 {
18 compatible = "renesas,r8a7790-lvds";
19 reg = <0 0xfeb90000 0 0x1c>;
20
21 ports {
22 #address-cells = <1>;
23 #size-cells = <0>;
24
25 port@0 {
26 reg = <0>;
27 lvds0_input: endpoint {
28 };
29 };
30 port@1 {
31 reg = <1>;
32 lvds0_out: endpoint {
33 };
34 };
35 };
36 };
37
38 lvds@feb94000 {
39 compatible = "renesas,r8a7790-lvds";
40 reg = <0 0xfeb94000 0 0x1c>;
41
42 ports {
43 #address-cells = <1>;
44 #size-cells = <0>;
45
46 port@0 {
47 reg = <0>;
48 lvds1_input: endpoint {
49 };
50 };
51 port@1 {
52 reg = <1>;
53 lvds1_out: endpoint {
54 };
55 };
56 };
57 };
58 };
59 };
60
61 fragment@1 {
62 target-path = "/display@feb00000/ports";
63 __overlay__ {
64 port@1 {
65 endpoint {
66 remote-endpoint = <&lvds0_input>;
67 };
68 };
69 port@2 {
70 endpoint {
71 remote-endpoint = <&lvds1_input>;
72 };
73 };
74 };
75 };
76};
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7791.dts b/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7791.dts
new file mode 100644
index 000000000000..cb9da1f3942b
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7791.dts
@@ -0,0 +1,50 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * rcar_du_of_lvds_r8a7791.dts - Legacy LVDS DT bindings conversion for R8A7791
4 *
5 * Copyright (C) 2018 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
6 */
7
8/dts-v1/;
9/plugin/;
10/ {
11 fragment@0 {
12 target-path = "/";
13 __overlay__ {
14 #address-cells = <2>;
15 #size-cells = <2>;
16
17 lvds@feb90000 {
18 compatible = "renesas,r8a7791-lvds";
19 reg = <0 0xfeb90000 0 0x1c>;
20
21 ports {
22 #address-cells = <1>;
23 #size-cells = <0>;
24
25 port@0 {
26 reg = <0>;
27 lvds0_input: endpoint {
28 };
29 };
30 port@1 {
31 reg = <1>;
32 lvds0_out: endpoint {
33 };
34 };
35 };
36 };
37 };
38 };
39
40 fragment@1 {
41 target-path = "/display@feb00000/ports";
42 __overlay__ {
43 port@1 {
44 endpoint {
45 remote-endpoint = <&lvds0_input>;
46 };
47 };
48 };
49 };
50};
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7793.dts b/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7793.dts
new file mode 100644
index 000000000000..e7b8804dc3c1
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7793.dts
@@ -0,0 +1,50 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * rcar_du_of_lvds_r8a7793.dts - Legacy LVDS DT bindings conversion for R8A7793
4 *
5 * Copyright (C) 2018 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
6 */
7
8/dts-v1/;
9/plugin/;
10/ {
11 fragment@0 {
12 target-path = "/";
13 __overlay__ {
14 #address-cells = <2>;
15 #size-cells = <2>;
16
17 lvds@feb90000 {
18 compatible = "renesas,r8a7793-lvds";
19 reg = <0 0xfeb90000 0 0x1c>;
20
21 ports {
22 #address-cells = <1>;
23 #size-cells = <0>;
24
25 port@0 {
26 reg = <0>;
27 lvds0_input: endpoint {
28 };
29 };
30 port@1 {
31 reg = <1>;
32 lvds0_out: endpoint {
33 };
34 };
35 };
36 };
37 };
38 };
39
40 fragment@1 {
41 target-path = "/display@feb00000/ports";
42 __overlay__ {
43 port@1 {
44 endpoint {
45 remote-endpoint = <&lvds0_input>;
46 };
47 };
48 };
49 };
50};
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7795.dts b/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7795.dts
new file mode 100644
index 000000000000..a1327443e6fa
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7795.dts
@@ -0,0 +1,50 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * rcar_du_of_lvds_r8a7795.dts - Legacy LVDS DT bindings conversion for R8A7795
4 *
5 * Copyright (C) 2018 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
6 */
7
8/dts-v1/;
9/plugin/;
10/ {
11 fragment@0 {
12 target-path = "/soc";
13 __overlay__ {
14 #address-cells = <2>;
15 #size-cells = <2>;
16
17 lvds@feb90000 {
18 compatible = "renesas,r8a7795-lvds";
19 reg = <0 0xfeb90000 0 0x14>;
20
21 ports {
22 #address-cells = <1>;
23 #size-cells = <0>;
24
25 port@0 {
26 reg = <0>;
27 lvds0_input: endpoint {
28 };
29 };
30 port@1 {
31 reg = <1>;
32 lvds0_out: endpoint {
33 };
34 };
35 };
36 };
37 };
38 };
39
40 fragment@1 {
41 target-path = "/soc/display@feb00000/ports";
42 __overlay__ {
43 port@3 {
44 endpoint {
45 remote-endpoint = <&lvds0_input>;
46 };
47 };
48 };
49 };
50};
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7796.dts b/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7796.dts
new file mode 100644
index 000000000000..b23d6466c415
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_du_of_lvds_r8a7796.dts
@@ -0,0 +1,50 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * rcar_du_of_lvds_r8a7796.dts - Legacy LVDS DT bindings conversion for R8A7796
4 *
5 * Copyright (C) 2018 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
6 */
7
8/dts-v1/;
9/plugin/;
10/ {
11 fragment@0 {
12 target-path = "/soc";
13 __overlay__ {
14 #address-cells = <2>;
15 #size-cells = <2>;
16
17 lvds@feb90000 {
18 compatible = "renesas,r8a7796-lvds";
19 reg = <0 0xfeb90000 0 0x14>;
20
21 ports {
22 #address-cells = <1>;
23 #size-cells = <0>;
24
25 port@0 {
26 reg = <0>;
27 lvds0_input: endpoint {
28 };
29 };
30 port@1 {
31 reg = <1>;
32 lvds0_out: endpoint {
33 };
34 };
35 };
36 };
37 };
38 };
39
40 fragment@1 {
41 target-path = "/soc/display@feb00000/ports";
42 __overlay__ {
43 port@3 {
44 endpoint {
45 remote-endpoint = <&lvds0_input>;
46 };
47 };
48 };
49 };
50};
diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.c b/drivers/gpu/drm/rcar-du/rcar_lvds.c
new file mode 100644
index 000000000000..3d2d3bbd1342
--- /dev/null
+++ b/drivers/gpu/drm/rcar-du/rcar_lvds.c
@@ -0,0 +1,540 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * rcar_lvds.c -- R-Car LVDS Encoder
4 *
5 * Copyright (C) 2013-2018 Renesas Electronics Corporation
6 *
7 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
8 */
9
10#include <linux/clk.h>
11#include <linux/delay.h>
12#include <linux/io.h>
13#include <linux/of.h>
14#include <linux/of_device.h>
15#include <linux/of_graph.h>
16#include <linux/platform_device.h>
17#include <linux/slab.h>
18
19#include <drm/drm_atomic.h>
20#include <drm/drm_atomic_helper.h>
21#include <drm/drm_bridge.h>
22#include <drm/drm_crtc_helper.h>
23#include <drm/drm_panel.h>
24
25#include "rcar_lvds_regs.h"
26
27/* Keep in sync with the LVDCR0.LVMD hardware register values. */
28enum rcar_lvds_mode {
29 RCAR_LVDS_MODE_JEIDA = 0,
30 RCAR_LVDS_MODE_MIRROR = 1,
31 RCAR_LVDS_MODE_VESA = 4,
32};
33
34#define RCAR_LVDS_QUIRK_LANES (1 << 0) /* LVDS lanes 1 and 3 inverted */
35#define RCAR_LVDS_QUIRK_GEN2_PLLCR (1 << 1) /* LVDPLLCR has gen2 layout */
36#define RCAR_LVDS_QUIRK_GEN3_LVEN (1 << 2) /* LVEN bit needs to be set */
37 /* on R8A77970/R8A7799x */
38
39struct rcar_lvds_device_info {
40 unsigned int gen;
41 unsigned int quirks;
42};
43
44struct rcar_lvds {
45 struct device *dev;
46 const struct rcar_lvds_device_info *info;
47
48 struct drm_bridge bridge;
49
50 struct drm_bridge *next_bridge;
51 struct drm_connector connector;
52 struct drm_panel *panel;
53
54 void __iomem *mmio;
55 struct clk *clock;
56 bool enabled;
57
58 struct drm_display_mode display_mode;
59 enum rcar_lvds_mode mode;
60};
61
62#define bridge_to_rcar_lvds(bridge) \
63 container_of(bridge, struct rcar_lvds, bridge)
64
65#define connector_to_rcar_lvds(connector) \
66 container_of(connector, struct rcar_lvds, connector)
67
68static void rcar_lvds_write(struct rcar_lvds *lvds, u32 reg, u32 data)
69{
70 iowrite32(data, lvds->mmio + reg);
71}
72
73/* -----------------------------------------------------------------------------
74 * Connector & Panel
75 */
76
77static int rcar_lvds_connector_get_modes(struct drm_connector *connector)
78{
79 struct rcar_lvds *lvds = connector_to_rcar_lvds(connector);
80
81 return drm_panel_get_modes(lvds->panel);
82}
83
84static int rcar_lvds_connector_atomic_check(struct drm_connector *connector,
85 struct drm_connector_state *state)
86{
87 struct rcar_lvds *lvds = connector_to_rcar_lvds(connector);
88 const struct drm_display_mode *panel_mode;
89 struct drm_crtc_state *crtc_state;
90
91 if (list_empty(&connector->modes)) {
92 dev_dbg(lvds->dev, "connector: empty modes list\n");
93 return -EINVAL;
94 }
95
96 panel_mode = list_first_entry(&connector->modes,
97 struct drm_display_mode, head);
98
99 /* We're not allowed to modify the resolution. */
100 crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);
101 if (IS_ERR(crtc_state))
102 return PTR_ERR(crtc_state);
103
104 if (crtc_state->mode.hdisplay != panel_mode->hdisplay ||
105 crtc_state->mode.vdisplay != panel_mode->vdisplay)
106 return -EINVAL;
107
108 /* The flat panel mode is fixed, just copy it to the adjusted mode. */
109 drm_mode_copy(&crtc_state->adjusted_mode, panel_mode);
110
111 return 0;
112}
113
114static const struct drm_connector_helper_funcs rcar_lvds_conn_helper_funcs = {
115 .get_modes = rcar_lvds_connector_get_modes,
116 .atomic_check = rcar_lvds_connector_atomic_check,
117};
118
119static const struct drm_connector_funcs rcar_lvds_conn_funcs = {
120 .reset = drm_atomic_helper_connector_reset,
121 .fill_modes = drm_helper_probe_single_connector_modes,
122 .destroy = drm_connector_cleanup,
123 .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
124 .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
125};
126
127/* -----------------------------------------------------------------------------
128 * Bridge
129 */
130
131static u32 rcar_lvds_lvdpllcr_gen2(unsigned int freq)
132{
133 if (freq < 39000)
134 return LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_38M;
135 else if (freq < 61000)
136 return LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_60M;
137 else if (freq < 121000)
138 return LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_121M;
139 else
140 return LVDPLLCR_PLLDLYCNT_150M;
141}
142
143static u32 rcar_lvds_lvdpllcr_gen3(unsigned int freq)
144{
145 if (freq < 42000)
146 return LVDPLLCR_PLLDIVCNT_42M;
147 else if (freq < 85000)
148 return LVDPLLCR_PLLDIVCNT_85M;
149 else if (freq < 128000)
150 return LVDPLLCR_PLLDIVCNT_128M;
151 else
152 return LVDPLLCR_PLLDIVCNT_148M;
153}
154
155static void rcar_lvds_enable(struct drm_bridge *bridge)
156{
157 struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
158 const struct drm_display_mode *mode = &lvds->display_mode;
159 /*
160 * FIXME: We should really retrieve the CRTC through the state, but how
161 * do we get a state pointer?
162 */
163 struct drm_crtc *crtc = lvds->bridge.encoder->crtc;
164 u32 lvdpllcr;
165 u32 lvdhcr;
166 u32 lvdcr0;
167 int ret;
168
169 WARN_ON(lvds->enabled);
170
171 ret = clk_prepare_enable(lvds->clock);
172 if (ret < 0)
173 return;
174
175 /*
176 * Hardcode the channels and control signals routing for now.
177 *
178 * HSYNC -> CTRL0
179 * VSYNC -> CTRL1
180 * DISP -> CTRL2
181 * 0 -> CTRL3
182 */
183 rcar_lvds_write(lvds, LVDCTRCR, LVDCTRCR_CTR3SEL_ZERO |
184 LVDCTRCR_CTR2SEL_DISP | LVDCTRCR_CTR1SEL_VSYNC |
185 LVDCTRCR_CTR0SEL_HSYNC);
186
187 if (lvds->info->quirks & RCAR_LVDS_QUIRK_LANES)
188 lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 3)
189 | LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 1);
190 else
191 lvdhcr = LVDCHCR_CHSEL_CH(0, 0) | LVDCHCR_CHSEL_CH(1, 1)
192 | LVDCHCR_CHSEL_CH(2, 2) | LVDCHCR_CHSEL_CH(3, 3);
193
194 rcar_lvds_write(lvds, LVDCHCR, lvdhcr);
195
196 /* PLL clock configuration. */
197 if (lvds->info->quirks & RCAR_LVDS_QUIRK_GEN2_PLLCR)
198 lvdpllcr = rcar_lvds_lvdpllcr_gen2(mode->clock);
199 else
200 lvdpllcr = rcar_lvds_lvdpllcr_gen3(mode->clock);
201 rcar_lvds_write(lvds, LVDPLLCR, lvdpllcr);
202
203 /* Set the LVDS mode and select the input. */
204 lvdcr0 = lvds->mode << LVDCR0_LVMD_SHIFT;
205 if (drm_crtc_index(crtc) == 2)
206 lvdcr0 |= LVDCR0_DUSEL;
207 rcar_lvds_write(lvds, LVDCR0, lvdcr0);
208
209 /* Turn all the channels on. */
210 rcar_lvds_write(lvds, LVDCR1,
211 LVDCR1_CHSTBY(3) | LVDCR1_CHSTBY(2) |
212 LVDCR1_CHSTBY(1) | LVDCR1_CHSTBY(0) | LVDCR1_CLKSTBY);
213
214 if (lvds->info->gen < 3) {
215 /* Enable LVDS operation and turn the bias circuitry on. */
216 lvdcr0 |= LVDCR0_BEN | LVDCR0_LVEN;
217 rcar_lvds_write(lvds, LVDCR0, lvdcr0);
218 }
219
220 /* Turn the PLL on. */
221 lvdcr0 |= LVDCR0_PLLON;
222 rcar_lvds_write(lvds, LVDCR0, lvdcr0);
223
224 if (lvds->info->gen > 2) {
225 /* Set LVDS normal mode. */
226 lvdcr0 |= LVDCR0_PWD;
227 rcar_lvds_write(lvds, LVDCR0, lvdcr0);
228 }
229
230 if (lvds->info->quirks & RCAR_LVDS_QUIRK_GEN3_LVEN) {
231 /* Turn on the LVDS PHY. */
232 lvdcr0 |= LVDCR0_LVEN;
233 rcar_lvds_write(lvds, LVDCR0, lvdcr0);
234 }
235
236 /* Wait for the startup delay. */
237 usleep_range(100, 150);
238
239 /* Turn the output on. */
240 lvdcr0 |= LVDCR0_LVRES;
241 rcar_lvds_write(lvds, LVDCR0, lvdcr0);
242
243 if (lvds->panel) {
244 drm_panel_prepare(lvds->panel);
245 drm_panel_enable(lvds->panel);
246 }
247
248 lvds->enabled = true;
249}
250
251static void rcar_lvds_disable(struct drm_bridge *bridge)
252{
253 struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
254
255 WARN_ON(!lvds->enabled);
256
257 if (lvds->panel) {
258 drm_panel_disable(lvds->panel);
259 drm_panel_unprepare(lvds->panel);
260 }
261
262 rcar_lvds_write(lvds, LVDCR0, 0);
263 rcar_lvds_write(lvds, LVDCR1, 0);
264
265 clk_disable_unprepare(lvds->clock);
266
267 lvds->enabled = false;
268}
269
270static bool rcar_lvds_mode_fixup(struct drm_bridge *bridge,
271 const struct drm_display_mode *mode,
272 struct drm_display_mode *adjusted_mode)
273{
274 /*
275 * The internal LVDS encoder has a restricted clock frequency operating
276 * range (31MHz to 148.5MHz). Clamp the clock accordingly.
277 */
278 adjusted_mode->clock = clamp(adjusted_mode->clock, 31000, 148500);
279
280 return true;
281}
282
283static void rcar_lvds_get_lvds_mode(struct rcar_lvds *lvds)
284{
285 struct drm_display_info *info = &lvds->connector.display_info;
286 enum rcar_lvds_mode mode;
287
288 /*
289 * There is no API yet to retrieve LVDS mode from a bridge, only panels
290 * are supported.
291 */
292 if (!lvds->panel)
293 return;
294
295 if (!info->num_bus_formats || !info->bus_formats) {
296 dev_err(lvds->dev, "no LVDS bus format reported\n");
297 return;
298 }
299
300 switch (info->bus_formats[0]) {
301 case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
302 case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
303 mode = RCAR_LVDS_MODE_JEIDA;
304 break;
305 case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
306 mode = RCAR_LVDS_MODE_VESA;
307 break;
308 default:
309 dev_err(lvds->dev, "unsupported LVDS bus format 0x%04x\n",
310 info->bus_formats[0]);
311 return;
312 }
313
314 if (info->bus_flags & DRM_BUS_FLAG_DATA_LSB_TO_MSB)
315 mode |= RCAR_LVDS_MODE_MIRROR;
316
317 lvds->mode = mode;
318}
319
320static void rcar_lvds_mode_set(struct drm_bridge *bridge,
321 struct drm_display_mode *mode,
322 struct drm_display_mode *adjusted_mode)
323{
324 struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
325
326 WARN_ON(lvds->enabled);
327
328 lvds->display_mode = *adjusted_mode;
329
330 rcar_lvds_get_lvds_mode(lvds);
331}
332
333static int rcar_lvds_attach(struct drm_bridge *bridge)
334{
335 struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
336 struct drm_connector *connector = &lvds->connector;
337 struct drm_encoder *encoder = bridge->encoder;
338 int ret;
339
340 /* If we have a next bridge just attach it. */
341 if (lvds->next_bridge)
342 return drm_bridge_attach(bridge->encoder, lvds->next_bridge,
343 bridge);
344
345 /* Otherwise we have a panel, create a connector. */
346 ret = drm_connector_init(bridge->dev, connector, &rcar_lvds_conn_funcs,
347 DRM_MODE_CONNECTOR_LVDS);
348 if (ret < 0)
349 return ret;
350
351 drm_connector_helper_add(connector, &rcar_lvds_conn_helper_funcs);
352
353 ret = drm_mode_connector_attach_encoder(connector, encoder);
354 if (ret < 0)
355 return ret;
356
357 return drm_panel_attach(lvds->panel, connector);
358}
359
360static void rcar_lvds_detach(struct drm_bridge *bridge)
361{
362 struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge);
363
364 if (lvds->panel)
365 drm_panel_detach(lvds->panel);
366}
367
368static const struct drm_bridge_funcs rcar_lvds_bridge_ops = {
369 .attach = rcar_lvds_attach,
370 .detach = rcar_lvds_detach,
371 .enable = rcar_lvds_enable,
372 .disable = rcar_lvds_disable,
373 .mode_fixup = rcar_lvds_mode_fixup,
374 .mode_set = rcar_lvds_mode_set,
375};
376
377/* -----------------------------------------------------------------------------
378 * Probe & Remove
379 */
380
381static int rcar_lvds_parse_dt(struct rcar_lvds *lvds)
382{
383 struct device_node *local_output = NULL;
384 struct device_node *remote_input = NULL;
385 struct device_node *remote = NULL;
386 struct device_node *node;
387 bool is_bridge = false;
388 int ret = 0;
389
390 local_output = of_graph_get_endpoint_by_regs(lvds->dev->of_node, 1, 0);
391 if (!local_output) {
392 dev_dbg(lvds->dev, "unconnected port@1\n");
393 return -ENODEV;
394 }
395
396 /*
397 * Locate the connected entity and infer its type from the number of
398 * endpoints.
399 */
400 remote = of_graph_get_remote_port_parent(local_output);
401 if (!remote) {
402 dev_dbg(lvds->dev, "unconnected endpoint %pOF\n", local_output);
403 ret = -ENODEV;
404 goto done;
405 }
406
407 if (!of_device_is_available(remote)) {
408 dev_dbg(lvds->dev, "connected entity %pOF is disabled\n",
409 remote);
410 ret = -ENODEV;
411 goto done;
412 }
413
414 remote_input = of_graph_get_remote_endpoint(local_output);
415
416 for_each_endpoint_of_node(remote, node) {
417 if (node != remote_input) {
418 /*
419 * We've found one endpoint other than the input, this
420 * must be a bridge.
421 */
422 is_bridge = true;
423 of_node_put(node);
424 break;
425 }
426 }
427
428 if (is_bridge) {
429 lvds->next_bridge = of_drm_find_bridge(remote);
430 if (!lvds->next_bridge)
431 ret = -EPROBE_DEFER;
432 } else {
433 lvds->panel = of_drm_find_panel(remote);
434 if (!lvds->panel)
435 ret = -EPROBE_DEFER;
436 }
437
438done:
439 of_node_put(local_output);
440 of_node_put(remote_input);
441 of_node_put(remote);
442
443 return ret;
444}
445
446static int rcar_lvds_probe(struct platform_device *pdev)
447{
448 struct rcar_lvds *lvds;
449 struct resource *mem;
450 int ret;
451
452 lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL);
453 if (lvds == NULL)
454 return -ENOMEM;
455
456 platform_set_drvdata(pdev, lvds);
457
458 lvds->dev = &pdev->dev;
459 lvds->info = of_device_get_match_data(&pdev->dev);
460 lvds->enabled = false;
461
462 ret = rcar_lvds_parse_dt(lvds);
463 if (ret < 0)
464 return ret;
465
466 lvds->bridge.driver_private = lvds;
467 lvds->bridge.funcs = &rcar_lvds_bridge_ops;
468 lvds->bridge.of_node = pdev->dev.of_node;
469
470 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
471 lvds->mmio = devm_ioremap_resource(&pdev->dev, mem);
472 if (IS_ERR(lvds->mmio))
473 return PTR_ERR(lvds->mmio);
474
475 lvds->clock = devm_clk_get(&pdev->dev, NULL);
476 if (IS_ERR(lvds->clock)) {
477 dev_err(&pdev->dev, "failed to get clock\n");
478 return PTR_ERR(lvds->clock);
479 }
480
481 drm_bridge_add(&lvds->bridge);
482
483 return 0;
484}
485
486static int rcar_lvds_remove(struct platform_device *pdev)
487{
488 struct rcar_lvds *lvds = platform_get_drvdata(pdev);
489
490 drm_bridge_remove(&lvds->bridge);
491
492 return 0;
493}
494
495static const struct rcar_lvds_device_info rcar_lvds_gen2_info = {
496 .gen = 2,
497 .quirks = RCAR_LVDS_QUIRK_GEN2_PLLCR,
498};
499
500static const struct rcar_lvds_device_info rcar_lvds_r8a7790_info = {
501 .gen = 2,
502 .quirks = RCAR_LVDS_QUIRK_GEN2_PLLCR | RCAR_LVDS_QUIRK_LANES,
503};
504
505static const struct rcar_lvds_device_info rcar_lvds_gen3_info = {
506 .gen = 3,
507};
508
509static const struct rcar_lvds_device_info rcar_lvds_r8a77970_info = {
510 .gen = 3,
511 .quirks = RCAR_LVDS_QUIRK_GEN2_PLLCR | RCAR_LVDS_QUIRK_GEN3_LVEN,
512};
513
514static const struct of_device_id rcar_lvds_of_table[] = {
515 { .compatible = "renesas,r8a7743-lvds", .data = &rcar_lvds_gen2_info },
516 { .compatible = "renesas,r8a7790-lvds", .data = &rcar_lvds_r8a7790_info },
517 { .compatible = "renesas,r8a7791-lvds", .data = &rcar_lvds_gen2_info },
518 { .compatible = "renesas,r8a7793-lvds", .data = &rcar_lvds_gen2_info },
519 { .compatible = "renesas,r8a7795-lvds", .data = &rcar_lvds_gen3_info },
520 { .compatible = "renesas,r8a7796-lvds", .data = &rcar_lvds_gen3_info },
521 { .compatible = "renesas,r8a77970-lvds", .data = &rcar_lvds_r8a77970_info },
522 { }
523};
524
525MODULE_DEVICE_TABLE(of, rcar_lvds_of_table);
526
527static struct platform_driver rcar_lvds_platform_driver = {
528 .probe = rcar_lvds_probe,
529 .remove = rcar_lvds_remove,
530 .driver = {
531 .name = "rcar-lvds",
532 .of_match_table = rcar_lvds_of_table,
533 },
534};
535
536module_platform_driver(rcar_lvds_platform_driver);
537
538MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
539MODULE_DESCRIPTION("Renesas R-Car LVDS Encoder Driver");
540MODULE_LICENSE("GPL");
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index 783e0870bd22..ad3fcad4d75b 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -92,6 +92,7 @@ config OF_RESOLVE
92config OF_OVERLAY 92config OF_OVERLAY
93 bool "Device Tree overlays" 93 bool "Device Tree overlays"
94 select OF_DYNAMIC 94 select OF_DYNAMIC
95 select OF_FLATTREE
95 select OF_RESOLVE 96 select OF_RESOLVE
96 help 97 help
97 Overlays are a method to dynamically modify part of the kernel's 98 Overlays are a method to dynamically modify part of the kernel's
diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c
index 3397d7642958..b930e05d1215 100644
--- a/drivers/of/overlay.c
+++ b/drivers/of/overlay.c
@@ -12,10 +12,12 @@
12#include <linux/module.h> 12#include <linux/module.h>
13#include <linux/of.h> 13#include <linux/of.h>
14#include <linux/of_device.h> 14#include <linux/of_device.h>
15#include <linux/of_fdt.h>
15#include <linux/string.h> 16#include <linux/string.h>
16#include <linux/ctype.h> 17#include <linux/ctype.h>
17#include <linux/errno.h> 18#include <linux/errno.h>
18#include <linux/slab.h> 19#include <linux/slab.h>
20#include <linux/libfdt.h>
19#include <linux/err.h> 21#include <linux/err.h>
20#include <linux/idr.h> 22#include <linux/idr.h>
21 23
@@ -33,7 +35,9 @@ struct fragment {
33 35
34/** 36/**
35 * struct overlay_changeset 37 * struct overlay_changeset
38 * @id: changeset identifier
36 * @ovcs_list: list on which we are located 39 * @ovcs_list: list on which we are located
40 * @fdt: FDT that was unflattened to create @overlay_tree
37 * @overlay_tree: expanded device tree that contains the fragment nodes 41 * @overlay_tree: expanded device tree that contains the fragment nodes
38 * @count: count of fragment structures 42 * @count: count of fragment structures
39 * @fragments: fragment nodes in the overlay expanded device tree 43 * @fragments: fragment nodes in the overlay expanded device tree
@@ -43,6 +47,7 @@ struct fragment {
43struct overlay_changeset { 47struct overlay_changeset {
44 int id; 48 int id;
45 struct list_head ovcs_list; 49 struct list_head ovcs_list;
50 const void *fdt;
46 struct device_node *overlay_tree; 51 struct device_node *overlay_tree;
47 int count; 52 int count;
48 struct fragment *fragments; 53 struct fragment *fragments;
@@ -483,27 +488,38 @@ static int build_changeset(struct overlay_changeset *ovcs)
483 */ 488 */
484static struct device_node *find_target_node(struct device_node *info_node) 489static struct device_node *find_target_node(struct device_node *info_node)
485{ 490{
491 struct device_node *node;
486 const char *path; 492 const char *path;
487 u32 val; 493 u32 val;
488 int ret; 494 int ret;
489 495
490 ret = of_property_read_u32(info_node, "target", &val); 496 ret = of_property_read_u32(info_node, "target", &val);
491 if (!ret) 497 if (!ret) {
492 return of_find_node_by_phandle(val); 498 node = of_find_node_by_phandle(val);
499 if (!node)
500 pr_err("find target, node: %pOF, phandle 0x%x not found\n",
501 info_node, val);
502 return node;
503 }
493 504
494 ret = of_property_read_string(info_node, "target-path", &path); 505 ret = of_property_read_string(info_node, "target-path", &path);
495 if (!ret) 506 if (!ret) {
496 return of_find_node_by_path(path); 507 node = of_find_node_by_path(path);
508 if (!node)
509 pr_err("find target, node: %pOF, path '%s' not found\n",
510 info_node, path);
511 return node;
512 }
497 513
498 pr_err("Failed to find target for node %p (%s)\n", 514 pr_err("find target, node: %pOF, no target property\n", info_node);
499 info_node, info_node->name);
500 515
501 return NULL; 516 return NULL;
502} 517}
503 518
504/** 519/**
505 * init_overlay_changeset() - initialize overlay changeset from overlay tree 520 * init_overlay_changeset() - initialize overlay changeset from overlay tree
506 * @ovcs Overlay changeset to build 521 * @ovcs: Overlay changeset to build
522 * @fdt: the FDT that was unflattened to create @tree
507 * @tree: Contains all the overlay fragments and overlay fixup nodes 523 * @tree: Contains all the overlay fragments and overlay fixup nodes
508 * 524 *
509 * Initialize @ovcs. Populate @ovcs->fragments with node information from 525 * Initialize @ovcs. Populate @ovcs->fragments with node information from
@@ -514,7 +530,7 @@ static struct device_node *find_target_node(struct device_node *info_node)
514 * detected in @tree, or -ENOSPC if idr_alloc() error. 530 * detected in @tree, or -ENOSPC if idr_alloc() error.
515 */ 531 */
516static int init_overlay_changeset(struct overlay_changeset *ovcs, 532static int init_overlay_changeset(struct overlay_changeset *ovcs,
517 struct device_node *tree) 533 const void *fdt, struct device_node *tree)
518{ 534{
519 struct device_node *node, *overlay_node; 535 struct device_node *node, *overlay_node;
520 struct fragment *fragment; 536 struct fragment *fragment;
@@ -535,6 +551,7 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs,
535 pr_debug("%s() tree is not root\n", __func__); 551 pr_debug("%s() tree is not root\n", __func__);
536 552
537 ovcs->overlay_tree = tree; 553 ovcs->overlay_tree = tree;
554 ovcs->fdt = fdt;
538 555
539 INIT_LIST_HEAD(&ovcs->ovcs_list); 556 INIT_LIST_HEAD(&ovcs->ovcs_list);
540 557
@@ -606,6 +623,7 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs,
606 } 623 }
607 624
608 if (!cnt) { 625 if (!cnt) {
626 pr_err("no fragments or symbols in overlay\n");
609 ret = -EINVAL; 627 ret = -EINVAL;
610 goto err_free_fragments; 628 goto err_free_fragments;
611 } 629 }
@@ -642,11 +660,24 @@ static void free_overlay_changeset(struct overlay_changeset *ovcs)
642 } 660 }
643 kfree(ovcs->fragments); 661 kfree(ovcs->fragments);
644 662
663 /*
664 * TODO
665 *
666 * would like to: kfree(ovcs->overlay_tree);
667 * but can not since drivers may have pointers into this data
668 *
669 * would like to: kfree(ovcs->fdt);
670 * but can not since drivers may have pointers into this data
671 */
672
645 kfree(ovcs); 673 kfree(ovcs);
646} 674}
647 675
648/** 676/*
677 * internal documentation
678 *
649 * of_overlay_apply() - Create and apply an overlay changeset 679 * of_overlay_apply() - Create and apply an overlay changeset
680 * @fdt: the FDT that was unflattened to create @tree
650 * @tree: Expanded overlay device tree 681 * @tree: Expanded overlay device tree
651 * @ovcs_id: Pointer to overlay changeset id 682 * @ovcs_id: Pointer to overlay changeset id
652 * 683 *
@@ -685,21 +716,29 @@ static void free_overlay_changeset(struct overlay_changeset *ovcs)
685 * id is returned to *ovcs_id. 716 * id is returned to *ovcs_id.
686 */ 717 */
687 718
688int of_overlay_apply(struct device_node *tree, int *ovcs_id) 719static int of_overlay_apply(const void *fdt, struct device_node *tree,
720 int *ovcs_id)
689{ 721{
690 struct overlay_changeset *ovcs; 722 struct overlay_changeset *ovcs;
691 int ret = 0, ret_revert, ret_tmp; 723 int ret = 0, ret_revert, ret_tmp;
692 724
693 *ovcs_id = 0; 725 /*
726 * As of this point, fdt and tree belong to the overlay changeset.
727 * overlay changeset code is responsible for freeing them.
728 */
694 729
695 if (devicetree_corrupt()) { 730 if (devicetree_corrupt()) {
696 pr_err("devicetree state suspect, refuse to apply overlay\n"); 731 pr_err("devicetree state suspect, refuse to apply overlay\n");
732 kfree(fdt);
733 kfree(tree);
697 ret = -EBUSY; 734 ret = -EBUSY;
698 goto out; 735 goto out;
699 } 736 }
700 737
701 ovcs = kzalloc(sizeof(*ovcs), GFP_KERNEL); 738 ovcs = kzalloc(sizeof(*ovcs), GFP_KERNEL);
702 if (!ovcs) { 739 if (!ovcs) {
740 kfree(fdt);
741 kfree(tree);
703 ret = -ENOMEM; 742 ret = -ENOMEM;
704 goto out; 743 goto out;
705 } 744 }
@@ -709,12 +748,17 @@ int of_overlay_apply(struct device_node *tree, int *ovcs_id)
709 748
710 ret = of_resolve_phandles(tree); 749 ret = of_resolve_phandles(tree);
711 if (ret) 750 if (ret)
712 goto err_free_overlay_changeset; 751 goto err_free_tree;
713 752
714 ret = init_overlay_changeset(ovcs, tree); 753 ret = init_overlay_changeset(ovcs, fdt, tree);
715 if (ret) 754 if (ret)
716 goto err_free_overlay_changeset; 755 goto err_free_tree;
717 756
757 /*
758 * after overlay_notify(), ovcs->overlay_tree related pointers may have
759 * leaked to drivers, so can not kfree() tree, aka ovcs->overlay_tree;
760 * and can not free fdt, aka ovcs->fdt
761 */
718 ret = overlay_notify(ovcs, OF_OVERLAY_PRE_APPLY); 762 ret = overlay_notify(ovcs, OF_OVERLAY_PRE_APPLY);
719 if (ret) { 763 if (ret) {
720 pr_err("overlay changeset pre-apply notify error %d\n", ret); 764 pr_err("overlay changeset pre-apply notify error %d\n", ret);
@@ -754,6 +798,10 @@ int of_overlay_apply(struct device_node *tree, int *ovcs_id)
754 798
755 goto out_unlock; 799 goto out_unlock;
756 800
801err_free_tree:
802 kfree(fdt);
803 kfree(tree);
804
757err_free_overlay_changeset: 805err_free_overlay_changeset:
758 free_overlay_changeset(ovcs); 806 free_overlay_changeset(ovcs);
759 807
@@ -766,7 +814,63 @@ out:
766 814
767 return ret; 815 return ret;
768} 816}
769EXPORT_SYMBOL_GPL(of_overlay_apply); 817
818int of_overlay_fdt_apply(const void *overlay_fdt, u32 overlay_fdt_size,
819 int *ovcs_id)
820{
821 const void *new_fdt;
822 int ret;
823 u32 size;
824 struct device_node *overlay_root;
825
826 *ovcs_id = 0;
827 ret = 0;
828
829 if (overlay_fdt_size < sizeof(struct fdt_header) ||
830 fdt_check_header(overlay_fdt)) {
831 pr_err("Invalid overlay_fdt header\n");
832 return -EINVAL;
833 }
834
835 size = fdt_totalsize(overlay_fdt);
836 if (overlay_fdt_size < size)
837 return -EINVAL;
838
839 /*
840 * Must create permanent copy of FDT because of_fdt_unflatten_tree()
841 * will create pointers to the passed in FDT in the unflattened tree.
842 */
843 new_fdt = kmemdup(overlay_fdt, size, GFP_KERNEL);
844 if (!new_fdt)
845 return -ENOMEM;
846
847 of_fdt_unflatten_tree(new_fdt, NULL, &overlay_root);
848 if (!overlay_root) {
849 pr_err("unable to unflatten overlay_fdt\n");
850 ret = -EINVAL;
851 goto out_free_new_fdt;
852 }
853
854 ret = of_overlay_apply(new_fdt, overlay_root, ovcs_id);
855 if (ret < 0) {
856 /*
857 * new_fdt and overlay_root now belong to the overlay
858 * changeset.
859 * overlay changeset code is responsible for freeing them.
860 */
861 goto out;
862 }
863
864 return 0;
865
866
867out_free_new_fdt:
868 kfree(new_fdt);
869
870out:
871 return ret;
872}
873EXPORT_SYMBOL_GPL(of_overlay_fdt_apply);
770 874
771/* 875/*
772 * Find @np in @tree. 876 * Find @np in @tree.
diff --git a/drivers/of/resolver.c b/drivers/of/resolver.c
index 740d19bde601..b2f645187213 100644
--- a/drivers/of/resolver.c
+++ b/drivers/of/resolver.c
@@ -269,17 +269,11 @@ int of_resolve_phandles(struct device_node *overlay)
269 goto out; 269 goto out;
270 } 270 }
271 271
272#if 0
273 Temporarily disable check so that old style overlay unittests
274 do not fail when of_resolve_phandles() is moved into
275 of_overlay_apply().
276
277 if (!of_node_check_flag(overlay, OF_DETACHED)) { 272 if (!of_node_check_flag(overlay, OF_DETACHED)) {
278 pr_err("overlay not detached\n"); 273 pr_err("overlay not detached\n");
279 err = -EINVAL; 274 err = -EINVAL;
280 goto out; 275 goto out;
281 } 276 }
282#endif
283 277
284 phandle_delta = live_tree_max_phandle() + 1; 278 phandle_delta = live_tree_max_phandle() + 1;
285 adjust_overlay_phandles(overlay, phandle_delta); 279 adjust_overlay_phandles(overlay, phandle_delta);
diff --git a/drivers/of/unittest-data/Makefile b/drivers/of/unittest-data/Makefile
index df697976740a..8fd0ea4b92b0 100644
--- a/drivers/of/unittest-data/Makefile
+++ b/drivers/of/unittest-data/Makefile
@@ -1,8 +1,22 @@
1# SPDX-License-Identifier: GPL-2.0 1# SPDX-License-Identifier: GPL-2.0
2DTC_FLAGS_testcases := -Wno-interrupts_property
3obj-y += testcases.dtb.o 2obj-y += testcases.dtb.o
4 3
5obj-$(CONFIG_OF_OVERLAY) += overlay.dtb.o \ 4obj-$(CONFIG_OF_OVERLAY) += overlay.dtb.o \
5 overlay_0.dtb.o \
6 overlay_1.dtb.o \
7 overlay_2.dtb.o \
8 overlay_3.dtb.o \
9 overlay_4.dtb.o \
10 overlay_5.dtb.o \
11 overlay_6.dtb.o \
12 overlay_7.dtb.o \
13 overlay_8.dtb.o \
14 overlay_9.dtb.o \
15 overlay_10.dtb.o \
16 overlay_11.dtb.o \
17 overlay_12.dtb.o \
18 overlay_13.dtb.o \
19 overlay_15.dtb.o \
6 overlay_bad_phandle.dtb.o \ 20 overlay_bad_phandle.dtb.o \
7 overlay_bad_symbol.dtb.o \ 21 overlay_bad_symbol.dtb.o \
8 overlay_base.dtb.o 22 overlay_base.dtb.o
@@ -10,10 +24,14 @@ obj-$(CONFIG_OF_OVERLAY) += overlay.dtb.o \
10targets += $(foreach suffix, dtb dtb.S, $(patsubst %.dtb.o,%.$(suffix),$(obj-y))) 24targets += $(foreach suffix, dtb dtb.S, $(patsubst %.dtb.o,%.$(suffix),$(obj-y)))
11 25
12# enable creation of __symbols__ node 26# enable creation of __symbols__ node
13DTC_FLAGS_overlay := -@ 27DTC_FLAGS_overlay += -@
14DTC_FLAGS_overlay_bad_phandle := -@ 28DTC_FLAGS_overlay_bad_phandle += -@
15DTC_FLAGS_overlay_bad_symbol := -@ 29DTC_FLAGS_overlay_bad_symbol += -@
16DTC_FLAGS_overlay_base := -@ 30DTC_FLAGS_overlay_base += -@
31DTC_FLAGS_testcases += -@
32
33# suppress warnings about intentional errors
34DTC_FLAGS_testcases += -Wno-interrupts_property
17 35
18.PRECIOUS: \ 36.PRECIOUS: \
19 $(obj)/%.dtb.S \ 37 $(obj)/%.dtb.S \
diff --git a/drivers/of/unittest-data/overlay.dts b/drivers/of/unittest-data/overlay.dts
index ab5e89b5e27e..3bbc59e922fe 100644
--- a/drivers/of/unittest-data/overlay.dts
+++ b/drivers/of/unittest-data/overlay.dts
@@ -2,76 +2,63 @@
2/dts-v1/; 2/dts-v1/;
3/plugin/; 3/plugin/;
4 4
5/ { 5&electric_1 {
6 6
7 fragment@0 { 7 status = "okay";
8 target = <&electric_1>;
9 8
10 __overlay__ { 9 hvac_2: hvac-large-1 {
11 status = "okay"; 10 compatible = "ot,hvac-large";
12 11 heat-range = < 40 75 >;
13 hvac_2: hvac-large-1 { 12 cool-range = < 65 80 >;
14 compatible = "ot,hvac-large";
15 heat-range = < 40 75 >;
16 cool-range = < 65 80 >;
17 };
18 };
19 }; 13 };
14};
20 15
21 fragment@1 { 16&rides_1 {
22 target = <&rides_1>;
23
24 __overlay__ {
25 #address-cells = <1>;
26 #size-cells = <1>;
27 status = "okay";
28
29 ride@100 {
30 #address-cells = <1>;
31 #size-cells = <1>;
32
33 track@30 {
34 incline-up = < 48 32 16 >;
35 };
36 17
37 track@40 { 18 #address-cells = <1>;
38 incline-up = < 47 31 15 >; 19 #size-cells = <1>;
39 }; 20 status = "okay";
40 };
41 21
42 ride_200: ride@200 { 22 ride@100 {
43 #address-cells = <1>; 23 #address-cells = <1>;
44 #size-cells = <1>; 24 #size-cells = <1>;
45 compatible = "ot,ferris-wheel";
46 reg = < 0x00000200 0x100 >;
47 hvac-provider = < &hvac_2 >;
48 hvac-thermostat = < 27 32 > ;
49 hvac-zones = < 12 5 >;
50 hvac-zone-names = "operator", "snack-bar";
51 spin-controller = < &spin_ctrl_1 3 >;
52 spin-rph = < 30 >;
53 gondolas = < 16 >;
54 gondola-capacity = < 6 >;
55 25
56 ride_200_left: track@10 { 26 track@30 {
57 reg = < 0x00000010 0x10 >; 27 incline-up = < 48 32 16 >;
58 }; 28 };
59 29
60 ride_200_right: track@20 { 30 track@40 {
61 reg = < 0x00000020 0x10 >; 31 incline-up = < 47 31 15 >;
62 };
63 };
64 }; 32 };
65 }; 33 };
66 34
67 fragment@2 { 35 ride_200: ride@200 {
68 target = <&lights_2>; 36 #address-cells = <1>;
37 #size-cells = <1>;
38 compatible = "ot,ferris-wheel";
39 reg = < 0x00000200 0x100 >;
40 hvac-provider = < &hvac_2 >;
41 hvac-thermostat = < 27 32 > ;
42 hvac-zones = < 12 5 >;
43 hvac-zone-names = "operator", "snack-bar";
44 spin-controller = < &spin_ctrl_1 3 >;
45 spin-rph = < 30 >;
46 gondolas = < 16 >;
47 gondola-capacity = < 6 >;
48
49 ride_200_left: track@10 {
50 reg = < 0x00000010 0x10 >;
51 };
69 52
70 __overlay__ { 53 ride_200_right: track@20 {
71 status = "okay"; 54 reg = < 0x00000020 0x10 >;
72 color = "purple", "white", "red", "green";
73 rate = < 3 256 >;
74 }; 55 };
75 }; 56 };
57};
58
59&lights_2 {
76 60
61 status = "okay";
62 color = "purple", "white", "red", "green";
63 rate = < 3 256 >;
77}; 64};
diff --git a/drivers/of/unittest-data/overlay_0.dts b/drivers/of/unittest-data/overlay_0.dts
new file mode 100644
index 000000000000..ac0f9e0fe65f
--- /dev/null
+++ b/drivers/of/unittest-data/overlay_0.dts
@@ -0,0 +1,14 @@
1// SPDX-License-Identifier: GPL-2.0
2/dts-v1/;
3/plugin/;
4
5/ {
6 /* overlay_0 - enable using absolute target path */
7
8 fragment@0 {
9 target-path = "/testcase-data/overlay-node/test-bus/test-unittest0";
10 __overlay__ {
11 status = "okay";
12 };
13 };
14};
diff --git a/drivers/of/unittest-data/overlay_1.dts b/drivers/of/unittest-data/overlay_1.dts
new file mode 100644
index 000000000000..e92a626e2948
--- /dev/null
+++ b/drivers/of/unittest-data/overlay_1.dts
@@ -0,0 +1,14 @@
1// SPDX-License-Identifier: GPL-2.0
2/dts-v1/;
3/plugin/;
4
5/ {
6 /* overlay_1 - disable using absolute target path */
7
8 fragment@0 {
9 target-path = "/testcase-data/overlay-node/test-bus/test-unittest1";
10 __overlay__ {
11 status = "disabled";
12 };
13 };
14};
diff --git a/drivers/of/unittest-data/overlay_10.dts b/drivers/of/unittest-data/overlay_10.dts
new file mode 100644
index 000000000000..73993bf23bf8
--- /dev/null
+++ b/drivers/of/unittest-data/overlay_10.dts
@@ -0,0 +1,27 @@
1// SPDX-License-Identifier: GPL-2.0
2/dts-v1/;
3/plugin/;
4
5/* overlay_10 */
6/* overlays 8, 9, 10, 11 application and removal in bad sequence */
7
8&unittest_test_bus {
9 /* suppress DTC warning */
10 #address-cells = <1>;
11 #size-cells = <0>;
12
13 test-unittest10 {
14 compatible = "unittest";
15 status = "okay";
16 reg = <10>;
17
18 #address-cells = <1>;
19 #size-cells = <0>;
20
21 test-unittest101 {
22 compatible = "unittest";
23 status = "okay";
24 reg = <1>;
25 };
26 };
27};
diff --git a/drivers/of/unittest-data/overlay_11.dts b/drivers/of/unittest-data/overlay_11.dts
new file mode 100644
index 000000000000..9a79b253a809
--- /dev/null
+++ b/drivers/of/unittest-data/overlay_11.dts
@@ -0,0 +1,28 @@
1// SPDX-License-Identifier: GPL-2.0
2/dts-v1/;
3/plugin/;
4
5/* overlay_11 */
6/* overlays 8, 9, 10, 11 application and removal in bad sequence */
7
8&unittest_test_bus {
9 /* suppress DTC warning */
10 #address-cells = <1>;
11 #size-cells = <0>;
12
13 test-unittest11 {
14 compatible = "unittest";
15 status = "okay";
16 reg = <11>;
17
18 #address-cells = <1>;
19 #size-cells = <0>;
20
21 test-unittest111 {
22 compatible = "unittest";
23 status = "okay";
24 reg = <1>;
25 };
26
27 };
28};
diff --git a/drivers/of/unittest-data/overlay_12.dts b/drivers/of/unittest-data/overlay_12.dts
new file mode 100644
index 000000000000..ca3441e2cbec
--- /dev/null
+++ b/drivers/of/unittest-data/overlay_12.dts
@@ -0,0 +1,14 @@
1// SPDX-License-Identifier: GPL-2.0
2/dts-v1/;
3/plugin/;
4
5/ {
6 /* overlay_12 - enable using absolute target path (i2c) */
7
8 fragment@0 {
9 target-path = "/testcase-data/overlay-node/test-bus/i2c-test-bus/test-unittest12";
10 __overlay__ {
11 status = "okay";
12 };
13 };
14};
diff --git a/drivers/of/unittest-data/overlay_13.dts b/drivers/of/unittest-data/overlay_13.dts
new file mode 100644
index 000000000000..3c30dec63894
--- /dev/null
+++ b/drivers/of/unittest-data/overlay_13.dts
@@ -0,0 +1,14 @@
1// SPDX-License-Identifier: GPL-2.0
2/dts-v1/;
3/plugin/;
4
5/ {
6 /* overlay_13 - disable using absolute target path (i2c) */
7
8 fragment@0 {
9 target-path = "/testcase-data/overlay-node/test-bus/i2c-test-bus/test-unittest13";
10 __overlay__ {
11 status = "disabled";
12 };
13 };
14};
diff --git a/drivers/of/unittest-data/overlay_15.dts b/drivers/of/unittest-data/overlay_15.dts
new file mode 100644
index 000000000000..b98f2514df4b
--- /dev/null
+++ b/drivers/of/unittest-data/overlay_15.dts
@@ -0,0 +1,30 @@
1// SPDX-License-Identifier: GPL-2.0
2/dts-v1/;
3/plugin/;
4
5/* overlay_15 - mux overlay */
6
7&unittest_i2c_test_bus {
8 #address-cells = <1>;
9 #size-cells = <0>;
10 test-unittest15 {
11 reg = <11>;
12 compatible = "unittest-i2c-mux";
13 status = "okay";
14
15 #address-cells = <1>;
16 #size-cells = <0>;
17
18 i2c@0 {
19 #address-cells = <1>;
20 #size-cells = <0>;
21 reg = <0>;
22
23 test-mux-dev {
24 reg = <32>;
25 compatible = "unittest-i2c-dev";
26 status = "okay";
27 };
28 };
29 };
30};
diff --git a/drivers/of/unittest-data/overlay_2.dts b/drivers/of/unittest-data/overlay_2.dts
new file mode 100644
index 000000000000..db8684ba89d9
--- /dev/null
+++ b/drivers/of/unittest-data/overlay_2.dts
@@ -0,0 +1,9 @@
1// SPDX-License-Identifier: GPL-2.0
2/dts-v1/;
3/plugin/;
4
5/* overlay_2 - enable using label */
6
7&unittest2 {
8 status = "okay";
9};
diff --git a/drivers/of/unittest-data/overlay_3.dts b/drivers/of/unittest-data/overlay_3.dts
new file mode 100644
index 000000000000..40f289e7c237
--- /dev/null
+++ b/drivers/of/unittest-data/overlay_3.dts
@@ -0,0 +1,9 @@
1// SPDX-License-Identifier: GPL-2.0
2/dts-v1/;
3/plugin/;
4
5/* overlay_3 - disable using label */
6
7&unittest3 {
8 status = "disabled";
9};
diff --git a/drivers/of/unittest-data/overlay_4.dts b/drivers/of/unittest-data/overlay_4.dts
new file mode 100644
index 000000000000..a8a77ddf9abe
--- /dev/null
+++ b/drivers/of/unittest-data/overlay_4.dts
@@ -0,0 +1,18 @@
1// SPDX-License-Identifier: GPL-2.0
2/dts-v1/;
3/plugin/;
4
5/* overlay_4 - test insertion of a full node */
6
7&unittest_test_bus {
8
9 /* suppress DTC warning */
10 #address-cells = <1>;
11 #size-cells = <0>;
12
13 test-unittest4 {
14 compatible = "unittest";
15 status = "okay";
16 reg = <4>;
17 };
18};
diff --git a/drivers/of/unittest-data/overlay_5.dts b/drivers/of/unittest-data/overlay_5.dts
new file mode 100644
index 000000000000..706f5f1b737c
--- /dev/null
+++ b/drivers/of/unittest-data/overlay_5.dts
@@ -0,0 +1,9 @@
1// SPDX-License-Identifier: GPL-2.0
2/dts-v1/;
3/plugin/;
4
5/* overlay_5 - test overlay apply revert */
6
7&unittest5 {
8 status = "okay";
9};
diff --git a/drivers/of/unittest-data/overlay_6.dts b/drivers/of/unittest-data/overlay_6.dts
new file mode 100644
index 000000000000..21a7fa4ca45e
--- /dev/null
+++ b/drivers/of/unittest-data/overlay_6.dts
@@ -0,0 +1,10 @@
1// SPDX-License-Identifier: GPL-2.0
2/dts-v1/;
3/plugin/;
4
5/* overlay_6 */
6/* overlays 6, 7 application and removal in sequence */
7
8&unittest6 {
9 status = "okay";
10};
diff --git a/drivers/of/unittest-data/overlay_7.dts b/drivers/of/unittest-data/overlay_7.dts
new file mode 100644
index 000000000000..58ba1bb51b50
--- /dev/null
+++ b/drivers/of/unittest-data/overlay_7.dts
@@ -0,0 +1,10 @@
1// SPDX-License-Identifier: GPL-2.0
2/dts-v1/;
3/plugin/;
4
5/* overlay_7 */
6/* overlays 6, 7 application and removal in sequence */
7
8&unittest7 {
9 status = "okay";
10};
diff --git a/drivers/of/unittest-data/overlay_8.dts b/drivers/of/unittest-data/overlay_8.dts
new file mode 100644
index 000000000000..e9718d118e38
--- /dev/null
+++ b/drivers/of/unittest-data/overlay_8.dts
@@ -0,0 +1,10 @@
1// SPDX-License-Identifier: GPL-2.0
2/dts-v1/;
3/plugin/;
4
5/* overlay_8 */
6/* overlays 8, 9, 10, 11 application and removal in bad sequence */
7
8&unittest8 {
9 status = "okay";
10};
diff --git a/drivers/of/unittest-data/overlay_9.dts b/drivers/of/unittest-data/overlay_9.dts
new file mode 100644
index 000000000000..b35e23edae50
--- /dev/null
+++ b/drivers/of/unittest-data/overlay_9.dts
@@ -0,0 +1,10 @@
1// SPDX-License-Identifier: GPL-2.0
2/dts-v1/;
3/plugin/;
4
5/* overlay_9 */
6/* overlays 8, 9, 10, 11 application and removal in bad sequence */
7
8&unittest8 {
9 property-foo = "bar";
10};
diff --git a/drivers/of/unittest-data/overlay_bad_phandle.dts b/drivers/of/unittest-data/overlay_bad_phandle.dts
index 4d5b99723bad..83b797360318 100644
--- a/drivers/of/unittest-data/overlay_bad_phandle.dts
+++ b/drivers/of/unittest-data/overlay_bad_phandle.dts
@@ -2,20 +2,13 @@
2/dts-v1/; 2/dts-v1/;
3/plugin/; 3/plugin/;
4 4
5/ { 5&electric_1 {
6 6
7 fragment@0 { 7 // This label should cause an error when the overlay
8 target = <&electric_1>; 8 // is applied. There is already a phandle value
9 9 // in the base tree for motor-1.
10 __overlay__ { 10 spin_ctrl_1_conflict: motor-1 {
11 11 accelerate = < 3 >;
12 // This label should cause an error when the overlay 12 decelerate = < 5 >;
13 // is applied. There is already a phandle value
14 // in the base tree for motor-1.
15 spin_ctrl_1_conflict: motor-1 {
16 accelerate = < 3 >;
17 decelerate = < 5 >;
18 };
19 };
20 }; 13 };
21}; 14};
diff --git a/drivers/of/unittest-data/overlay_bad_symbol.dts b/drivers/of/unittest-data/overlay_bad_symbol.dts
index 135052ee1517..98c6d1de144a 100644
--- a/drivers/of/unittest-data/overlay_bad_symbol.dts
+++ b/drivers/of/unittest-data/overlay_bad_symbol.dts
@@ -2,22 +2,15 @@
2/dts-v1/; 2/dts-v1/;
3/plugin/; 3/plugin/;
4 4
5/ { 5&electric_1 {
6 6
7 fragment@0 { 7 // This label should cause an error when the overlay
8 target = <&electric_1>; 8 // is applied. There is already a symbol hvac_1
9 9 // in the base tree
10 __overlay__ { 10 hvac_1: hvac-medium-2 {
11 11 compatible = "ot,hvac-medium";
12 // This label should cause an error when the overlay 12 heat-range = < 50 75 >;
13 // is applied. There is already a symbol hvac_1 13 cool-range = < 60 80 >;
14 // in the base tree
15 hvac_1: hvac-medium-2 {
16 compatible = "ot,hvac-medium";
17 heat-range = < 50 75 >;
18 cool-range = < 60 80 >;
19 };
20
21 };
22 }; 14 };
15
23}; 16};
diff --git a/drivers/of/unittest-data/tests-overlay.dtsi b/drivers/of/unittest-data/tests-overlay.dtsi
index 7b8001ab9f3a..25cf397b8f6b 100644
--- a/drivers/of/unittest-data/tests-overlay.dtsi
+++ b/drivers/of/unittest-data/tests-overlay.dtsi
@@ -5,7 +5,7 @@
5 overlay-node { 5 overlay-node {
6 6
7 /* test bus */ 7 /* test bus */
8 unittestbus: test-bus { 8 unittest_test_bus: test-bus {
9 compatible = "simple-bus"; 9 compatible = "simple-bus";
10 #address-cells = <1>; 10 #address-cells = <1>;
11 #size-cells = <0>; 11 #size-cells = <0>;
@@ -70,7 +70,7 @@
70 reg = <8>; 70 reg = <8>;
71 }; 71 };
72 72
73 i2c-test-bus { 73 unittest_i2c_test_bus: i2c-test-bus {
74 compatible = "unittest-i2c-bus"; 74 compatible = "unittest-i2c-bus";
75 status = "okay"; 75 status = "okay";
76 reg = <50>; 76 reg = <50>;
@@ -113,218 +113,5 @@
113 }; 113 };
114 }; 114 };
115 }; 115 };
116
117 /* test enable using absolute target path */
118 overlay0 {
119 fragment@0 {
120 target-path = "/testcase-data/overlay-node/test-bus/test-unittest0";
121 __overlay__ {
122 status = "okay";
123 };
124 };
125 };
126
127 /* test disable using absolute target path */
128 overlay1 {
129 fragment@0 {
130 target-path = "/testcase-data/overlay-node/test-bus/test-unittest1";
131 __overlay__ {
132 status = "disabled";
133 };
134 };
135 };
136
137 /* test enable using label */
138 overlay2 {
139 fragment@0 {
140 target = <&unittest2>;
141 __overlay__ {
142 status = "okay";
143 };
144 };
145 };
146
147 /* test disable using label */
148 overlay3 {
149 fragment@0 {
150 target = <&unittest3>;
151 __overlay__ {
152 status = "disabled";
153 };
154 };
155 };
156
157 /* test insertion of a full node */
158 overlay4 {
159 fragment@0 {
160 target = <&unittestbus>;
161 __overlay__ {
162
163 /* suppress DTC warning */
164 #address-cells = <1>;
165 #size-cells = <0>;
166
167 test-unittest4 {
168 compatible = "unittest";
169 status = "okay";
170 reg = <4>;
171 };
172 };
173 };
174 };
175
176 /* test overlay apply revert */
177 overlay5 {
178 fragment@0 {
179 target-path = "/testcase-data/overlay-node/test-bus/test-unittest5";
180 __overlay__ {
181 status = "okay";
182 };
183 };
184 };
185
186 /* test overlays application and removal in sequence */
187 overlay6 {
188 fragment@0 {
189 target-path = "/testcase-data/overlay-node/test-bus/test-unittest6";
190 __overlay__ {
191 status = "okay";
192 };
193 };
194 };
195 overlay7 {
196 fragment@0 {
197 target-path = "/testcase-data/overlay-node/test-bus/test-unittest7";
198 __overlay__ {
199 status = "okay";
200 };
201 };
202 };
203
204 /* test overlays application and removal in bad sequence */
205 overlay8 {
206 fragment@0 {
207 target-path = "/testcase-data/overlay-node/test-bus/test-unittest8";
208 __overlay__ {
209 status = "okay";
210 };
211 };
212 };
213 overlay9 {
214 fragment@0 {
215 target-path = "/testcase-data/overlay-node/test-bus/test-unittest8";
216 __overlay__ {
217 property-foo = "bar";
218 };
219 };
220 };
221
222 overlay10 {
223 fragment@0 {
224 target-path = "/testcase-data/overlay-node/test-bus";
225 __overlay__ {
226
227 /* suppress DTC warning */
228 #address-cells = <1>;
229 #size-cells = <0>;
230
231 test-unittest10 {
232 compatible = "unittest";
233 status = "okay";
234 reg = <10>;
235
236 #address-cells = <1>;
237 #size-cells = <0>;
238
239 test-unittest101 {
240 compatible = "unittest";
241 status = "okay";
242 reg = <1>;
243 };
244
245 };
246 };
247 };
248 };
249
250 overlay11 {
251 fragment@0 {
252 target-path = "/testcase-data/overlay-node/test-bus";
253 __overlay__ {
254
255 /* suppress DTC warning */
256 #address-cells = <1>;
257 #size-cells = <0>;
258
259 test-unittest11 {
260 compatible = "unittest";
261 status = "okay";
262 reg = <11>;
263
264 #address-cells = <1>;
265 #size-cells = <0>;
266
267 test-unittest111 {
268 compatible = "unittest";
269 status = "okay";
270 reg = <1>;
271 };
272
273 };
274 };
275 };
276 };
277
278 /* test enable using absolute target path (i2c) */
279 overlay12 {
280 fragment@0 {
281 target-path = "/testcase-data/overlay-node/test-bus/i2c-test-bus/test-unittest12";
282 __overlay__ {
283 status = "okay";
284 };
285 };
286 };
287
288 /* test disable using absolute target path (i2c) */
289 overlay13 {
290 fragment@0 {
291 target-path = "/testcase-data/overlay-node/test-bus/i2c-test-bus/test-unittest13";
292 __overlay__ {
293 status = "disabled";
294 };
295 };
296 };
297
298 /* test mux overlay */
299 overlay15 {
300 fragment@0 {
301 target-path = "/testcase-data/overlay-node/test-bus/i2c-test-bus";
302 __overlay__ {
303 #address-cells = <1>;
304 #size-cells = <0>;
305 test-unittest15 {
306 reg = <11>;
307 compatible = "unittest-i2c-mux";
308 status = "okay";
309
310 #address-cells = <1>;
311 #size-cells = <0>;
312
313 i2c@0 {
314 #address-cells = <1>;
315 #size-cells = <0>;
316 reg = <0>;
317
318 test-mux-dev {
319 reg = <32>;
320 compatible = "unittest-i2c-dev";
321 status = "okay";
322 };
323 };
324 };
325 };
326 };
327 };
328
329 }; 116 };
330}; 117};
diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c
index 7a9abaae874d..a23b54780c7d 100644
--- a/drivers/of/unittest.c
+++ b/drivers/of/unittest.c
@@ -45,6 +45,8 @@ static struct unittest_results {
45 failed; \ 45 failed; \
46}) 46})
47 47
48static int __init overlay_data_apply(const char *overlay_name, int *overlay_id);
49
48static void __init of_unittest_find_node_by_name(void) 50static void __init of_unittest_find_node_by_name(void)
49{ 51{
50 struct device_node *np; 52 struct device_node *np;
@@ -997,8 +999,7 @@ static int __init unittest_data_add(void)
997 } 999 }
998 1000
999 /* 1001 /*
1000 * This lock normally encloses of_overlay_apply() as well as 1002 * This lock normally encloses of_resolve_phandles()
1001 * of_resolve_phandles().
1002 */ 1003 */
1003 of_overlay_mutex_lock(); 1004 of_overlay_mutex_lock();
1004 1005
@@ -1191,12 +1192,12 @@ static int of_unittest_device_exists(int unittest_nr, enum overlay_type ovtype)
1191 return 0; 1192 return 0;
1192} 1193}
1193 1194
1194static const char *overlay_path(int nr) 1195static const char *overlay_name_from_nr(int nr)
1195{ 1196{
1196 static char buf[256]; 1197 static char buf[256];
1197 1198
1198 snprintf(buf, sizeof(buf) - 1, 1199 snprintf(buf, sizeof(buf) - 1,
1199 "/testcase-data/overlay%d", nr); 1200 "overlay_%d", nr);
1200 buf[sizeof(buf) - 1] = '\0'; 1201 buf[sizeof(buf) - 1] = '\0';
1201 1202
1202 return buf; 1203 return buf;
@@ -1263,25 +1264,19 @@ static void of_unittest_destroy_tracked_overlays(void)
1263 } while (defers > 0); 1264 } while (defers > 0);
1264} 1265}
1265 1266
1266static int of_unittest_apply_overlay(int overlay_nr, int unittest_nr, 1267static int __init of_unittest_apply_overlay(int overlay_nr, int unittest_nr,
1267 int *overlay_id) 1268 int *overlay_id)
1268{ 1269{
1269 struct device_node *np = NULL; 1270 struct device_node *np = NULL;
1271 const char *overlay_name;
1270 int ret; 1272 int ret;
1271 1273
1272 np = of_find_node_by_path(overlay_path(overlay_nr)); 1274 overlay_name = overlay_name_from_nr(overlay_nr);
1273 if (np == NULL) {
1274 unittest(0, "could not find overlay node @\"%s\"\n",
1275 overlay_path(overlay_nr));
1276 ret = -EINVAL;
1277 goto out;
1278 }
1279 1275
1280 *overlay_id = 0; 1276 ret = overlay_data_apply(overlay_name, overlay_id);
1281 ret = of_overlay_apply(np, overlay_id); 1277 if (!ret) {
1282 if (ret < 0) { 1278 unittest(0, "could not apply overlay \"%s\"\n",
1283 unittest(0, "could not create overlay from \"%s\"\n", 1279 overlay_name);
1284 overlay_path(overlay_nr));
1285 goto out; 1280 goto out;
1286 } 1281 }
1287 of_unittest_track_overlay(*overlay_id); 1282 of_unittest_track_overlay(*overlay_id);
@@ -1295,15 +1290,16 @@ out:
1295} 1290}
1296 1291
1297/* apply an overlay while checking before and after states */ 1292/* apply an overlay while checking before and after states */
1298static int of_unittest_apply_overlay_check(int overlay_nr, int unittest_nr, 1293static int __init of_unittest_apply_overlay_check(int overlay_nr,
1299 int before, int after, enum overlay_type ovtype) 1294 int unittest_nr, int before, int after,
1295 enum overlay_type ovtype)
1300{ 1296{
1301 int ret, ovcs_id; 1297 int ret, ovcs_id;
1302 1298
1303 /* unittest device must not be in before state */ 1299 /* unittest device must not be in before state */
1304 if (of_unittest_device_exists(unittest_nr, ovtype) != before) { 1300 if (of_unittest_device_exists(unittest_nr, ovtype) != before) {
1305 unittest(0, "overlay @\"%s\" with device @\"%s\" %s\n", 1301 unittest(0, "%s with device @\"%s\" %s\n",
1306 overlay_path(overlay_nr), 1302 overlay_name_from_nr(overlay_nr),
1307 unittest_path(unittest_nr, ovtype), 1303 unittest_path(unittest_nr, ovtype),
1308 !before ? "enabled" : "disabled"); 1304 !before ? "enabled" : "disabled");
1309 return -EINVAL; 1305 return -EINVAL;
@@ -1318,8 +1314,8 @@ static int of_unittest_apply_overlay_check(int overlay_nr, int unittest_nr,
1318 1314
1319 /* unittest device must be to set to after state */ 1315 /* unittest device must be to set to after state */
1320 if (of_unittest_device_exists(unittest_nr, ovtype) != after) { 1316 if (of_unittest_device_exists(unittest_nr, ovtype) != after) {
1321 unittest(0, "overlay @\"%s\" failed to create @\"%s\" %s\n", 1317 unittest(0, "%s failed to create @\"%s\" %s\n",
1322 overlay_path(overlay_nr), 1318 overlay_name_from_nr(overlay_nr),
1323 unittest_path(unittest_nr, ovtype), 1319 unittest_path(unittest_nr, ovtype),
1324 !after ? "enabled" : "disabled"); 1320 !after ? "enabled" : "disabled");
1325 return -EINVAL; 1321 return -EINVAL;
@@ -1329,7 +1325,7 @@ static int of_unittest_apply_overlay_check(int overlay_nr, int unittest_nr,
1329} 1325}
1330 1326
1331/* apply an overlay and then revert it while checking before, after states */ 1327/* apply an overlay and then revert it while checking before, after states */
1332static int of_unittest_apply_revert_overlay_check(int overlay_nr, 1328static int __init of_unittest_apply_revert_overlay_check(int overlay_nr,
1333 int unittest_nr, int before, int after, 1329 int unittest_nr, int before, int after,
1334 enum overlay_type ovtype) 1330 enum overlay_type ovtype)
1335{ 1331{
@@ -1337,8 +1333,8 @@ static int of_unittest_apply_revert_overlay_check(int overlay_nr,
1337 1333
1338 /* unittest device must be in before state */ 1334 /* unittest device must be in before state */
1339 if (of_unittest_device_exists(unittest_nr, ovtype) != before) { 1335 if (of_unittest_device_exists(unittest_nr, ovtype) != before) {
1340 unittest(0, "overlay @\"%s\" with device @\"%s\" %s\n", 1336 unittest(0, "%s with device @\"%s\" %s\n",
1341 overlay_path(overlay_nr), 1337 overlay_name_from_nr(overlay_nr),
1342 unittest_path(unittest_nr, ovtype), 1338 unittest_path(unittest_nr, ovtype),
1343 !before ? "enabled" : "disabled"); 1339 !before ? "enabled" : "disabled");
1344 return -EINVAL; 1340 return -EINVAL;
@@ -1354,8 +1350,8 @@ static int of_unittest_apply_revert_overlay_check(int overlay_nr,
1354 1350
1355 /* unittest device must be in after state */ 1351 /* unittest device must be in after state */
1356 if (of_unittest_device_exists(unittest_nr, ovtype) != after) { 1352 if (of_unittest_device_exists(unittest_nr, ovtype) != after) {
1357 unittest(0, "overlay @\"%s\" failed to create @\"%s\" %s\n", 1353 unittest(0, "%s failed to create @\"%s\" %s\n",
1358 overlay_path(overlay_nr), 1354 overlay_name_from_nr(overlay_nr),
1359 unittest_path(unittest_nr, ovtype), 1355 unittest_path(unittest_nr, ovtype),
1360 !after ? "enabled" : "disabled"); 1356 !after ? "enabled" : "disabled");
1361 return -EINVAL; 1357 return -EINVAL;
@@ -1363,16 +1359,16 @@ static int of_unittest_apply_revert_overlay_check(int overlay_nr,
1363 1359
1364 ret = of_overlay_remove(&ovcs_id); 1360 ret = of_overlay_remove(&ovcs_id);
1365 if (ret != 0) { 1361 if (ret != 0) {
1366 unittest(0, "overlay @\"%s\" failed to be destroyed @\"%s\"\n", 1362 unittest(0, "%s failed to be destroyed @\"%s\"\n",
1367 overlay_path(overlay_nr), 1363 overlay_name_from_nr(overlay_nr),
1368 unittest_path(unittest_nr, ovtype)); 1364 unittest_path(unittest_nr, ovtype));
1369 return ret; 1365 return ret;
1370 } 1366 }
1371 1367
1372 /* unittest device must be again in before state */ 1368 /* unittest device must be again in before state */
1373 if (of_unittest_device_exists(unittest_nr, PDEV_OVERLAY) != before) { 1369 if (of_unittest_device_exists(unittest_nr, PDEV_OVERLAY) != before) {
1374 unittest(0, "overlay @\"%s\" with device @\"%s\" %s\n", 1370 unittest(0, "%s with device @\"%s\" %s\n",
1375 overlay_path(overlay_nr), 1371 overlay_name_from_nr(overlay_nr),
1376 unittest_path(unittest_nr, ovtype), 1372 unittest_path(unittest_nr, ovtype),
1377 !before ? "enabled" : "disabled"); 1373 !before ? "enabled" : "disabled");
1378 return -EINVAL; 1374 return -EINVAL;
@@ -1382,7 +1378,7 @@ static int of_unittest_apply_revert_overlay_check(int overlay_nr,
1382} 1378}
1383 1379
1384/* test activation of device */ 1380/* test activation of device */
1385static void of_unittest_overlay_0(void) 1381static void __init of_unittest_overlay_0(void)
1386{ 1382{
1387 int ret; 1383 int ret;
1388 1384
@@ -1395,7 +1391,7 @@ static void of_unittest_overlay_0(void)
1395} 1391}
1396 1392
1397/* test deactivation of device */ 1393/* test deactivation of device */
1398static void of_unittest_overlay_1(void) 1394static void __init of_unittest_overlay_1(void)
1399{ 1395{
1400 int ret; 1396 int ret;
1401 1397
@@ -1408,7 +1404,7 @@ static void of_unittest_overlay_1(void)
1408} 1404}
1409 1405
1410/* test activation of device */ 1406/* test activation of device */
1411static void of_unittest_overlay_2(void) 1407static void __init of_unittest_overlay_2(void)
1412{ 1408{
1413 int ret; 1409 int ret;
1414 1410
@@ -1421,7 +1417,7 @@ static void of_unittest_overlay_2(void)
1421} 1417}
1422 1418
1423/* test deactivation of device */ 1419/* test deactivation of device */
1424static void of_unittest_overlay_3(void) 1420static void __init of_unittest_overlay_3(void)
1425{ 1421{
1426 int ret; 1422 int ret;
1427 1423
@@ -1434,7 +1430,7 @@ static void of_unittest_overlay_3(void)
1434} 1430}
1435 1431
1436/* test activation of a full device node */ 1432/* test activation of a full device node */
1437static void of_unittest_overlay_4(void) 1433static void __init of_unittest_overlay_4(void)
1438{ 1434{
1439 int ret; 1435 int ret;
1440 1436
@@ -1447,7 +1443,7 @@ static void of_unittest_overlay_4(void)
1447} 1443}
1448 1444
1449/* test overlay apply/revert sequence */ 1445/* test overlay apply/revert sequence */
1450static void of_unittest_overlay_5(void) 1446static void __init of_unittest_overlay_5(void)
1451{ 1447{
1452 int ret; 1448 int ret;
1453 1449
@@ -1460,19 +1456,19 @@ static void of_unittest_overlay_5(void)
1460} 1456}
1461 1457
1462/* test overlay application in sequence */ 1458/* test overlay application in sequence */
1463static void of_unittest_overlay_6(void) 1459static void __init of_unittest_overlay_6(void)
1464{ 1460{
1465 struct device_node *np;
1466 int ret, i, ov_id[2], ovcs_id; 1461 int ret, i, ov_id[2], ovcs_id;
1467 int overlay_nr = 6, unittest_nr = 6; 1462 int overlay_nr = 6, unittest_nr = 6;
1468 int before = 0, after = 1; 1463 int before = 0, after = 1;
1464 const char *overlay_name;
1469 1465
1470 /* unittest device must be in before state */ 1466 /* unittest device must be in before state */
1471 for (i = 0; i < 2; i++) { 1467 for (i = 0; i < 2; i++) {
1472 if (of_unittest_device_exists(unittest_nr + i, PDEV_OVERLAY) 1468 if (of_unittest_device_exists(unittest_nr + i, PDEV_OVERLAY)
1473 != before) { 1469 != before) {
1474 unittest(0, "overlay @\"%s\" with device @\"%s\" %s\n", 1470 unittest(0, "%s with device @\"%s\" %s\n",
1475 overlay_path(overlay_nr + i), 1471 overlay_name_from_nr(overlay_nr + i),
1476 unittest_path(unittest_nr + i, 1472 unittest_path(unittest_nr + i,
1477 PDEV_OVERLAY), 1473 PDEV_OVERLAY),
1478 !before ? "enabled" : "disabled"); 1474 !before ? "enabled" : "disabled");
@@ -1483,18 +1479,12 @@ static void of_unittest_overlay_6(void)
1483 /* apply the overlays */ 1479 /* apply the overlays */
1484 for (i = 0; i < 2; i++) { 1480 for (i = 0; i < 2; i++) {
1485 1481
1486 np = of_find_node_by_path(overlay_path(overlay_nr + i)); 1482 overlay_name = overlay_name_from_nr(overlay_nr + i);
1487 if (np == NULL) {
1488 unittest(0, "could not find overlay node @\"%s\"\n",
1489 overlay_path(overlay_nr + i));
1490 return;
1491 }
1492 1483
1493 ovcs_id = 0; 1484 ret = overlay_data_apply(overlay_name, &ovcs_id);
1494 ret = of_overlay_apply(np, &ovcs_id); 1485 if (!ret) {
1495 if (ret < 0) { 1486 unittest(0, "could not apply overlay \"%s\"\n",
1496 unittest(0, "could not create overlay from \"%s\"\n", 1487 overlay_name);
1497 overlay_path(overlay_nr + i));
1498 return; 1488 return;
1499 } 1489 }
1500 ov_id[i] = ovcs_id; 1490 ov_id[i] = ovcs_id;
@@ -1506,7 +1496,7 @@ static void of_unittest_overlay_6(void)
1506 if (of_unittest_device_exists(unittest_nr + i, PDEV_OVERLAY) 1496 if (of_unittest_device_exists(unittest_nr + i, PDEV_OVERLAY)
1507 != after) { 1497 != after) {
1508 unittest(0, "overlay @\"%s\" failed @\"%s\" %s\n", 1498 unittest(0, "overlay @\"%s\" failed @\"%s\" %s\n",
1509 overlay_path(overlay_nr + i), 1499 overlay_name_from_nr(overlay_nr + i),
1510 unittest_path(unittest_nr + i, 1500 unittest_path(unittest_nr + i,
1511 PDEV_OVERLAY), 1501 PDEV_OVERLAY),
1512 !after ? "enabled" : "disabled"); 1502 !after ? "enabled" : "disabled");
@@ -1518,8 +1508,8 @@ static void of_unittest_overlay_6(void)
1518 ovcs_id = ov_id[i]; 1508 ovcs_id = ov_id[i];
1519 ret = of_overlay_remove(&ovcs_id); 1509 ret = of_overlay_remove(&ovcs_id);
1520 if (ret != 0) { 1510 if (ret != 0) {
1521 unittest(0, "overlay @\"%s\" failed destroy @\"%s\"\n", 1511 unittest(0, "%s failed destroy @\"%s\"\n",
1522 overlay_path(overlay_nr + i), 1512 overlay_name_from_nr(overlay_nr + i),
1523 unittest_path(unittest_nr + i, 1513 unittest_path(unittest_nr + i,
1524 PDEV_OVERLAY)); 1514 PDEV_OVERLAY));
1525 return; 1515 return;
@@ -1531,8 +1521,8 @@ static void of_unittest_overlay_6(void)
1531 /* unittest device must be again in before state */ 1521 /* unittest device must be again in before state */
1532 if (of_unittest_device_exists(unittest_nr + i, PDEV_OVERLAY) 1522 if (of_unittest_device_exists(unittest_nr + i, PDEV_OVERLAY)
1533 != before) { 1523 != before) {
1534 unittest(0, "overlay @\"%s\" with device @\"%s\" %s\n", 1524 unittest(0, "%s with device @\"%s\" %s\n",
1535 overlay_path(overlay_nr + i), 1525 overlay_name_from_nr(overlay_nr + i),
1536 unittest_path(unittest_nr + i, 1526 unittest_path(unittest_nr + i,
1537 PDEV_OVERLAY), 1527 PDEV_OVERLAY),
1538 !before ? "enabled" : "disabled"); 1528 !before ? "enabled" : "disabled");
@@ -1544,29 +1534,23 @@ static void of_unittest_overlay_6(void)
1544} 1534}
1545 1535
1546/* test overlay application in sequence */ 1536/* test overlay application in sequence */
1547static void of_unittest_overlay_8(void) 1537static void __init of_unittest_overlay_8(void)
1548{ 1538{
1549 struct device_node *np;
1550 int ret, i, ov_id[2], ovcs_id; 1539 int ret, i, ov_id[2], ovcs_id;
1551 int overlay_nr = 8, unittest_nr = 8; 1540 int overlay_nr = 8, unittest_nr = 8;
1541 const char *overlay_name;
1552 1542
1553 /* we don't care about device state in this test */ 1543 /* we don't care about device state in this test */
1554 1544
1555 /* apply the overlays */ 1545 /* apply the overlays */
1556 for (i = 0; i < 2; i++) { 1546 for (i = 0; i < 2; i++) {
1557 1547
1558 np = of_find_node_by_path(overlay_path(overlay_nr + i)); 1548 overlay_name = overlay_name_from_nr(overlay_nr + i);
1559 if (np == NULL) {
1560 unittest(0, "could not find overlay node @\"%s\"\n",
1561 overlay_path(overlay_nr + i));
1562 return;
1563 }
1564 1549
1565 ovcs_id = 0; 1550 ret = overlay_data_apply(overlay_name, &ovcs_id);
1566 ret = of_overlay_apply(np, &ovcs_id);
1567 if (ret < 0) { 1551 if (ret < 0) {
1568 unittest(0, "could not create overlay from \"%s\"\n", 1552 unittest(0, "could not apply overlay \"%s\"\n",
1569 overlay_path(overlay_nr + i)); 1553 overlay_name);
1570 return; 1554 return;
1571 } 1555 }
1572 ov_id[i] = ovcs_id; 1556 ov_id[i] = ovcs_id;
@@ -1577,8 +1561,8 @@ static void of_unittest_overlay_8(void)
1577 ovcs_id = ov_id[0]; 1561 ovcs_id = ov_id[0];
1578 ret = of_overlay_remove(&ovcs_id); 1562 ret = of_overlay_remove(&ovcs_id);
1579 if (ret == 0) { 1563 if (ret == 0) {
1580 unittest(0, "overlay @\"%s\" was destroyed @\"%s\"\n", 1564 unittest(0, "%s was destroyed @\"%s\"\n",
1581 overlay_path(overlay_nr + 0), 1565 overlay_name_from_nr(overlay_nr + 0),
1582 unittest_path(unittest_nr, 1566 unittest_path(unittest_nr,
1583 PDEV_OVERLAY)); 1567 PDEV_OVERLAY));
1584 return; 1568 return;
@@ -1589,8 +1573,8 @@ static void of_unittest_overlay_8(void)
1589 ovcs_id = ov_id[i]; 1573 ovcs_id = ov_id[i];
1590 ret = of_overlay_remove(&ovcs_id); 1574 ret = of_overlay_remove(&ovcs_id);
1591 if (ret != 0) { 1575 if (ret != 0) {
1592 unittest(0, "overlay @\"%s\" not destroyed @\"%s\"\n", 1576 unittest(0, "%s not destroyed @\"%s\"\n",
1593 overlay_path(overlay_nr + i), 1577 overlay_name_from_nr(overlay_nr + i),
1594 unittest_path(unittest_nr, 1578 unittest_path(unittest_nr,
1595 PDEV_OVERLAY)); 1579 PDEV_OVERLAY));
1596 return; 1580 return;
@@ -1602,7 +1586,7 @@ static void of_unittest_overlay_8(void)
1602} 1586}
1603 1587
1604/* test insertion of a bus with parent devices */ 1588/* test insertion of a bus with parent devices */
1605static void of_unittest_overlay_10(void) 1589static void __init of_unittest_overlay_10(void)
1606{ 1590{
1607 int ret; 1591 int ret;
1608 char *child_path; 1592 char *child_path;
@@ -1625,7 +1609,7 @@ static void of_unittest_overlay_10(void)
1625} 1609}
1626 1610
1627/* test insertion of a bus with parent devices (and revert) */ 1611/* test insertion of a bus with parent devices (and revert) */
1628static void of_unittest_overlay_11(void) 1612static void __init of_unittest_overlay_11(void)
1629{ 1613{
1630 int ret; 1614 int ret;
1631 1615
@@ -1891,7 +1875,7 @@ static void of_unittest_overlay_i2c_cleanup(void)
1891 i2c_del_driver(&unittest_i2c_dev_driver); 1875 i2c_del_driver(&unittest_i2c_dev_driver);
1892} 1876}
1893 1877
1894static void of_unittest_overlay_i2c_12(void) 1878static void __init of_unittest_overlay_i2c_12(void)
1895{ 1879{
1896 int ret; 1880 int ret;
1897 1881
@@ -1904,7 +1888,7 @@ static void of_unittest_overlay_i2c_12(void)
1904} 1888}
1905 1889
1906/* test deactivation of device */ 1890/* test deactivation of device */
1907static void of_unittest_overlay_i2c_13(void) 1891static void __init of_unittest_overlay_i2c_13(void)
1908{ 1892{
1909 int ret; 1893 int ret;
1910 1894
@@ -1921,7 +1905,7 @@ static void of_unittest_overlay_i2c_14(void)
1921{ 1905{
1922} 1906}
1923 1907
1924static void of_unittest_overlay_i2c_15(void) 1908static void __init of_unittest_overlay_i2c_15(void)
1925{ 1909{
1926 int ret; 1910 int ret;
1927 1911
@@ -2023,23 +2007,38 @@ static inline void __init of_unittest_overlay(void) { }
2023 extern uint8_t __dtb_##name##_begin[]; \ 2007 extern uint8_t __dtb_##name##_begin[]; \
2024 extern uint8_t __dtb_##name##_end[] 2008 extern uint8_t __dtb_##name##_end[]
2025 2009
2026#define OVERLAY_INFO(name, expected) \ 2010#define OVERLAY_INFO(overlay_name, expected) \
2027{ .dtb_begin = __dtb_##name##_begin, \ 2011{ .dtb_begin = __dtb_##overlay_name##_begin, \
2028 .dtb_end = __dtb_##name##_end, \ 2012 .dtb_end = __dtb_##overlay_name##_end, \
2029 .expected_result = expected, \ 2013 .expected_result = expected, \
2014 .name = #overlay_name, \
2030} 2015}
2031 2016
2032struct overlay_info { 2017struct overlay_info {
2033 uint8_t *dtb_begin; 2018 uint8_t *dtb_begin;
2034 uint8_t *dtb_end; 2019 uint8_t *dtb_end;
2035 void *data; 2020 int expected_result;
2036 struct device_node *np_overlay; 2021 int overlay_id;
2037 int expected_result; 2022 char *name;
2038 int overlay_id;
2039}; 2023};
2040 2024
2041OVERLAY_INFO_EXTERN(overlay_base); 2025OVERLAY_INFO_EXTERN(overlay_base);
2042OVERLAY_INFO_EXTERN(overlay); 2026OVERLAY_INFO_EXTERN(overlay);
2027OVERLAY_INFO_EXTERN(overlay_0);
2028OVERLAY_INFO_EXTERN(overlay_1);
2029OVERLAY_INFO_EXTERN(overlay_2);
2030OVERLAY_INFO_EXTERN(overlay_3);
2031OVERLAY_INFO_EXTERN(overlay_4);
2032OVERLAY_INFO_EXTERN(overlay_5);
2033OVERLAY_INFO_EXTERN(overlay_6);
2034OVERLAY_INFO_EXTERN(overlay_7);
2035OVERLAY_INFO_EXTERN(overlay_8);
2036OVERLAY_INFO_EXTERN(overlay_9);
2037OVERLAY_INFO_EXTERN(overlay_10);
2038OVERLAY_INFO_EXTERN(overlay_11);
2039OVERLAY_INFO_EXTERN(overlay_12);
2040OVERLAY_INFO_EXTERN(overlay_13);
2041OVERLAY_INFO_EXTERN(overlay_15);
2043OVERLAY_INFO_EXTERN(overlay_bad_phandle); 2042OVERLAY_INFO_EXTERN(overlay_bad_phandle);
2044OVERLAY_INFO_EXTERN(overlay_bad_symbol); 2043OVERLAY_INFO_EXTERN(overlay_bad_symbol);
2045 2044
@@ -2047,6 +2046,21 @@ OVERLAY_INFO_EXTERN(overlay_bad_symbol);
2047static struct overlay_info overlays[] = { 2046static struct overlay_info overlays[] = {
2048 OVERLAY_INFO(overlay_base, -9999), 2047 OVERLAY_INFO(overlay_base, -9999),
2049 OVERLAY_INFO(overlay, 0), 2048 OVERLAY_INFO(overlay, 0),
2049 OVERLAY_INFO(overlay_0, 0),
2050 OVERLAY_INFO(overlay_1, 0),
2051 OVERLAY_INFO(overlay_2, 0),
2052 OVERLAY_INFO(overlay_3, 0),
2053 OVERLAY_INFO(overlay_4, 0),
2054 OVERLAY_INFO(overlay_5, 0),
2055 OVERLAY_INFO(overlay_6, 0),
2056 OVERLAY_INFO(overlay_7, 0),
2057 OVERLAY_INFO(overlay_8, 0),
2058 OVERLAY_INFO(overlay_9, 0),
2059 OVERLAY_INFO(overlay_10, 0),
2060 OVERLAY_INFO(overlay_11, 0),
2061 OVERLAY_INFO(overlay_12, 0),
2062 OVERLAY_INFO(overlay_13, 0),
2063 OVERLAY_INFO(overlay_15, 0),
2050 OVERLAY_INFO(overlay_bad_phandle, -EINVAL), 2064 OVERLAY_INFO(overlay_bad_phandle, -EINVAL),
2051 OVERLAY_INFO(overlay_bad_symbol, -EINVAL), 2065 OVERLAY_INFO(overlay_bad_symbol, -EINVAL),
2052 {} 2066 {}
@@ -2077,6 +2091,7 @@ void __init unittest_unflatten_overlay_base(void)
2077{ 2091{
2078 struct overlay_info *info; 2092 struct overlay_info *info;
2079 u32 data_size; 2093 u32 data_size;
2094 void *new_fdt;
2080 u32 size; 2095 u32 size;
2081 2096
2082 info = &overlays[0]; 2097 info = &overlays[0];
@@ -2098,17 +2113,16 @@ void __init unittest_unflatten_overlay_base(void)
2098 return; 2113 return;
2099 } 2114 }
2100 2115
2101 info->data = dt_alloc_memory(size, roundup_pow_of_two(FDT_V17_SIZE)); 2116 new_fdt = dt_alloc_memory(size, roundup_pow_of_two(FDT_V17_SIZE));
2102 if (!info->data) { 2117 if (!new_fdt) {
2103 pr_err("alloc for dtb 'overlay_base' failed"); 2118 pr_err("alloc for dtb 'overlay_base' failed");
2104 return; 2119 return;
2105 } 2120 }
2106 2121
2107 memcpy(info->data, info->dtb_begin, size); 2122 memcpy(new_fdt, info->dtb_begin, size);
2108 2123
2109 __unflatten_device_tree(info->data, NULL, &info->np_overlay, 2124 __unflatten_device_tree(new_fdt, NULL, &overlay_base_root,
2110 dt_alloc_memory, true); 2125 dt_alloc_memory, true);
2111 overlay_base_root = info->np_overlay;
2112} 2126}
2113 2127
2114/* 2128/*
@@ -2122,73 +2136,44 @@ void __init unittest_unflatten_overlay_base(void)
2122 * 2136 *
2123 * Return 0 on unexpected error. 2137 * Return 0 on unexpected error.
2124 */ 2138 */
2125static int __init overlay_data_add(int onum) 2139static int __init overlay_data_apply(const char *overlay_name, int *overlay_id)
2126{ 2140{
2127 struct overlay_info *info; 2141 struct overlay_info *info;
2142 int found = 0;
2128 int k; 2143 int k;
2129 int ret; 2144 int ret;
2130 u32 size; 2145 u32 size;
2131 u32 size_from_header;
2132 2146
2133 for (k = 0, info = overlays; info; info++, k++) { 2147 for (k = 0, info = overlays; info && info->name; info++, k++) {
2134 if (k == onum) 2148 if (!strcmp(overlay_name, info->name)) {
2149 found = 1;
2135 break; 2150 break;
2151 }
2136 } 2152 }
2137 if (onum > k) 2153 if (!found) {
2154 pr_err("no overlay data for %s\n", overlay_name);
2138 return 0; 2155 return 0;
2156 }
2139 2157
2140 size = info->dtb_end - info->dtb_begin; 2158 size = info->dtb_end - info->dtb_begin;
2141 if (!size) { 2159 if (!size) {
2142 pr_err("no overlay to attach, %d\n", onum); 2160 pr_err("no overlay data for %s\n", overlay_name);
2143 ret = 0; 2161 ret = 0;
2144 } 2162 }
2145 2163
2146 size_from_header = fdt_totalsize(info->dtb_begin); 2164 ret = of_overlay_fdt_apply(info->dtb_begin, size, &info->overlay_id);
2147 if (size_from_header != size) { 2165 if (overlay_id)
2148 pr_err("overlay header totalsize != actual size, %d", onum); 2166 *overlay_id = info->overlay_id;
2149 return 0; 2167 if (ret < 0)
2150 } 2168 goto out;
2151
2152 /*
2153 * Must create permanent copy of FDT because of_fdt_unflatten_tree()
2154 * will create pointers to the passed in FDT in the EDT.
2155 */
2156 info->data = kmemdup(info->dtb_begin, size, GFP_KERNEL);
2157 if (!info->data) {
2158 pr_err("unable to allocate memory for data, %d\n", onum);
2159 return 0;
2160 }
2161
2162 of_fdt_unflatten_tree(info->data, NULL, &info->np_overlay);
2163 if (!info->np_overlay) {
2164 pr_err("unable to unflatten overlay, %d\n", onum);
2165 ret = 0;
2166 goto out_free_data;
2167 }
2168
2169 info->overlay_id = 0;
2170 ret = of_overlay_apply(info->np_overlay, &info->overlay_id);
2171 if (ret < 0) {
2172 pr_err("of_overlay_apply() (ret=%d), %d\n", ret, onum);
2173 goto out_free_np_overlay;
2174 }
2175
2176 pr_debug("__dtb_overlay_begin applied, overlay id %d\n", ret);
2177
2178 goto out;
2179
2180out_free_np_overlay:
2181 /*
2182 * info->np_overlay is the unflattened device tree
2183 * It has not been spliced into the live tree.
2184 */
2185
2186 /* todo: function to free unflattened device tree */
2187 2169
2188out_free_data: 2170 pr_debug("%s applied\n", overlay_name);
2189 kfree(info->data);
2190 2171
2191out: 2172out:
2173 if (ret != info->expected_result)
2174 pr_err("of_overlay_fdt_apply() expected %d, ret=%d, %s\n",
2175 info->expected_result, ret, overlay_name);
2176
2192 return (ret == info->expected_result); 2177 return (ret == info->expected_result);
2193} 2178}
2194 2179
@@ -2290,18 +2275,29 @@ static __init void of_unittest_overlay_high_level(void)
2290 __of_attach_node_sysfs(np); 2275 __of_attach_node_sysfs(np);
2291 2276
2292 if (of_symbols) { 2277 if (of_symbols) {
2278 struct property *new_prop;
2293 for_each_property_of_node(overlay_base_symbols, prop) { 2279 for_each_property_of_node(overlay_base_symbols, prop) {
2294 ret = __of_add_property(of_symbols, prop); 2280
2281 new_prop = __of_prop_dup(prop, GFP_KERNEL);
2282 if (!new_prop) {
2283 unittest(0, "__of_prop_dup() of '%s' from overlay_base node __symbols__",
2284 prop->name);
2285 goto err_unlock;
2286 }
2287 ret = __of_add_property(of_symbols, new_prop);
2295 if (ret) { 2288 if (ret) {
2296 unittest(0, 2289 if (!strcmp(new_prop->name, "name")) {
2297 "duplicate property '%s' in overlay_base node __symbols__", 2290 /* auto-generated by unflatten */
2291 ret = 0;
2292 continue;
2293 }
2294 unittest(0, "duplicate property '%s' in overlay_base node __symbols__",
2298 prop->name); 2295 prop->name);
2299 goto err_unlock; 2296 goto err_unlock;
2300 } 2297 }
2301 ret = __of_add_property_sysfs(of_symbols, prop); 2298 ret = __of_add_property_sysfs(of_symbols, new_prop);
2302 if (ret) { 2299 if (ret) {
2303 unittest(0, 2300 unittest(0, "unable to add property '%s' in overlay_base node __symbols__ to sysfs",
2304 "unable to add property '%s' in overlay_base node __symbols__ to sysfs",
2305 prop->name); 2301 prop->name);
2306 goto err_unlock; 2302 goto err_unlock;
2307 } 2303 }
@@ -2313,13 +2309,13 @@ static __init void of_unittest_overlay_high_level(void)
2313 2309
2314 /* now do the normal overlay usage test */ 2310 /* now do the normal overlay usage test */
2315 2311
2316 unittest(overlay_data_add(1), 2312 unittest(overlay_data_apply("overlay", NULL),
2317 "Adding overlay 'overlay' failed\n"); 2313 "Adding overlay 'overlay' failed\n");
2318 2314
2319 unittest(overlay_data_add(2), 2315 unittest(overlay_data_apply("overlay_bad_phandle", NULL),
2320 "Adding overlay 'overlay_bad_phandle' failed\n"); 2316 "Adding overlay 'overlay_bad_phandle' failed\n");
2321 2317
2322 unittest(overlay_data_add(3), 2318 unittest(overlay_data_apply("overlay_bad_symbol", NULL),
2323 "Adding overlay 'overlay_bad_symbol' failed\n"); 2319 "Adding overlay 'overlay_bad_symbol' failed\n");
2324 2320
2325 return; 2321 return;
diff --git a/include/linux/of.h b/include/linux/of.h
index da1ee95241c1..ebf22dd0860c 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -1359,8 +1359,8 @@ struct of_overlay_notify_data {
1359 1359
1360#ifdef CONFIG_OF_OVERLAY 1360#ifdef CONFIG_OF_OVERLAY
1361 1361
1362/* ID based overlays; the API for external users */ 1362int of_overlay_fdt_apply(const void *overlay_fdt, u32 overlay_fdt_size,
1363int of_overlay_apply(struct device_node *tree, int *ovcs_id); 1363 int *ovcs_id);
1364int of_overlay_remove(int *ovcs_id); 1364int of_overlay_remove(int *ovcs_id);
1365int of_overlay_remove_all(void); 1365int of_overlay_remove_all(void);
1366 1366
@@ -1369,7 +1369,7 @@ int of_overlay_notifier_unregister(struct notifier_block *nb);
1369 1369
1370#else 1370#else
1371 1371
1372static inline int of_overlay_apply(struct device_node *tree, int *ovcs_id) 1372static inline int of_overlay_fdt_apply(void *overlay_fdt, int *ovcs_id)
1373{ 1373{
1374 return -ENOTSUPP; 1374 return -ENOTSUPP;
1375} 1375}