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(sizeof(*prop), GFP_KERNEL); 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(sizeof(struct dss_conv_node), 114 GFP_KERNEL); 115 if (n) { 116 n->node = node; 117 n->root = root; 118 list_add(&n->list, &dss_conv_list); 119 } 120 } 121 122 static bool __init omapdss_list_contains(const struct device_node *node) 123 { 124 struct dss_conv_node *n; 125 126 list_for_each_entry(n, &dss_conv_list, list) { 127 if (n->node == node) 128 return true; 129 } 130 131 return false; 132 } 133 134 static void __init omapdss_walk_device(struct device_node *node, bool root) 135 { 136 struct device_node *n; 137 138 omapdss_add_to_list(node, root); 139 140 /* 141 * of_graph_get_remote_port_parent() prints an error if there is no 142 * port/ports node. To avoid that, check first that there's the node. 143 */ 144 n = of_get_child_by_name(node, "ports"); 145 if (!n) 146 n = of_get_child_by_name(node, "port"); 147 if (!n) 148 return; 149 150 of_node_put(n); 151 152 n = NULL; 153 while ((n = of_graph_get_next_endpoint(node, n)) != NULL) { 154 struct device_node *pn; 155 156 pn = of_graph_get_remote_port_parent(n); 157 158 if (!pn) 159 continue; 160 161 if (!of_device_is_available(pn) || omapdss_list_contains(pn)) { 162 of_node_put(pn); 163 continue; 164 } 165 166 omapdss_walk_device(pn, false); 167 } 168 } 169 170 static const struct of_device_id omapdss_of_match[] __initconst = { 171 { .compatible = "ti,omap2-dss", }, 172 { .compatible = "ti,omap3-dss", }, 173 { .compatible = "ti,omap4-dss", }, 174 { .compatible = "ti,omap5-dss", }, 175 { .compatible = "ti,dra7-dss", }, 176 {}, 177 }; 178 179 static int __init omapdss_boot_init(void) 180 { 181 struct device_node *dss, *child; 182 183 INIT_LIST_HEAD(&dss_conv_list); 184 185 dss = of_find_matching_node(NULL, omapdss_of_match); 186 187 if (dss == NULL || !of_device_is_available(dss)) { 188 of_node_put(dss); 189 return 0; 190 } 191 192 omapdss_walk_device(dss, true); 193 194 for_each_available_child_of_node(dss, child) { 195 if (!of_find_property(child, "compatible", NULL)) 196 continue; 197 198 omapdss_walk_device(child, true); 199 } 200 201 while (!list_empty(&dss_conv_list)) { 202 struct dss_conv_node *n; 203 204 n = list_first_entry(&dss_conv_list, struct dss_conv_node, 205 list); 206 207 if (!n->root) 208 omapdss_omapify_node(n->node); 209 210 list_del(&n->list); 211 of_node_put(n->node); 212 kfree(n); 213 } 214 215 return 0; 216 } 217 218 subsys_initcall(omapdss_boot_init); 219