2 * Copyright (C) 2015 Texas Instruments
3 * Author: Jyri Sarha <jsarha@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.
12 * To support the old "ti,tilcdc,slave" binding the binding has to be
13 * transformed to the new external encoder binding.
16 #include <linux/kernel.h>
18 #include <linux/of_graph.h>
19 #include <linux/of_fdt.h>
20 #include <linux/slab.h>
21 #include <linux/list.h>
23 #include "tilcdc_slave_compat.h"
31 static int __init
kfree_table_init(struct kfree_table
*kft
)
35 kft
->table
= kmalloc(kft
->total
* sizeof(*kft
->table
),
43 static int __init
kfree_table_add(struct kfree_table
*kft
, void *p
)
45 if (kft
->num
== kft
->total
) {
46 void **old
= kft
->table
;
49 kft
->table
= krealloc(old
, kft
->total
* sizeof(*kft
->table
),
57 kft
->table
[kft
->num
++] = p
;
61 static void __init
kfree_table_free(struct kfree_table
*kft
)
65 for (i
= 0; i
< kft
->num
; i
++)
72 struct property
* __init
tilcdc_prop_dup(const struct property
*prop
,
73 struct kfree_table
*kft
)
75 struct property
*nprop
;
77 nprop
= kzalloc(sizeof(*nprop
), GFP_KERNEL
);
78 if (!nprop
|| kfree_table_add(kft
, nprop
))
81 nprop
->name
= kstrdup(prop
->name
, GFP_KERNEL
);
82 if (!nprop
->name
|| kfree_table_add(kft
, nprop
->name
))
85 nprop
->value
= kmemdup(prop
->value
, prop
->length
, GFP_KERNEL
);
86 if (!nprop
->value
|| kfree_table_add(kft
, nprop
->value
))
89 nprop
->length
= prop
->length
;
94 static void __init
tilcdc_copy_props(struct device_node
*from
,
95 struct device_node
*to
,
96 const char * const props
[],
97 struct kfree_table
*kft
)
99 struct property
*prop
;
102 for (i
= 0; props
[i
]; i
++) {
103 prop
= of_find_property(from
, props
[i
], NULL
);
107 prop
= tilcdc_prop_dup(prop
, kft
);
111 prop
->next
= to
->properties
;
112 to
->properties
= prop
;
116 static int __init
tilcdc_prop_str_update(struct property
*prop
,
118 struct kfree_table
*kft
)
120 prop
->value
= kstrdup(str
, GFP_KERNEL
);
121 if (kfree_table_add(kft
, prop
->value
) || !prop
->value
)
123 prop
->length
= strlen(str
)+1;
127 static void __init
tilcdc_node_disable(struct device_node
*node
)
129 struct property
*prop
;
131 prop
= kzalloc(sizeof(*prop
), GFP_KERNEL
);
135 prop
->name
= "status";
136 prop
->value
= "disabled";
137 prop
->length
= strlen((char *)prop
->value
)+1;
139 of_update_property(node
, prop
);
142 struct device_node
* __init
tilcdc_get_overlay(struct kfree_table
*kft
)
144 const int size
= __dtb_tilcdc_slave_compat_end
-
145 __dtb_tilcdc_slave_compat_begin
;
146 static void *overlay_data
;
147 struct device_node
*overlay
;
151 pr_warn("%s: No overlay data\n", __func__
);
155 overlay_data
= kmemdup(__dtb_tilcdc_slave_compat_begin
,
157 if (!overlay_data
|| kfree_table_add(kft
, overlay_data
))
160 of_fdt_unflatten_tree(overlay_data
, &overlay
);
162 pr_warn("%s: Unfattening overlay tree failed\n", __func__
);
166 of_node_set_flag(overlay
, OF_DETACHED
);
167 ret
= of_resolve_phandles(overlay
);
169 pr_err("%s: Failed to resolve phandles: %d\n", __func__
, ret
);
176 static const struct of_device_id tilcdc_slave_of_match
[] __initconst
= {
177 { .compatible
= "ti,tilcdc,slave", },
181 static const struct of_device_id tilcdc_of_match
[] __initconst
= {
182 { .compatible
= "ti,am33xx-tilcdc", },
186 static const struct of_device_id tilcdc_tda998x_of_match
[] __initconst
= {
187 { .compatible
= "nxp,tda998x", },
191 static const char * const tilcdc_slave_props
[] __initconst
= {
198 void __init
tilcdc_convert_slave_node(void)
200 struct device_node
*slave
= NULL
, *lcdc
= NULL
;
201 struct device_node
*i2c
= NULL
, *fragment
= NULL
;
202 struct device_node
*overlay
, *encoder
;
203 struct property
*prop
;
204 /* For all memory needed for the overlay tree. This memory can
205 be freed after the overlay has been applied. */
206 struct kfree_table kft
;
209 if (kfree_table_init(&kft
))
212 lcdc
= of_find_matching_node(NULL
, tilcdc_of_match
);
213 slave
= of_find_matching_node(NULL
, tilcdc_slave_of_match
);
215 if (!slave
|| !of_device_is_available(lcdc
))
218 i2c
= of_parse_phandle(slave
, "i2c", 0);
220 pr_err("%s: Can't find i2c node trough phandle\n", __func__
);
224 overlay
= tilcdc_get_overlay(&kft
);
228 encoder
= of_find_matching_node(overlay
, tilcdc_tda998x_of_match
);
230 pr_err("%s: Failed to find tda998x node\n", __func__
);
234 tilcdc_copy_props(slave
, encoder
, tilcdc_slave_props
, &kft
);
236 for_each_child_of_node(overlay
, fragment
) {
237 prop
= of_find_property(fragment
, "target-path", NULL
);
240 if (!strncmp("i2c", (char *)prop
->value
, prop
->length
))
241 if (tilcdc_prop_str_update(prop
, i2c
->full_name
, &kft
))
243 if (!strncmp("lcdc", (char *)prop
->value
, prop
->length
))
244 if (tilcdc_prop_str_update(prop
, lcdc
->full_name
, &kft
))
248 tilcdc_node_disable(slave
);
250 ret
= of_overlay_create(overlay
);
252 pr_err("%s: Creating overlay failed: %d\n", __func__
, ret
);
254 pr_info("%s: ti,tilcdc,slave node successfully converted\n",
257 kfree_table_free(&kft
);
261 of_node_put(fragment
);
264 int __init
tilcdc_slave_compat_init(void)
266 tilcdc_convert_slave_node();
270 subsys_initcall(tilcdc_slave_compat_init
);