Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) 2014 Texas Instruments
0004  * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
0005  */
0006 
0007 /*
0008  * As omapdss panel drivers are omapdss specific, but we want to define the
0009  * DT-data in generic manner, we convert the compatible strings of the panel and
0010  * encoder nodes from "panel-foo" to "omapdss,panel-foo". This way we can have
0011  * both correct DT data and omapdss specific drivers.
0012  *
0013  * When we get generic panel drivers to the kernel, this file will be removed.
0014  */
0015 
0016 #include <linux/kernel.h>
0017 #include <linux/of.h>
0018 #include <linux/of_graph.h>
0019 #include <linux/slab.h>
0020 #include <linux/list.h>
0021 
0022 static struct list_head dss_conv_list __initdata;
0023 
0024 static const char prefix[] __initconst = "omapdss,";
0025 
0026 struct dss_conv_node {
0027     struct list_head list;
0028     struct device_node *node;
0029     bool root;
0030 };
0031 
0032 static int __init omapdss_count_strings(const struct property *prop)
0033 {
0034     const char *p = prop->value;
0035     int l = 0, total = 0;
0036     int i;
0037 
0038     for (i = 0; total < prop->length; total += l, p += l, i++)
0039         l = strlen(p) + 1;
0040 
0041     return i;
0042 }
0043 
0044 static void __init omapdss_update_prop(struct device_node *node, char *compat,
0045     int len)
0046 {
0047     struct property *prop;
0048 
0049     prop = kzalloc(sizeof(*prop), GFP_KERNEL);
0050     if (!prop)
0051         return;
0052 
0053     prop->name = "compatible";
0054     prop->value = compat;
0055     prop->length = len;
0056 
0057     of_update_property(node, prop);
0058 }
0059 
0060 static void __init omapdss_prefix_strcpy(char *dst, int dst_len,
0061     const char *src, int src_len)
0062 {
0063     size_t total = 0;
0064 
0065     while (total < src_len) {
0066         size_t l = strlen(src) + 1;
0067 
0068         strcpy(dst, prefix);
0069         dst += strlen(prefix);
0070 
0071         strcpy(dst, src);
0072         dst += l;
0073 
0074         src += l;
0075         total += l;
0076     }
0077 }
0078 
0079 /* prepend compatible property strings with "omapdss," */
0080 static void __init omapdss_omapify_node(struct device_node *node)
0081 {
0082     struct property *prop;
0083     char *new_compat;
0084     int num_strs;
0085     int new_len;
0086 
0087     prop = of_find_property(node, "compatible", NULL);
0088 
0089     if (!prop || !prop->value)
0090         return;
0091 
0092     if (strnlen(prop->value, prop->length) >= prop->length)
0093         return;
0094 
0095     /* is it already prefixed? */
0096     if (strncmp(prefix, prop->value, strlen(prefix)) == 0)
0097         return;
0098 
0099     num_strs = omapdss_count_strings(prop);
0100 
0101     new_len = prop->length + strlen(prefix) * num_strs;
0102     new_compat = kmalloc(new_len, GFP_KERNEL);
0103     if (!new_compat)
0104         return;
0105 
0106     omapdss_prefix_strcpy(new_compat, new_len, prop->value, prop->length);
0107 
0108     omapdss_update_prop(node, new_compat, new_len);
0109 }
0110 
0111 static void __init omapdss_add_to_list(struct device_node *node, bool root)
0112 {
0113     struct dss_conv_node *n = kmalloc(sizeof(struct dss_conv_node),
0114         GFP_KERNEL);
0115     if (n) {
0116         n->node = node;
0117         n->root = root;
0118         list_add(&n->list, &dss_conv_list);
0119     }
0120 }
0121 
0122 static bool __init omapdss_list_contains(const struct device_node *node)
0123 {
0124     struct dss_conv_node *n;
0125 
0126     list_for_each_entry(n, &dss_conv_list, list) {
0127         if (n->node == node)
0128             return true;
0129     }
0130 
0131     return false;
0132 }
0133 
0134 static void __init omapdss_walk_device(struct device_node *node, bool root)
0135 {
0136     struct device_node *n;
0137 
0138     omapdss_add_to_list(node, root);
0139 
0140     /*
0141      * of_graph_get_remote_port_parent() prints an error if there is no
0142      * port/ports node. To avoid that, check first that there's the node.
0143      */
0144     n = of_get_child_by_name(node, "ports");
0145     if (!n)
0146         n = of_get_child_by_name(node, "port");
0147     if (!n)
0148         return;
0149 
0150     of_node_put(n);
0151 
0152     n = NULL;
0153     while ((n = of_graph_get_next_endpoint(node, n)) != NULL) {
0154         struct device_node *pn;
0155 
0156         pn = of_graph_get_remote_port_parent(n);
0157 
0158         if (!pn)
0159             continue;
0160 
0161         if (!of_device_is_available(pn) || omapdss_list_contains(pn)) {
0162             of_node_put(pn);
0163             continue;
0164         }
0165 
0166         omapdss_walk_device(pn, false);
0167     }
0168 }
0169 
0170 static const struct of_device_id omapdss_of_match[] __initconst = {
0171     { .compatible = "ti,omap2-dss", },
0172     { .compatible = "ti,omap3-dss", },
0173     { .compatible = "ti,omap4-dss", },
0174     { .compatible = "ti,omap5-dss", },
0175     { .compatible = "ti,dra7-dss", },
0176     {},
0177 };
0178 
0179 static int __init omapdss_boot_init(void)
0180 {
0181     struct device_node *dss, *child;
0182 
0183     INIT_LIST_HEAD(&dss_conv_list);
0184 
0185     dss = of_find_matching_node(NULL, omapdss_of_match);
0186 
0187     if (dss == NULL || !of_device_is_available(dss)) {
0188         of_node_put(dss);
0189         return 0;
0190     }
0191 
0192     omapdss_walk_device(dss, true);
0193 
0194     for_each_available_child_of_node(dss, child) {
0195         if (!of_find_property(child, "compatible", NULL))
0196             continue;
0197 
0198         omapdss_walk_device(child, true);
0199     }
0200 
0201     while (!list_empty(&dss_conv_list)) {
0202         struct dss_conv_node *n;
0203 
0204         n = list_first_entry(&dss_conv_list, struct dss_conv_node,
0205             list);
0206 
0207         if (!n->root)
0208             omapdss_omapify_node(n->node);
0209 
0210         list_del(&n->list);
0211         of_node_put(n->node);
0212         kfree(n);
0213     }
0214 
0215     return 0;
0216 }
0217 
0218 subsys_initcall(omapdss_boot_init);