1 // SPDX-License-Identifier: GPL-2.0
3 * rcar_du_of.c - Legacy DT bindings compatibility
5 * Copyright (C) 2018 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
7 * Based on work from Jyri Sarha <jsarha@ti.com>
8 * Copyright (C) 2015 Texas Instruments
11 #include <linux/init.h>
12 #include <linux/kernel.h>
14 #include <linux/of_address.h>
15 #include <linux/of_fdt.h>
16 #include <linux/of_graph.h>
17 #include <linux/slab.h>
19 #include "rcar_du_crtc.h"
20 #include "rcar_du_drv.h"
21 #include "rcar_du_of.h"
23 /* -----------------------------------------------------------------------------
24 * Generic Overlay Handling
27 struct rcar_du_of_overlay
{
28 const char *compatible
;
33 #define RCAR_DU_OF_DTB(type, soc) \
34 extern char __dtb_rcar_du_of_##type##_##soc##_begin[]; \
35 extern char __dtb_rcar_du_of_##type##_##soc##_end[]
37 #define RCAR_DU_OF_OVERLAY(type, soc) \
39 .compatible = "renesas,du-" #soc, \
40 .begin = __dtb_rcar_du_of_##type##_##soc##_begin, \
41 .end = __dtb_rcar_du_of_##type##_##soc##_end, \
44 static int __init
rcar_du_of_apply_overlay(const struct rcar_du_of_overlay
*dtbs
,
45 const char *compatible
)
47 const struct rcar_du_of_overlay
*dtb
= NULL
;
51 for (i
= 0; dtbs
[i
].compatible
; ++i
) {
52 if (!strcmp(dtbs
[i
].compatible
, compatible
)) {
62 return of_overlay_fdt_apply(dtb
->begin
, dtb
->end
- dtb
->begin
,
66 static int __init
rcar_du_of_add_property(struct of_changeset
*ocs
,
67 struct device_node
*np
,
68 const char *name
, const void *value
,
71 struct property
*prop
;
74 prop
= kzalloc(sizeof(*prop
), GFP_KERNEL
);
78 prop
->name
= kstrdup(name
, GFP_KERNEL
);
82 prop
->value
= kmemdup(value
, length
, GFP_KERNEL
);
86 of_property_set_flag(prop
, OF_DYNAMIC
);
88 prop
->length
= length
;
90 ret
= of_changeset_add_property(ocs
, np
, prop
);
101 /* -----------------------------------------------------------------------------
105 RCAR_DU_OF_DTB(lvds
, r8a7790
);
106 RCAR_DU_OF_DTB(lvds
, r8a7791
);
107 RCAR_DU_OF_DTB(lvds
, r8a7793
);
108 RCAR_DU_OF_DTB(lvds
, r8a7795
);
109 RCAR_DU_OF_DTB(lvds
, r8a7796
);
111 static const struct rcar_du_of_overlay rcar_du_lvds_overlays
[] __initconst
= {
112 RCAR_DU_OF_OVERLAY(lvds
, r8a7790
),
113 RCAR_DU_OF_OVERLAY(lvds
, r8a7791
),
114 RCAR_DU_OF_OVERLAY(lvds
, r8a7793
),
115 RCAR_DU_OF_OVERLAY(lvds
, r8a7795
),
116 RCAR_DU_OF_OVERLAY(lvds
, r8a7796
),
120 static struct of_changeset rcar_du_lvds_changeset
;
122 static void __init
rcar_du_of_lvds_patch_one(struct device_node
*lvds
,
123 const struct of_phandle_args
*clk
,
124 struct device_node
*local
,
125 struct device_node
*remote
)
133 * Set the LVDS clocks property. This can't be performed by the overlay
134 * as the structure of the clock specifier has changed over time, and we
135 * don't know at compile time which binding version the system we will
138 if (clk
->args_count
>= ARRAY_SIZE(value
) - 1)
141 of_changeset_init(&rcar_du_lvds_changeset
);
143 value
[0] = cpu_to_be32(clk
->np
->phandle
);
144 for (i
= 0; i
< clk
->args_count
; ++i
)
145 value
[i
+ 1] = cpu_to_be32(clk
->args
[i
]);
147 psize
= (clk
->args_count
+ 1) * 4;
148 ret
= rcar_du_of_add_property(&rcar_du_lvds_changeset
, lvds
,
149 "clocks", value
, psize
);
154 * Insert the node in the OF graph: patch the LVDS ports remote-endpoint
155 * properties to point to the endpoints of the sibling nodes in the
156 * graph. This can't be performed by the overlay: on the input side the
157 * overlay would contain a phandle for the DU LVDS output port that
158 * would clash with the system DT, and on the output side the connection
161 value
[0] = cpu_to_be32(local
->phandle
);
162 value
[1] = cpu_to_be32(remote
->phandle
);
164 for (i
= 0; i
< 2; ++i
) {
165 struct device_node
*endpoint
;
167 endpoint
= of_graph_get_endpoint_by_regs(lvds
, i
, 0);
173 ret
= rcar_du_of_add_property(&rcar_du_lvds_changeset
,
174 endpoint
, "remote-endpoint",
175 &value
[i
], sizeof(value
[i
]));
176 of_node_put(endpoint
);
181 ret
= of_changeset_apply(&rcar_du_lvds_changeset
);
185 of_changeset_destroy(&rcar_du_lvds_changeset
);
188 struct lvds_of_data
{
190 struct of_phandle_args clkspec
;
191 struct device_node
*local
;
192 struct device_node
*remote
;
195 static void __init
rcar_du_of_lvds_patch(const struct of_device_id
*of_ids
)
197 const struct rcar_du_device_info
*info
;
198 const struct of_device_id
*match
;
199 struct lvds_of_data lvds_data
[2] = { };
200 struct device_node
*lvds_node
;
201 struct device_node
*soc_node
;
202 struct device_node
*du_node
;
204 const char *soc_name
;
208 /* Get the DU node and exit if not present or disabled. */
209 du_node
= of_find_matching_node_and_match(NULL
, of_ids
, &match
);
210 if (!du_node
|| !of_device_is_available(du_node
)) {
211 of_node_put(du_node
);
216 soc_node
= of_get_parent(du_node
);
218 if (WARN_ON(info
->num_lvds
> ARRAY_SIZE(lvds_data
)))
222 * Skip if the LVDS nodes already exists.
224 * The nodes are searched based on the compatible string, which we
225 * construct from the SoC name found in the DU compatible string. As a
226 * match has been found we know the compatible string matches the
227 * expected format and can thus skip some of the string manipulation
228 * normal safety checks.
230 soc_name
= strchr(match
->compatible
, '-') + 1;
231 sprintf(compatible
, "renesas,%s-lvds", soc_name
);
232 lvds_node
= of_find_compatible_node(NULL
, NULL
, compatible
);
234 of_node_put(lvds_node
);
239 * Parse the DU node and store the register specifier, the clock
240 * specifier and the local and remote endpoint of the LVDS link for
243 for (i
= 0; i
< info
->num_lvds
; ++i
) {
244 struct lvds_of_data
*lvds
= &lvds_data
[i
];
249 sprintf(name
, "lvds.%u", i
);
250 index
= of_property_match_string(du_node
, "clock-names", name
);
254 ret
= of_parse_phandle_with_args(du_node
, "clocks",
255 "#clock-cells", index
,
260 port
= info
->routes
[RCAR_DU_OUTPUT_LVDS0
+ i
].port
;
262 lvds
->local
= of_graph_get_endpoint_by_regs(du_node
, port
, 0);
266 lvds
->remote
= of_graph_get_remote_endpoint(lvds
->local
);
270 index
= of_property_match_string(du_node
, "reg-names", name
);
274 of_address_to_resource(du_node
, index
, &lvds
->res
);
277 /* Parse and apply the overlay. This will resolve phandles. */
278 ret
= rcar_du_of_apply_overlay(rcar_du_lvds_overlays
,
283 /* Patch the newly created LVDS encoder nodes. */
284 for_each_child_of_node(soc_node
, lvds_node
) {
287 if (!of_device_is_compatible(lvds_node
, compatible
))
290 /* Locate the lvds_data entry based on the resource start. */
291 ret
= of_address_to_resource(lvds_node
, 0, &res
);
295 for (i
= 0; i
< ARRAY_SIZE(lvds_data
); ++i
) {
296 if (lvds_data
[i
].res
.start
== res
.start
)
300 if (i
== ARRAY_SIZE(lvds_data
))
303 /* Patch the LVDS encoder. */
304 rcar_du_of_lvds_patch_one(lvds_node
, &lvds_data
[i
].clkspec
,
306 lvds_data
[i
].remote
);
310 for (i
= 0; i
< info
->num_lvds
; ++i
) {
311 of_node_put(lvds_data
[i
].clkspec
.np
);
312 of_node_put(lvds_data
[i
].local
);
313 of_node_put(lvds_data
[i
].remote
);
316 of_node_put(soc_node
);
317 of_node_put(du_node
);
320 void __init
rcar_du_of_init(const struct of_device_id
*of_ids
)
322 rcar_du_of_lvds_patch(of_ids
);