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