diff options
author | Dave Airlie <airlied@redhat.com> | 2018-03-08 19:22:30 -0500 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2018-03-08 19:22:30 -0500 |
commit | 2ec360bbc319903f878135adeed85bb648ef95cf (patch) | |
tree | 797b19e8d05c6afa29690d9445c057105b0b6eef | |
parent | f073d78eeb8efd85718e611c15f9a78647751dea (diff) | |
parent | 77f59f895da2fe5526073181c74c3fb85a7c80d1 (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()
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 @@ | |||
1 | Renesas R-Car LVDS Encoder | ||
2 | ========================== | ||
3 | |||
4 | These DT bindings describe the LVDS encoder embedded in the Renesas R-Car | ||
5 | Gen2, R-Car Gen3 and RZ/G SoCs. | ||
6 | |||
7 | Required 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 | |||
23 | Required nodes: | ||
24 | |||
25 | The LVDS encoder has two video ports. Their connections are modelled using the | ||
26 | OF 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 | |||
31 | Each port shall have a single endpoint. | ||
32 | |||
33 | |||
34 | Example: | ||
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 | ||
68 | Example: R8A7795 (R-Car H3) ES2.0 DU | 66 | Example: 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 | ||
88 | The API is quite easy to use. | 88 | The API is quite easy to use. |
89 | 89 | ||
90 | 1. Call of_overlay_apply() to create and apply an overlay changeset. The return | 90 | 1. Call of_overlay_fdt_apply() to create and apply an overlay changeset. The |
91 | value is an error or a cookie identifying this overlay. | 91 | return value is an error or a cookie identifying this overlay. |
92 | 92 | ||
93 | 2. Call of_overlay_remove() to remove and cleanup the overlay changeset | 93 | 2. Call of_overlay_remove() to remove and cleanup the overlay changeset |
94 | previously created via the call to of_overlay_apply(). Removal of an overlay | 94 | previously 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/ | |||
4744 | F: drivers/gpu/drm/shmobile/ | 4744 | F: drivers/gpu/drm/shmobile/ |
4745 | F: include/linux/platform_data/shmob_drm.h | 4745 | F: include/linux/platform_data/shmob_drm.h |
4746 | F: Documentation/devicetree/bindings/display/bridge/renesas,dw-hdmi.txt | 4746 | F: Documentation/devicetree/bindings/display/bridge/renesas,dw-hdmi.txt |
4747 | F: Documentation/devicetree/bindings/display/bridge/renesas,lvds.txt | ||
4747 | F: Documentation/devicetree/bindings/display/renesas,du.txt | 4748 | F: Documentation/devicetree/bindings/display/renesas,du.txt |
4748 | 4749 | ||
4749 | DRM DRIVERS FOR ROCKCHIP | 4750 | DRM 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 |
263 | static void __init x86_flattree_get_config(void) | 263 | static 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 | ||
21 | config DRM_RCAR_LVDS | 21 | config 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 | ||
10 | rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_lvdsenc.o | 9 | rcar-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 | ||
12 | rcar-du-drm-$(CONFIG_DRM_RCAR_VSP) += rcar_du_vsp.o | 15 | rcar-du-drm-$(CONFIG_DRM_RCAR_VSP) += rcar_du_vsp.o |
13 | 16 | ||
14 | obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o | 17 | obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o |
15 | obj-$(CONFIG_DRM_RCAR_DW_HDMI) += rcar_dw_hdmi.o | 18 | obj-$(CONFIG_DRM_RCAR_DW_HDMI) += rcar_dw_hdmi.o |
19 | obj-$(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 | ||
80 | static const struct rcar_du_device_info rcar_du_r8a7779_info = { | 80 | static 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 | ||
101 | static const struct rcar_du_device_info rcar_du_r8a7790_info = { | 100 | static 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 | ||
170 | static const struct rcar_du_device_info rcar_du_r8a7794_info = { | 168 | static 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 | ||
192 | static const struct rcar_du_device_info rcar_du_r8a7795_info = { | 189 | static 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 | ||
249 | static 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 | |||
252 | static const struct of_device_id rcar_du_of_table[] = { | 269 | static 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 | ||
437 | module_platform_driver(rcar_du_platform_driver); | 455 | static 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 | } | ||
461 | module_init(rcar_du_init); | ||
462 | |||
463 | static void __exit rcar_du_exit(void) | ||
464 | { | ||
465 | platform_driver_unregister(&rcar_du_platform_driver); | ||
466 | } | ||
467 | module_exit(rcar_du_exit); | ||
438 | 468 | ||
439 | MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); | 469 | MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); |
440 | MODULE_DESCRIPTION("Renesas R-Car Display Unit DRM Driver"); | 470 | MODULE_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; | |||
26 | struct drm_device; | 26 | struct drm_device; |
27 | struct drm_fbdev_cma; | 27 | struct drm_fbdev_cma; |
28 | struct rcar_du_device; | 28 | struct rcar_du_device; |
29 | struct 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 | ||
76 | struct rcar_du_device { | 73 | struct 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 | ||
103 | static inline bool rcar_du_has(struct rcar_du_device *rcdu, | 98 | static 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 | ||
31 | static 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 | |||
44 | static 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 | |||
57 | static 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 | |||
100 | static void rcar_du_encoder_mode_set(struct drm_encoder *encoder, | 29 | static 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 | ||
147 | static const struct drm_encoder_helper_funcs encoder_helper_funcs = { | 38 | static 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 | ||
154 | static const struct drm_encoder_funcs encoder_funcs = { | 42 | static 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 | ||
235 | done: | 90 | done: |
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 | ||
20 | struct drm_panel; | 20 | struct drm_panel; |
21 | struct rcar_du_device; | 21 | struct rcar_du_device; |
22 | struct rcar_du_lvdsenc; | ||
23 | 22 | ||
24 | struct rcar_du_encoder { | 23 | struct 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 | ||
36 | struct 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 | |||
45 | int rcar_du_encoder_init(struct rcar_du_device *rcdu, | 33 | int 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 | |||
29 | static 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 | |||
36 | static const struct drm_connector_helper_funcs connector_helper_funcs = { | ||
37 | .get_modes = rcar_du_lvds_connector_get_modes, | ||
38 | }; | ||
39 | |||
40 | static 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 | |||
48 | static 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 | |||
56 | int 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 | |||
17 | struct rcar_du_device; | ||
18 | struct rcar_du_encoder; | ||
19 | |||
20 | int 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 | |||
25 | struct 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 | |||
37 | static void rcar_lvds_write(struct rcar_du_lvdsenc *lvds, u32 reg, u32 data) | ||
38 | { | ||
39 | iowrite32(data, lvds->mmio + reg); | ||
40 | } | ||
41 | |||
42 | static 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 | |||
54 | static 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 | |||
66 | static 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 | |||
149 | static 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 | |||
162 | int 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 | |||
175 | void 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 | |||
185 | void rcar_du_lvdsenc_set_mode(struct rcar_du_lvdsenc *lvds, | ||
186 | enum rcar_lvds_mode mode) | ||
187 | { | ||
188 | lvds->mode = mode; | ||
189 | } | ||
190 | |||
191 | static 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 | |||
213 | int 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 | |||
20 | struct rcar_drm_crtc; | ||
21 | struct rcar_du_lvdsenc; | ||
22 | |||
23 | enum 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. */ | ||
30 | enum 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) | ||
37 | int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu); | ||
38 | void rcar_du_lvdsenc_set_mode(struct rcar_du_lvdsenc *lvds, | ||
39 | enum rcar_lvds_mode mode); | ||
40 | int rcar_du_lvdsenc_enable(struct rcar_du_lvdsenc *lvds, | ||
41 | struct drm_crtc *crtc, bool enable); | ||
42 | void rcar_du_lvdsenc_atomic_check(struct rcar_du_lvdsenc *lvds, | ||
43 | struct drm_display_mode *mode); | ||
44 | #else | ||
45 | static inline int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu) | ||
46 | { | ||
47 | return 0; | ||
48 | } | ||
49 | static inline void rcar_du_lvdsenc_set_mode(struct rcar_du_lvdsenc *lvds, | ||
50 | enum rcar_lvds_mode mode) | ||
51 | { | ||
52 | } | ||
53 | static inline int rcar_du_lvdsenc_enable(struct rcar_du_lvdsenc *lvds, | ||
54 | struct drm_crtc *crtc, bool enable) | ||
55 | { | ||
56 | return 0; | ||
57 | } | ||
58 | static 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 | |||
26 | struct 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 | |||
43 | static 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 | |||
65 | static 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 | |||
93 | out_err: | ||
94 | kfree(prop->value); | ||
95 | kfree(prop->name); | ||
96 | kfree(prop); | ||
97 | return ret; | ||
98 | } | ||
99 | |||
100 | /* ----------------------------------------------------------------------------- | ||
101 | * LVDS Overlays | ||
102 | */ | ||
103 | |||
104 | RCAR_DU_OF_DTB(lvds, r8a7790); | ||
105 | RCAR_DU_OF_DTB(lvds, r8a7791); | ||
106 | RCAR_DU_OF_DTB(lvds, r8a7793); | ||
107 | RCAR_DU_OF_DTB(lvds, r8a7795); | ||
108 | RCAR_DU_OF_DTB(lvds, r8a7796); | ||
109 | |||
110 | static 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 | |||
119 | static struct of_changeset rcar_du_lvds_changeset; | ||
120 | |||
121 | static 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 | |||
182 | done: | ||
183 | if (ret < 0) | ||
184 | of_changeset_destroy(&rcar_du_lvds_changeset); | ||
185 | } | ||
186 | |||
187 | struct 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 | |||
194 | static 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 | |||
308 | done: | ||
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 | |||
319 | void __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 | |||
12 | struct of_device_id; | ||
13 | |||
14 | #ifdef CONFIG_DRM_RCAR_LVDS | ||
15 | void __init rcar_du_of_init(const struct of_device_id *of_ids); | ||
16 | #else | ||
17 | static 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. */ | ||
28 | enum 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 | |||
39 | struct rcar_lvds_device_info { | ||
40 | unsigned int gen; | ||
41 | unsigned int quirks; | ||
42 | }; | ||
43 | |||
44 | struct 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 | |||
68 | static 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 | |||
77 | static 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 | |||
84 | static 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 | |||
114 | static 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 | |||
119 | static 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 | |||
131 | static 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 | |||
143 | static 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 | |||
155 | static 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 | |||
251 | static 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 | |||
270 | static 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 | |||
283 | static 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 | |||
320 | static 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 | |||
333 | static 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 | |||
360 | static 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 | |||
368 | static 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 | |||
381 | static 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 | |||
438 | done: | ||
439 | of_node_put(local_output); | ||
440 | of_node_put(remote_input); | ||
441 | of_node_put(remote); | ||
442 | |||
443 | return ret; | ||
444 | } | ||
445 | |||
446 | static 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 | |||
486 | static 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 | |||
495 | static const struct rcar_lvds_device_info rcar_lvds_gen2_info = { | ||
496 | .gen = 2, | ||
497 | .quirks = RCAR_LVDS_QUIRK_GEN2_PLLCR, | ||
498 | }; | ||
499 | |||
500 | static 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 | |||
505 | static const struct rcar_lvds_device_info rcar_lvds_gen3_info = { | ||
506 | .gen = 3, | ||
507 | }; | ||
508 | |||
509 | static 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 | |||
514 | static 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 | |||
525 | MODULE_DEVICE_TABLE(of, rcar_lvds_of_table); | ||
526 | |||
527 | static 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 | |||
536 | module_platform_driver(rcar_lvds_platform_driver); | ||
537 | |||
538 | MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); | ||
539 | MODULE_DESCRIPTION("Renesas R-Car LVDS Encoder Driver"); | ||
540 | MODULE_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 | |||
92 | config OF_OVERLAY | 92 | config 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 { | |||
43 | struct overlay_changeset { | 47 | struct 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 | */ |
484 | static struct device_node *find_target_node(struct device_node *info_node) | 489 | static 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 | */ |
516 | static int init_overlay_changeset(struct overlay_changeset *ovcs, | 532 | static 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 | ||
688 | int of_overlay_apply(struct device_node *tree, int *ovcs_id) | 719 | static 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 | ||
801 | err_free_tree: | ||
802 | kfree(fdt); | ||
803 | kfree(tree); | ||
804 | |||
757 | err_free_overlay_changeset: | 805 | err_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 | } |
769 | EXPORT_SYMBOL_GPL(of_overlay_apply); | 817 | |
818 | int 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 | |||
867 | out_free_new_fdt: | ||
868 | kfree(new_fdt); | ||
869 | |||
870 | out: | ||
871 | return ret; | ||
872 | } | ||
873 | EXPORT_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 |
2 | DTC_FLAGS_testcases := -Wno-interrupts_property | ||
3 | obj-y += testcases.dtb.o | 2 | obj-y += testcases.dtb.o |
4 | 3 | ||
5 | obj-$(CONFIG_OF_OVERLAY) += overlay.dtb.o \ | 4 | obj-$(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 \ | |||
10 | targets += $(foreach suffix, dtb dtb.S, $(patsubst %.dtb.o,%.$(suffix),$(obj-y))) | 24 | targets += $(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 |
13 | DTC_FLAGS_overlay := -@ | 27 | DTC_FLAGS_overlay += -@ |
14 | DTC_FLAGS_overlay_bad_phandle := -@ | 28 | DTC_FLAGS_overlay_bad_phandle += -@ |
15 | DTC_FLAGS_overlay_bad_symbol := -@ | 29 | DTC_FLAGS_overlay_bad_symbol += -@ |
16 | DTC_FLAGS_overlay_base := -@ | 30 | DTC_FLAGS_overlay_base += -@ |
31 | DTC_FLAGS_testcases += -@ | ||
32 | |||
33 | # suppress warnings about intentional errors | ||
34 | DTC_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 | ||
48 | static int __init overlay_data_apply(const char *overlay_name, int *overlay_id); | ||
49 | |||
48 | static void __init of_unittest_find_node_by_name(void) | 50 | static 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 | ||
1194 | static const char *overlay_path(int nr) | 1195 | static 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 | ||
1266 | static int of_unittest_apply_overlay(int overlay_nr, int unittest_nr, | 1267 | static 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 */ |
1298 | static int of_unittest_apply_overlay_check(int overlay_nr, int unittest_nr, | 1293 | static 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 */ |
1332 | static int of_unittest_apply_revert_overlay_check(int overlay_nr, | 1328 | static 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 */ |
1385 | static void of_unittest_overlay_0(void) | 1381 | static 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 */ |
1398 | static void of_unittest_overlay_1(void) | 1394 | static 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 */ |
1411 | static void of_unittest_overlay_2(void) | 1407 | static 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 */ |
1424 | static void of_unittest_overlay_3(void) | 1420 | static 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 */ |
1437 | static void of_unittest_overlay_4(void) | 1433 | static 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 */ |
1450 | static void of_unittest_overlay_5(void) | 1446 | static 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 */ |
1463 | static void of_unittest_overlay_6(void) | 1459 | static 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 */ |
1547 | static void of_unittest_overlay_8(void) | 1537 | static 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 */ |
1605 | static void of_unittest_overlay_10(void) | 1589 | static 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) */ |
1628 | static void of_unittest_overlay_11(void) | 1612 | static 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 | ||
1894 | static void of_unittest_overlay_i2c_12(void) | 1878 | static 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 */ |
1907 | static void of_unittest_overlay_i2c_13(void) | 1891 | static 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 | ||
1924 | static void of_unittest_overlay_i2c_15(void) | 1908 | static 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 | ||
2032 | struct overlay_info { | 2017 | struct 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 | ||
2041 | OVERLAY_INFO_EXTERN(overlay_base); | 2025 | OVERLAY_INFO_EXTERN(overlay_base); |
2042 | OVERLAY_INFO_EXTERN(overlay); | 2026 | OVERLAY_INFO_EXTERN(overlay); |
2027 | OVERLAY_INFO_EXTERN(overlay_0); | ||
2028 | OVERLAY_INFO_EXTERN(overlay_1); | ||
2029 | OVERLAY_INFO_EXTERN(overlay_2); | ||
2030 | OVERLAY_INFO_EXTERN(overlay_3); | ||
2031 | OVERLAY_INFO_EXTERN(overlay_4); | ||
2032 | OVERLAY_INFO_EXTERN(overlay_5); | ||
2033 | OVERLAY_INFO_EXTERN(overlay_6); | ||
2034 | OVERLAY_INFO_EXTERN(overlay_7); | ||
2035 | OVERLAY_INFO_EXTERN(overlay_8); | ||
2036 | OVERLAY_INFO_EXTERN(overlay_9); | ||
2037 | OVERLAY_INFO_EXTERN(overlay_10); | ||
2038 | OVERLAY_INFO_EXTERN(overlay_11); | ||
2039 | OVERLAY_INFO_EXTERN(overlay_12); | ||
2040 | OVERLAY_INFO_EXTERN(overlay_13); | ||
2041 | OVERLAY_INFO_EXTERN(overlay_15); | ||
2043 | OVERLAY_INFO_EXTERN(overlay_bad_phandle); | 2042 | OVERLAY_INFO_EXTERN(overlay_bad_phandle); |
2044 | OVERLAY_INFO_EXTERN(overlay_bad_symbol); | 2043 | OVERLAY_INFO_EXTERN(overlay_bad_symbol); |
2045 | 2044 | ||
@@ -2047,6 +2046,21 @@ OVERLAY_INFO_EXTERN(overlay_bad_symbol); | |||
2047 | static struct overlay_info overlays[] = { | 2046 | static 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 | */ |
2125 | static int __init overlay_data_add(int onum) | 2139 | static 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 | |||
2180 | out_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 | ||
2188 | out_free_data: | 2170 | pr_debug("%s applied\n", overlay_name); |
2189 | kfree(info->data); | ||
2190 | 2171 | ||
2191 | out: | 2172 | out: |
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 */ | 1362 | int of_overlay_fdt_apply(const void *overlay_fdt, u32 overlay_fdt_size, |
1363 | int of_overlay_apply(struct device_node *tree, int *ovcs_id); | 1363 | int *ovcs_id); |
1364 | int of_overlay_remove(int *ovcs_id); | 1364 | int of_overlay_remove(int *ovcs_id); |
1365 | int of_overlay_remove_all(void); | 1365 | int 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 | ||
1372 | static inline int of_overlay_apply(struct device_node *tree, int *ovcs_id) | 1372 | static inline int of_overlay_fdt_apply(void *overlay_fdt, int *ovcs_id) |
1373 | { | 1373 | { |
1374 | return -ENOTSUPP; | 1374 | return -ENOTSUPP; |
1375 | } | 1375 | } |