1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * OMAP Display Subsystem Base
4 *
5 * Copyright (C) 2015-2017 Texas Instruments Incorporated - https://www.ti.com/
6 */
7
8 #include <linux/kernel.h>
9 #include <linux/list.h>
10 #include <linux/module.h>
11 #include <linux/mutex.h>
12 #include <linux/of.h>
13 #include <linux/of_graph.h>
14 #include <linux/platform_device.h>
15
16 #include "dss.h"
17 #include "omapdss.h"
18
dispc_get_dispc(struct dss_device * dss)19 struct dispc_device *dispc_get_dispc(struct dss_device *dss)
20 {
21 return dss->dispc;
22 }
23
24 /* -----------------------------------------------------------------------------
25 * OMAP DSS Devices Handling
26 */
27
28 static LIST_HEAD(omapdss_devices_list);
29 static DEFINE_MUTEX(omapdss_devices_lock);
30
omapdss_device_register(struct omap_dss_device * dssdev)31 void omapdss_device_register(struct omap_dss_device *dssdev)
32 {
33 mutex_lock(&omapdss_devices_lock);
34 list_add_tail(&dssdev->list, &omapdss_devices_list);
35 mutex_unlock(&omapdss_devices_lock);
36 }
37
omapdss_device_unregister(struct omap_dss_device * dssdev)38 void omapdss_device_unregister(struct omap_dss_device *dssdev)
39 {
40 mutex_lock(&omapdss_devices_lock);
41 list_del(&dssdev->list);
42 mutex_unlock(&omapdss_devices_lock);
43 }
44
omapdss_device_is_registered(struct device_node * node)45 static bool omapdss_device_is_registered(struct device_node *node)
46 {
47 struct omap_dss_device *dssdev;
48 bool found = false;
49
50 mutex_lock(&omapdss_devices_lock);
51
52 list_for_each_entry(dssdev, &omapdss_devices_list, list) {
53 if (dssdev->dev->of_node == node) {
54 found = true;
55 break;
56 }
57 }
58
59 mutex_unlock(&omapdss_devices_lock);
60 return found;
61 }
62
omapdss_device_get(struct omap_dss_device * dssdev)63 struct omap_dss_device *omapdss_device_get(struct omap_dss_device *dssdev)
64 {
65 if (get_device(dssdev->dev) == NULL)
66 return NULL;
67
68 return dssdev;
69 }
70
omapdss_device_put(struct omap_dss_device * dssdev)71 void omapdss_device_put(struct omap_dss_device *dssdev)
72 {
73 put_device(dssdev->dev);
74 }
75
omapdss_find_device_by_node(struct device_node * node)76 struct omap_dss_device *omapdss_find_device_by_node(struct device_node *node)
77 {
78 struct omap_dss_device *dssdev;
79
80 list_for_each_entry(dssdev, &omapdss_devices_list, list) {
81 if (dssdev->dev->of_node == node)
82 return omapdss_device_get(dssdev);
83 }
84
85 return NULL;
86 }
87
88 /*
89 * Search for the next output device starting at @from. Release the reference to
90 * the @from device, and acquire a reference to the returned device if found.
91 */
omapdss_device_next_output(struct omap_dss_device * from)92 struct omap_dss_device *omapdss_device_next_output(struct omap_dss_device *from)
93 {
94 struct omap_dss_device *dssdev;
95 struct list_head *list;
96
97 mutex_lock(&omapdss_devices_lock);
98
99 if (list_empty(&omapdss_devices_list)) {
100 dssdev = NULL;
101 goto done;
102 }
103
104 /*
105 * Start from the from entry if given or from omapdss_devices_list
106 * otherwise.
107 */
108 list = from ? &from->list : &omapdss_devices_list;
109
110 list_for_each_entry(dssdev, list, list) {
111 /*
112 * Stop if we reach the omapdss_devices_list, that's the end of
113 * the list.
114 */
115 if (&dssdev->list == &omapdss_devices_list) {
116 dssdev = NULL;
117 goto done;
118 }
119
120 if (dssdev->id && dssdev->bridge)
121 goto done;
122 }
123
124 dssdev = NULL;
125
126 done:
127 if (from)
128 omapdss_device_put(from);
129 if (dssdev)
130 omapdss_device_get(dssdev);
131
132 mutex_unlock(&omapdss_devices_lock);
133 return dssdev;
134 }
135
omapdss_device_is_connected(struct omap_dss_device * dssdev)136 static bool omapdss_device_is_connected(struct omap_dss_device *dssdev)
137 {
138 return dssdev->dss;
139 }
140
omapdss_device_connect(struct dss_device * dss,struct omap_dss_device * dst)141 int omapdss_device_connect(struct dss_device *dss,
142 struct omap_dss_device *dst)
143 {
144 dev_dbg(&dss->pdev->dev, "connect(%s)\n",
145 dst ? dev_name(dst->dev) : "NULL");
146
147 if (!dst)
148 return -EINVAL;
149
150 if (omapdss_device_is_connected(dst))
151 return -EBUSY;
152
153 dst->dss = dss;
154
155 return 0;
156 }
157
omapdss_device_disconnect(struct dss_device * dss,struct omap_dss_device * dst)158 void omapdss_device_disconnect(struct dss_device *dss,
159 struct omap_dss_device *dst)
160 {
161 dev_dbg(&dss->pdev->dev, "disconnect(%s)\n",
162 dst ? dev_name(dst->dev) : "NULL");
163
164 if (WARN_ON(!dst))
165 return;
166
167 if (!dst->id && !omapdss_device_is_connected(dst)) {
168 WARN_ON(1);
169 return;
170 }
171
172 dst->dss = NULL;
173 }
174
175 /* -----------------------------------------------------------------------------
176 * Components Handling
177 */
178
179 static struct list_head omapdss_comp_list;
180
181 struct omapdss_comp_node {
182 struct list_head list;
183 struct device_node *node;
184 bool dss_core_component;
185 const char *compat;
186 };
187
omapdss_list_contains(const struct device_node * node)188 static bool omapdss_list_contains(const struct device_node *node)
189 {
190 struct omapdss_comp_node *comp;
191
192 list_for_each_entry(comp, &omapdss_comp_list, list) {
193 if (comp->node == node)
194 return true;
195 }
196
197 return false;
198 }
199
omapdss_walk_device(struct device * dev,struct device_node * node,bool dss_core)200 static void omapdss_walk_device(struct device *dev, struct device_node *node,
201 bool dss_core)
202 {
203 struct omapdss_comp_node *comp;
204 struct device_node *n;
205 const char *compat;
206 int ret;
207
208 ret = of_property_read_string(node, "compatible", &compat);
209 if (ret < 0)
210 return;
211
212 comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL);
213 if (comp) {
214 comp->node = node;
215 comp->dss_core_component = dss_core;
216 comp->compat = compat;
217 list_add(&comp->list, &omapdss_comp_list);
218 }
219
220 /*
221 * of_graph_get_remote_port_parent() prints an error if there is no
222 * port/ports node. To avoid that, check first that there's the node.
223 */
224 n = of_get_child_by_name(node, "ports");
225 if (!n)
226 n = of_get_child_by_name(node, "port");
227 if (!n)
228 return;
229
230 of_node_put(n);
231
232 for_each_endpoint_of_node(node, n) {
233 struct device_node *pn = of_graph_get_remote_port_parent(n);
234
235 if (!pn)
236 continue;
237
238 if (!of_device_is_available(pn) || omapdss_list_contains(pn)) {
239 of_node_put(pn);
240 continue;
241 }
242
243 omapdss_walk_device(dev, pn, false);
244 }
245 }
246
omapdss_gather_components(struct device * dev)247 void omapdss_gather_components(struct device *dev)
248 {
249 struct device_node *child;
250
251 INIT_LIST_HEAD(&omapdss_comp_list);
252
253 omapdss_walk_device(dev, dev->of_node, true);
254
255 for_each_available_child_of_node(dev->of_node, child)
256 omapdss_walk_device(dev, child, true);
257 }
258
omapdss_component_is_loaded(struct omapdss_comp_node * comp)259 static bool omapdss_component_is_loaded(struct omapdss_comp_node *comp)
260 {
261 if (comp->dss_core_component)
262 return true;
263 if (!strstarts(comp->compat, "omapdss,"))
264 return true;
265 if (omapdss_device_is_registered(comp->node))
266 return true;
267
268 return false;
269 }
270
omapdss_stack_is_ready(void)271 bool omapdss_stack_is_ready(void)
272 {
273 struct omapdss_comp_node *comp;
274
275 list_for_each_entry(comp, &omapdss_comp_list, list) {
276 if (!omapdss_component_is_loaded(comp))
277 return false;
278 }
279
280 return true;
281 }
282