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 for_each_endpoint_of_node(node, n) { 153 struct device_node *pn; 154 155 pn = of_graph_get_remote_port_parent(n); 156 157 if (!pn) 158 continue; 159 160 if (!of_device_is_available(pn) || omapdss_list_contains(pn)) { 161 of_node_put(pn); 162 continue; 163 } 164 165 omapdss_walk_device(pn, false); 166 } 167 } 168 169 static const struct of_device_id omapdss_of_match[] __initconst = { 170 { .compatible = "ti,omap2-dss", }, 171 { .compatible = "ti,omap3-dss", }, 172 { .compatible = "ti,omap4-dss", }, 173 { .compatible = "ti,omap5-dss", }, 174 { .compatible = "ti,dra7-dss", }, 175 {}, 176 }; 177 178 static int __init omapdss_boot_init(void) 179 { 180 struct device_node *dss, *child; 181 182 INIT_LIST_HEAD(&dss_conv_list); 183 184 dss = of_find_matching_node(NULL, omapdss_of_match); 185 186 if (dss == NULL || !of_device_is_available(dss)) { 187 of_node_put(dss); 188 return 0; 189 } 190 191 omapdss_walk_device(dss, true); 192 193 for_each_available_child_of_node(dss, child) { 194 if (!of_property_present(child, "compatible")) 195 continue; 196 197 omapdss_walk_device(child, true); 198 } 199 200 while (!list_empty(&dss_conv_list)) { 201 struct dss_conv_node *n; 202 203 n = list_first_entry(&dss_conv_list, struct dss_conv_node, 204 list); 205 206 if (!n->root) 207 omapdss_omapify_node(n->node); 208 209 list_del(&n->list); 210 of_node_put(n->node); 211 kfree(n); 212 } 213 214 return 0; 215 } 216 217 subsys_initcall(omapdss_boot_init); 218