1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2014 Texas Instruments 4 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> 5 */ 6 7 /* 8 * As omapdss panel drivers are omapdss specific, but we want to define the 9 * DT-data in generic manner, we convert the compatible strings of the panel and 10 * encoder nodes from "panel-foo" to "omapdss,panel-foo". This way we can have 11 * both correct DT data and omapdss specific drivers. 12 * 13 * When we get generic panel drivers to the kernel, this file will be removed. 14 */ 15 16 #include <linux/kernel.h> 17 #include <linux/of.h> 18 #include <linux/of_graph.h> 19 #include <linux/slab.h> 20 #include <linux/list.h> 21 22 static struct list_head dss_conv_list __initdata; 23 24 static const char prefix[] __initconst = "omapdss,"; 25 26 struct dss_conv_node { 27 struct list_head list; 28 struct device_node *node; 29 bool root; 30 }; 31 32 static int __init omapdss_count_strings(const struct property *prop) 33 { 34 const char *p = prop->value; 35 int l = 0, total = 0; 36 int i; 37 38 for (i = 0; total < prop->length; total += l, p += l, i++) 39 l = strlen(p) + 1; 40 41 return i; 42 } 43 44 static void __init omapdss_update_prop(struct device_node *node, char *compat, 45 int len) 46 { 47 struct property *prop; 48 49 prop = kzalloc_obj(*prop); 50 if (!prop) 51 return; 52 53 prop->name = "compatible"; 54 prop->value = compat; 55 prop->length = len; 56 57 of_update_property(node, prop); 58 } 59 60 static void __init omapdss_prefix_strcpy(char *dst, int dst_len, 61 const char *src, int src_len) 62 { 63 size_t total = 0; 64 65 while (total < src_len) { 66 size_t l = strlen(src) + 1; 67 68 strcpy(dst, prefix); 69 dst += strlen(prefix); 70 71 strcpy(dst, src); 72 dst += l; 73 74 src += l; 75 total += l; 76 } 77 } 78 79 /* prepend compatible property strings with "omapdss," */ 80 static void __init omapdss_omapify_node(struct device_node *node) 81 { 82 struct property *prop; 83 char *new_compat; 84 int num_strs; 85 int new_len; 86 87 prop = of_find_property(node, "compatible", NULL); 88 89 if (!prop || !prop->value) 90 return; 91 92 if (strnlen(prop->value, prop->length) >= prop->length) 93 return; 94 95 /* is it already prefixed? */ 96 if (strncmp(prefix, prop->value, strlen(prefix)) == 0) 97 return; 98 99 num_strs = omapdss_count_strings(prop); 100 101 new_len = prop->length + strlen(prefix) * num_strs; 102 new_compat = kmalloc(new_len, GFP_KERNEL); 103 if (!new_compat) 104 return; 105 106 omapdss_prefix_strcpy(new_compat, new_len, prop->value, prop->length); 107 108 omapdss_update_prop(node, new_compat, new_len); 109 } 110 111 static void __init omapdss_add_to_list(struct device_node *node, bool root) 112 { 113 struct dss_conv_node *n = kmalloc_obj(struct dss_conv_node); 114 if (n) { 115 n->node = node; 116 n->root = root; 117 list_add(&n->list, &dss_conv_list); 118 } 119 } 120 121 static bool __init omapdss_list_contains(const struct device_node *node) 122 { 123 struct dss_conv_node *n; 124 125 list_for_each_entry(n, &dss_conv_list, list) { 126 if (n->node == node) 127 return true; 128 } 129 130 return false; 131 } 132 133 static void __init omapdss_walk_device(struct device_node *node, bool root) 134 { 135 struct device_node *n; 136 137 omapdss_add_to_list(node, root); 138 139 /* 140 * of_graph_get_remote_port_parent() prints an error if there is no 141 * port/ports node. To avoid that, check first that there's the node. 142 */ 143 n = of_get_child_by_name(node, "ports"); 144 if (!n) 145 n = of_get_child_by_name(node, "port"); 146 if (!n) 147 return; 148 149 of_node_put(n); 150 151 for_each_endpoint_of_node(node, n) { 152 struct device_node *pn; 153 154 pn = of_graph_get_remote_port_parent(n); 155 156 if (!pn) 157 continue; 158 159 if (!of_device_is_available(pn) || omapdss_list_contains(pn)) { 160 of_node_put(pn); 161 continue; 162 } 163 164 omapdss_walk_device(pn, false); 165 } 166 } 167 168 static const struct of_device_id omapdss_of_match[] __initconst = { 169 { .compatible = "ti,omap2-dss", }, 170 { .compatible = "ti,omap3-dss", }, 171 { .compatible = "ti,omap4-dss", }, 172 { .compatible = "ti,omap5-dss", }, 173 { .compatible = "ti,dra7-dss", }, 174 {}, 175 }; 176 177 static int __init omapdss_boot_init(void) 178 { 179 struct device_node *dss, *child; 180 181 INIT_LIST_HEAD(&dss_conv_list); 182 183 dss = of_find_matching_node(NULL, omapdss_of_match); 184 185 if (dss == NULL || !of_device_is_available(dss)) { 186 of_node_put(dss); 187 return 0; 188 } 189 190 omapdss_walk_device(dss, true); 191 192 for_each_available_child_of_node(dss, child) { 193 if (!of_property_present(child, "compatible")) 194 continue; 195 196 omapdss_walk_device(child, true); 197 } 198 199 while (!list_empty(&dss_conv_list)) { 200 struct dss_conv_node *n; 201 202 n = list_first_entry(&dss_conv_list, struct dss_conv_node, 203 list); 204 205 if (!n->root) 206 omapdss_omapify_node(n->node); 207 208 list_del(&n->list); 209 of_node_put(n->node); 210 kfree(n); 211 } 212 213 return 0; 214 } 215 216 subsys_initcall(omapdss_boot_init); 217