18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/ 48c2ecf20Sopenharmony_ci * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci/* 88c2ecf20Sopenharmony_ci * As omapdss panel drivers are omapdss specific, but we want to define the 98c2ecf20Sopenharmony_ci * DT-data in generic manner, we convert the compatible strings of the panel and 108c2ecf20Sopenharmony_ci * encoder nodes from "panel-foo" to "omapdss,panel-foo". This way we can have 118c2ecf20Sopenharmony_ci * both correct DT data and omapdss specific drivers. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * When we get generic panel drivers to the kernel, this file will be removed. 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/of.h> 188c2ecf20Sopenharmony_ci#include <linux/of_graph.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/list.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic struct list_head dss_conv_list __initdata; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic const char prefix[] __initconst = "omapdss,"; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistruct dss_conv_node { 278c2ecf20Sopenharmony_ci struct list_head list; 288c2ecf20Sopenharmony_ci struct device_node *node; 298c2ecf20Sopenharmony_ci bool root; 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic int __init omapdss_count_strings(const struct property *prop) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci const char *p = prop->value; 358c2ecf20Sopenharmony_ci int l = 0, total = 0; 368c2ecf20Sopenharmony_ci int i; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci for (i = 0; total < prop->length; total += l, p += l, i++) 398c2ecf20Sopenharmony_ci l = strlen(p) + 1; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci return i; 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic void __init omapdss_update_prop(struct device_node *node, char *compat, 458c2ecf20Sopenharmony_ci int len) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci struct property *prop; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci prop = kzalloc(sizeof(*prop), GFP_KERNEL); 508c2ecf20Sopenharmony_ci if (!prop) 518c2ecf20Sopenharmony_ci return; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci prop->name = "compatible"; 548c2ecf20Sopenharmony_ci prop->value = compat; 558c2ecf20Sopenharmony_ci prop->length = len; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci of_update_property(node, prop); 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic void __init omapdss_prefix_strcpy(char *dst, int dst_len, 618c2ecf20Sopenharmony_ci const char *src, int src_len) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci size_t total = 0; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci while (total < src_len) { 668c2ecf20Sopenharmony_ci size_t l = strlen(src) + 1; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci strcpy(dst, prefix); 698c2ecf20Sopenharmony_ci dst += strlen(prefix); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci strcpy(dst, src); 728c2ecf20Sopenharmony_ci dst += l; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci src += l; 758c2ecf20Sopenharmony_ci total += l; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/* prepend compatible property strings with "omapdss," */ 808c2ecf20Sopenharmony_cistatic void __init omapdss_omapify_node(struct device_node *node) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct property *prop; 838c2ecf20Sopenharmony_ci char *new_compat; 848c2ecf20Sopenharmony_ci int num_strs; 858c2ecf20Sopenharmony_ci int new_len; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci prop = of_find_property(node, "compatible", NULL); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (!prop || !prop->value) 908c2ecf20Sopenharmony_ci return; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (strnlen(prop->value, prop->length) >= prop->length) 938c2ecf20Sopenharmony_ci return; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci /* is it already prefixed? */ 968c2ecf20Sopenharmony_ci if (strncmp(prefix, prop->value, strlen(prefix)) == 0) 978c2ecf20Sopenharmony_ci return; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci num_strs = omapdss_count_strings(prop); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci new_len = prop->length + strlen(prefix) * num_strs; 1028c2ecf20Sopenharmony_ci new_compat = kmalloc(new_len, GFP_KERNEL); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci omapdss_prefix_strcpy(new_compat, new_len, prop->value, prop->length); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci omapdss_update_prop(node, new_compat, new_len); 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic void __init omapdss_add_to_list(struct device_node *node, bool root) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct dss_conv_node *n = kmalloc(sizeof(*n), GFP_KERNEL); 1128c2ecf20Sopenharmony_ci if (n) { 1138c2ecf20Sopenharmony_ci n->node = node; 1148c2ecf20Sopenharmony_ci n->root = root; 1158c2ecf20Sopenharmony_ci list_add(&n->list, &dss_conv_list); 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic bool __init omapdss_list_contains(const struct device_node *node) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct dss_conv_node *n; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci list_for_each_entry(n, &dss_conv_list, list) { 1248c2ecf20Sopenharmony_ci if (n->node == node) 1258c2ecf20Sopenharmony_ci return true; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci return false; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic void __init omapdss_walk_device(struct device_node *node, bool root) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct device_node *n; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci omapdss_add_to_list(node, root); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* 1388c2ecf20Sopenharmony_ci * of_graph_get_remote_port_parent() prints an error if there is no 1398c2ecf20Sopenharmony_ci * port/ports node. To avoid that, check first that there's the node. 1408c2ecf20Sopenharmony_ci */ 1418c2ecf20Sopenharmony_ci n = of_get_child_by_name(node, "ports"); 1428c2ecf20Sopenharmony_ci if (!n) 1438c2ecf20Sopenharmony_ci n = of_get_child_by_name(node, "port"); 1448c2ecf20Sopenharmony_ci if (!n) 1458c2ecf20Sopenharmony_ci return; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci of_node_put(n); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci n = NULL; 1508c2ecf20Sopenharmony_ci while ((n = of_graph_get_next_endpoint(node, n)) != NULL) { 1518c2ecf20Sopenharmony_ci struct device_node *pn; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci pn = of_graph_get_remote_port_parent(n); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (!pn) 1568c2ecf20Sopenharmony_ci continue; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (!of_device_is_available(pn) || omapdss_list_contains(pn)) { 1598c2ecf20Sopenharmony_ci of_node_put(pn); 1608c2ecf20Sopenharmony_ci continue; 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci omapdss_walk_device(pn, false); 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic const struct of_device_id omapdss_of_match[] __initconst = { 1688c2ecf20Sopenharmony_ci { .compatible = "ti,omap2-dss", }, 1698c2ecf20Sopenharmony_ci { .compatible = "ti,omap3-dss", }, 1708c2ecf20Sopenharmony_ci { .compatible = "ti,omap4-dss", }, 1718c2ecf20Sopenharmony_ci { .compatible = "ti,omap5-dss", }, 1728c2ecf20Sopenharmony_ci { .compatible = "ti,dra7-dss", }, 1738c2ecf20Sopenharmony_ci {}, 1748c2ecf20Sopenharmony_ci}; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic const struct of_device_id omapdss_of_fixups_whitelist[] __initconst = { 1778c2ecf20Sopenharmony_ci { .compatible = "panel-dsi-cm" }, 1788c2ecf20Sopenharmony_ci {}, 1798c2ecf20Sopenharmony_ci}; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic void __init omapdss_find_children(struct device_node *np) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct device_node *child; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci for_each_available_child_of_node(np, child) { 1868c2ecf20Sopenharmony_ci if (!of_find_property(child, "compatible", NULL)) 1878c2ecf20Sopenharmony_ci continue; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci omapdss_walk_device(child, true); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci if (of_device_is_compatible(child, "ti,sysc")) 1928c2ecf20Sopenharmony_ci omapdss_find_children(child); 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic int __init omapdss_boot_init(void) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct device_node *dss; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dss_conv_list); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci dss = of_find_matching_node(NULL, omapdss_of_match); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci if (dss == NULL || !of_device_is_available(dss)) 2058c2ecf20Sopenharmony_ci goto put_node; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci omapdss_walk_device(dss, true); 2088c2ecf20Sopenharmony_ci omapdss_find_children(dss); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci while (!list_empty(&dss_conv_list)) { 2118c2ecf20Sopenharmony_ci struct dss_conv_node *n; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci n = list_first_entry(&dss_conv_list, struct dss_conv_node, 2148c2ecf20Sopenharmony_ci list); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (of_match_node(omapdss_of_fixups_whitelist, n->node)) 2178c2ecf20Sopenharmony_ci omapdss_omapify_node(n->node); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci list_del(&n->list); 2208c2ecf20Sopenharmony_ci of_node_put(n->node); 2218c2ecf20Sopenharmony_ci kfree(n); 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ciput_node: 2258c2ecf20Sopenharmony_ci of_node_put(dss); 2268c2ecf20Sopenharmony_ci return 0; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cisubsys_initcall(omapdss_boot_init); 230