1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/ 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 22static struct list_head dss_conv_list __initdata; 23 24static const char prefix[] __initconst = "omapdss,"; 25 26struct dss_conv_node { 27 struct list_head list; 28 struct device_node *node; 29 bool root; 30}; 31 32static 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 44static 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 60static 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," */ 80static 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 104 omapdss_prefix_strcpy(new_compat, new_len, prop->value, prop->length); 105 106 omapdss_update_prop(node, new_compat, new_len); 107} 108 109static void __init omapdss_add_to_list(struct device_node *node, bool root) 110{ 111 struct dss_conv_node *n = kmalloc(sizeof(*n), GFP_KERNEL); 112 if (n) { 113 n->node = node; 114 n->root = root; 115 list_add(&n->list, &dss_conv_list); 116 } 117} 118 119static bool __init omapdss_list_contains(const struct device_node *node) 120{ 121 struct dss_conv_node *n; 122 123 list_for_each_entry(n, &dss_conv_list, list) { 124 if (n->node == node) 125 return true; 126 } 127 128 return false; 129} 130 131static void __init omapdss_walk_device(struct device_node *node, bool root) 132{ 133 struct device_node *n; 134 135 omapdss_add_to_list(node, root); 136 137 /* 138 * of_graph_get_remote_port_parent() prints an error if there is no 139 * port/ports node. To avoid that, check first that there's the node. 140 */ 141 n = of_get_child_by_name(node, "ports"); 142 if (!n) 143 n = of_get_child_by_name(node, "port"); 144 if (!n) 145 return; 146 147 of_node_put(n); 148 149 n = NULL; 150 while ((n = of_graph_get_next_endpoint(node, n)) != NULL) { 151 struct device_node *pn; 152 153 pn = of_graph_get_remote_port_parent(n); 154 155 if (!pn) 156 continue; 157 158 if (!of_device_is_available(pn) || omapdss_list_contains(pn)) { 159 of_node_put(pn); 160 continue; 161 } 162 163 omapdss_walk_device(pn, false); 164 } 165} 166 167static const struct of_device_id omapdss_of_match[] __initconst = { 168 { .compatible = "ti,omap2-dss", }, 169 { .compatible = "ti,omap3-dss", }, 170 { .compatible = "ti,omap4-dss", }, 171 { .compatible = "ti,omap5-dss", }, 172 { .compatible = "ti,dra7-dss", }, 173 {}, 174}; 175 176static const struct of_device_id omapdss_of_fixups_whitelist[] __initconst = { 177 { .compatible = "panel-dsi-cm" }, 178 {}, 179}; 180 181static void __init omapdss_find_children(struct device_node *np) 182{ 183 struct device_node *child; 184 185 for_each_available_child_of_node(np, child) { 186 if (!of_find_property(child, "compatible", NULL)) 187 continue; 188 189 omapdss_walk_device(child, true); 190 191 if (of_device_is_compatible(child, "ti,sysc")) 192 omapdss_find_children(child); 193 } 194} 195 196static int __init omapdss_boot_init(void) 197{ 198 struct device_node *dss; 199 200 INIT_LIST_HEAD(&dss_conv_list); 201 202 dss = of_find_matching_node(NULL, omapdss_of_match); 203 204 if (dss == NULL || !of_device_is_available(dss)) 205 goto put_node; 206 207 omapdss_walk_device(dss, true); 208 omapdss_find_children(dss); 209 210 while (!list_empty(&dss_conv_list)) { 211 struct dss_conv_node *n; 212 213 n = list_first_entry(&dss_conv_list, struct dss_conv_node, 214 list); 215 216 if (of_match_node(omapdss_of_fixups_whitelist, n->node)) 217 omapdss_omapify_node(n->node); 218 219 list_del(&n->list); 220 of_node_put(n->node); 221 kfree(n); 222 } 223 224put_node: 225 of_node_put(dss); 226 return 0; 227} 228 229subsys_initcall(omapdss_boot_init); 230