1 /*
2  * Copyright (C) 2013 Texas Instruments
3  * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 as published by
7  * the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
12  * more details.
13  */
14 
15 #include <linux/device.h>
16 #include <linux/err.h>
17 #include <linux/module.h>
18 #include <linux/of.h>
19 #include <linux/seq_file.h>
20 
21 #include <video/omapfb_dss.h>
22 
23 #include "dss.h"
24 
25 struct device_node *
26 omapdss_of_get_next_port(const struct device_node *parent,
27 			 struct device_node *prev)
28 {
29 	struct device_node *port = NULL;
30 
31 	if (!parent)
32 		return NULL;
33 
34 	if (!prev) {
35 		struct device_node *ports;
36 		/*
37 		 * It's the first call, we have to find a port subnode
38 		 * within this node or within an optional 'ports' node.
39 		 */
40 		ports = of_get_child_by_name(parent, "ports");
41 		if (ports)
42 			parent = ports;
43 
44 		port = of_get_child_by_name(parent, "port");
45 
46 		/* release the 'ports' node */
47 		of_node_put(ports);
48 	} else {
49 		struct device_node *ports;
50 
51 		ports = of_get_parent(prev);
52 		if (!ports)
53 			return NULL;
54 
55 		do {
56 			port = of_get_next_child(ports, prev);
57 			if (!port) {
58 				of_node_put(ports);
59 				return NULL;
60 			}
61 			prev = port;
62 		} while (of_node_cmp(port->name, "port") != 0);
63 
64 		of_node_put(ports);
65 	}
66 
67 	return port;
68 }
69 EXPORT_SYMBOL_GPL(omapdss_of_get_next_port);
70 
71 struct device_node *
72 omapdss_of_get_next_endpoint(const struct device_node *parent,
73 			     struct device_node *prev)
74 {
75 	struct device_node *ep = NULL;
76 
77 	if (!parent)
78 		return NULL;
79 
80 	do {
81 		ep = of_get_next_child(parent, prev);
82 		if (!ep)
83 			return NULL;
84 		prev = ep;
85 	} while (of_node_cmp(ep->name, "endpoint") != 0);
86 
87 	return ep;
88 }
89 EXPORT_SYMBOL_GPL(omapdss_of_get_next_endpoint);
90 
91 struct device_node *dss_of_port_get_parent_device(struct device_node *port)
92 {
93 	struct device_node *np;
94 	int i;
95 
96 	if (!port)
97 		return NULL;
98 
99 	np = of_get_parent(port);
100 
101 	for (i = 0; i < 2 && np; ++i) {
102 		struct property *prop;
103 
104 		prop = of_find_property(np, "compatible", NULL);
105 
106 		if (prop)
107 			return np;
108 
109 		np = of_get_next_parent(np);
110 	}
111 
112 	return NULL;
113 }
114 
115 u32 dss_of_port_get_port_number(struct device_node *port)
116 {
117 	int r;
118 	u32 reg;
119 
120 	r = of_property_read_u32(port, "reg", &reg);
121 	if (r)
122 		reg = 0;
123 
124 	return reg;
125 }
126 
127 static struct device_node *omapdss_of_get_remote_port(const struct device_node *node)
128 {
129 	struct device_node *np;
130 
131 	np = of_parse_phandle(node, "remote-endpoint", 0);
132 	if (!np)
133 		return NULL;
134 
135 	np = of_get_next_parent(np);
136 
137 	return np;
138 }
139 
140 struct device_node *
141 omapdss_of_get_first_endpoint(const struct device_node *parent)
142 {
143 	struct device_node *port, *ep;
144 
145 	port = omapdss_of_get_next_port(parent, NULL);
146 
147 	if (!port)
148 		return NULL;
149 
150 	ep = omapdss_of_get_next_endpoint(port, NULL);
151 
152 	of_node_put(port);
153 
154 	return ep;
155 }
156 EXPORT_SYMBOL_GPL(omapdss_of_get_first_endpoint);
157 
158 struct omap_dss_device *
159 omapdss_of_find_source_for_first_ep(struct device_node *node)
160 {
161 	struct device_node *ep;
162 	struct device_node *src_port;
163 	struct omap_dss_device *src;
164 
165 	ep = omapdss_of_get_first_endpoint(node);
166 	if (!ep)
167 		return ERR_PTR(-EINVAL);
168 
169 	src_port = omapdss_of_get_remote_port(ep);
170 	if (!src_port) {
171 		of_node_put(ep);
172 		return ERR_PTR(-EINVAL);
173 	}
174 
175 	of_node_put(ep);
176 
177 	src = omap_dss_find_output_by_port_node(src_port);
178 
179 	of_node_put(src_port);
180 
181 	return src ? src : ERR_PTR(-EPROBE_DEFER);
182 }
183 EXPORT_SYMBOL_GPL(omapdss_of_find_source_for_first_ep);
184