2 * Copyright (C) 2014 Texas Instruments
3 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
19 * As omapdss panel drivers are omapdss specific, but we want to define the
20 * DT-data in generic manner, we convert the compatible strings of the panel and
21 * encoder nodes from "panel-foo" to "omapdss,panel-foo". This way we can have
22 * both correct DT data and omapdss specific drivers.
24 * When we get generic panel drivers to the kernel, this file will be removed.
27 #include <linux/kernel.h>
29 #include <linux/of_graph.h>
30 #include <linux/slab.h>
31 #include <linux/list.h>
33 static struct list_head dss_conv_list __initdata
;
35 static const char prefix
[] __initconst
= "omapdss,";
37 struct dss_conv_node
{
38 struct list_head list
;
39 struct device_node
*node
;
43 static int __init
omapdss_count_strings(const struct property
*prop
)
45 const char *p
= prop
->value
;
49 for (i
= 0; total
< prop
->length
; total
+= l
, p
+= l
, i
++)
55 static void __init
omapdss_update_prop(struct device_node
*node
, char *compat
,
58 struct property
*prop
;
60 prop
= kzalloc(sizeof(*prop
), GFP_KERNEL
);
64 prop
->name
= "compatible";
68 of_update_property(node
, prop
);
71 static void __init
omapdss_prefix_strcpy(char *dst
, int dst_len
,
72 const char *src
, int src_len
)
76 while (total
< src_len
) {
77 size_t l
= strlen(src
) + 1;
80 dst
+= strlen(prefix
);
90 /* prepend compatible property strings with "omapdss," */
91 static void __init
omapdss_omapify_node(struct device_node
*node
)
93 struct property
*prop
;
98 prop
= of_find_property(node
, "compatible", NULL
);
100 if (!prop
|| !prop
->value
)
103 if (strnlen(prop
->value
, prop
->length
) >= prop
->length
)
106 /* is it already prefixed? */
107 if (strncmp(prefix
, prop
->value
, strlen(prefix
)) == 0)
110 num_strs
= omapdss_count_strings(prop
);
112 new_len
= prop
->length
+ strlen(prefix
) * num_strs
;
113 new_compat
= kmalloc(new_len
, GFP_KERNEL
);
115 omapdss_prefix_strcpy(new_compat
, new_len
, prop
->value
, prop
->length
);
117 omapdss_update_prop(node
, new_compat
, new_len
);
120 static void __init
omapdss_add_to_list(struct device_node
*node
, bool root
)
122 struct dss_conv_node
*n
= kmalloc(sizeof(struct dss_conv_node
),
127 list_add(&n
->list
, &dss_conv_list
);
131 static bool __init
omapdss_list_contains(const struct device_node
*node
)
133 struct dss_conv_node
*n
;
135 list_for_each_entry(n
, &dss_conv_list
, list
) {
143 static void __init
omapdss_walk_device(struct device_node
*node
, bool root
)
145 struct device_node
*n
;
147 omapdss_add_to_list(node
, root
);
150 * of_graph_get_remote_port_parent() prints an error if there is no
151 * port/ports node. To avoid that, check first that there's the node.
153 n
= of_get_child_by_name(node
, "ports");
155 n
= of_get_child_by_name(node
, "port");
162 while ((n
= of_graph_get_next_endpoint(node
, n
)) != NULL
) {
163 struct device_node
*pn
;
165 pn
= of_graph_get_remote_port_parent(n
);
170 if (!of_device_is_available(pn
) || omapdss_list_contains(pn
)) {
175 omapdss_walk_device(pn
, false);
179 static const struct of_device_id omapdss_of_match
[] __initconst
= {
180 { .compatible
= "ti,omap2-dss", },
181 { .compatible
= "ti,omap3-dss", },
182 { .compatible
= "ti,omap4-dss", },
183 { .compatible
= "ti,omap5-dss", },
184 { .compatible
= "ti,dra7-dss", },
188 static int __init
omapdss_boot_init(void)
190 struct device_node
*dss
, *child
;
192 INIT_LIST_HEAD(&dss_conv_list
);
194 dss
= of_find_matching_node(NULL
, omapdss_of_match
);
196 if (dss
== NULL
|| !of_device_is_available(dss
))
199 omapdss_walk_device(dss
, true);
201 for_each_available_child_of_node(dss
, child
) {
202 if (!of_find_property(child
, "compatible", NULL
))
205 omapdss_walk_device(child
, true);
208 while (!list_empty(&dss_conv_list
)) {
209 struct dss_conv_node
*n
;
211 n
= list_first_entry(&dss_conv_list
, struct dss_conv_node
,
215 omapdss_omapify_node(n
->node
);
218 of_node_put(n
->node
);
225 subsys_initcall(omapdss_boot_init
);