blob: f8dad99013e8dfcbc1d00831d4a070a0bfa7790c [file] [log] [blame]
Andrew F. Davisbb5cdf82017-12-05 14:29:31 -06001/*
2 * OMAP Display Subsystem Base
3 *
4 * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com/
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 */
15
Tomi Valkeinena99ac0d2015-11-05 17:19:32 +020016#include <linux/kernel.h>
Laurent Pinchart6a7c5a22018-02-28 23:49:24 +020017#include <linux/list.h>
Tomi Valkeinena99ac0d2015-11-05 17:19:32 +020018#include <linux/module.h>
Laurent Pinchart6a7c5a22018-02-28 23:49:24 +020019#include <linux/mutex.h>
Peter Ujfalusi1e08c822016-05-03 22:07:10 +030020#include <linux/of.h>
21#include <linux/of_graph.h>
Laurent Pinchart79107f22018-09-23 12:58:15 +030022#include <linux/platform_device.h>
Laurent Pinchartd3541ca2018-02-13 14:00:41 +020023
24#include "dss.h"
Peter Ujfalusi1e08c822016-05-03 22:07:10 +030025#include "omapdss.h"
Tomi Valkeinena99ac0d2015-11-05 17:19:32 +020026
Laurent Pinchart72877cf2018-02-13 14:00:40 +020027static struct dss_device *dss_device;
Tomi Valkeinen7c299712015-11-05 17:23:14 +020028
Laurent Pinchart72877cf2018-02-13 14:00:40 +020029struct dss_device *omapdss_get_dss(void)
Tomi Valkeinen7c299712015-11-05 17:23:14 +020030{
Laurent Pinchart72877cf2018-02-13 14:00:40 +020031 return dss_device;
Tomi Valkeinen7c299712015-11-05 17:23:14 +020032}
Laurent Pinchart72877cf2018-02-13 14:00:40 +020033EXPORT_SYMBOL(omapdss_get_dss);
Tomi Valkeinen7c299712015-11-05 17:23:14 +020034
Laurent Pinchart72877cf2018-02-13 14:00:40 +020035void omapdss_set_dss(struct dss_device *dss)
Tomi Valkeinen7c299712015-11-05 17:23:14 +020036{
Laurent Pinchart72877cf2018-02-13 14:00:40 +020037 dss_device = dss;
Tomi Valkeinen7c299712015-11-05 17:23:14 +020038}
Laurent Pinchart72877cf2018-02-13 14:00:40 +020039EXPORT_SYMBOL(omapdss_set_dss);
Tomi Valkeinen7c299712015-11-05 17:23:14 +020040
Laurent Pinchart50638ae2018-02-13 14:00:42 +020041struct dispc_device *dispc_get_dispc(struct dss_device *dss)
42{
43 return dss->dispc;
44}
45EXPORT_SYMBOL(dispc_get_dispc);
46
Laurent Pinchartd3541ca2018-02-13 14:00:41 +020047const struct dispc_ops *dispc_get_ops(struct dss_device *dss)
Tomi Valkeinen8a133982015-11-05 19:36:02 +020048{
Laurent Pinchartd3541ca2018-02-13 14:00:41 +020049 return dss->dispc_ops;
Tomi Valkeinen8a133982015-11-05 19:36:02 +020050}
51EXPORT_SYMBOL(dispc_get_ops);
52
Laurent Pinchart6a7c5a22018-02-28 23:49:24 +020053
54/* -----------------------------------------------------------------------------
55 * OMAP DSS Devices Handling
56 */
57
58static LIST_HEAD(omapdss_devices_list);
59static DEFINE_MUTEX(omapdss_devices_lock);
60
61void omapdss_device_register(struct omap_dss_device *dssdev)
62{
63 mutex_lock(&omapdss_devices_lock);
64 list_add_tail(&dssdev->list, &omapdss_devices_list);
65 mutex_unlock(&omapdss_devices_lock);
66}
Laurent Pinchartde57e9d2018-03-02 01:25:32 +020067EXPORT_SYMBOL_GPL(omapdss_device_register);
Laurent Pinchart6a7c5a22018-02-28 23:49:24 +020068
69void omapdss_device_unregister(struct omap_dss_device *dssdev)
70{
71 mutex_lock(&omapdss_devices_lock);
72 list_del(&dssdev->list);
73 mutex_unlock(&omapdss_devices_lock);
74}
Laurent Pinchartde57e9d2018-03-02 01:25:32 +020075EXPORT_SYMBOL_GPL(omapdss_device_unregister);
Laurent Pinchart6a7c5a22018-02-28 23:49:24 +020076
Laurent Pinchart9184f8d2018-02-28 23:53:16 +020077static bool omapdss_device_is_registered(struct device_node *node)
78{
79 struct omap_dss_device *dssdev;
80 bool found = false;
81
82 mutex_lock(&omapdss_devices_lock);
83
84 list_for_each_entry(dssdev, &omapdss_devices_list, list) {
85 if (dssdev->dev->of_node == node) {
86 found = true;
87 break;
88 }
89 }
90
91 mutex_unlock(&omapdss_devices_lock);
92 return found;
93}
94
Laurent Pinchartc1dfe722018-03-02 02:43:45 +020095struct omap_dss_device *omapdss_device_get(struct omap_dss_device *dssdev)
96{
97 if (!try_module_get(dssdev->owner))
98 return NULL;
99
100 if (get_device(dssdev->dev) == NULL) {
101 module_put(dssdev->owner);
102 return NULL;
103 }
104
105 return dssdev;
106}
107EXPORT_SYMBOL(omapdss_device_get);
108
109void omapdss_device_put(struct omap_dss_device *dssdev)
110{
111 put_device(dssdev->dev);
112 module_put(dssdev->owner);
113}
114EXPORT_SYMBOL(omapdss_device_put);
115
Laurent Pinchartce69aac2018-09-21 23:32:58 +0300116struct omap_dss_device *omapdss_find_device_by_node(struct device_node *node)
Laurent Pincharte10bd352018-03-01 23:35:55 +0200117{
118 struct omap_dss_device *dssdev;
119
120 list_for_each_entry(dssdev, &omapdss_devices_list, list) {
Laurent Pinchartce69aac2018-09-21 23:32:58 +0300121 if (dssdev->dev->of_node == node)
Laurent Pinchartc1dfe722018-03-02 02:43:45 +0200122 return omapdss_device_get(dssdev);
Laurent Pincharte10bd352018-03-01 23:35:55 +0200123 }
124
125 return NULL;
126}
127
Laurent Pinchartb9f4d2e2018-03-02 02:07:34 +0200128/*
Laurent Pinchart19b42002018-08-24 19:38:07 +0300129 * Search for the next output device starting at @from. Release the reference to
130 * the @from device, and acquire a reference to the returned device if found.
Laurent Pinchartb9f4d2e2018-03-02 02:07:34 +0200131 */
Laurent Pinchart19b42002018-08-24 19:38:07 +0300132struct omap_dss_device *omapdss_device_next_output(struct omap_dss_device *from)
Laurent Pinchartb9f4d2e2018-03-02 02:07:34 +0200133{
134 struct omap_dss_device *dssdev;
135 struct list_head *list;
136
137 mutex_lock(&omapdss_devices_lock);
138
139 if (list_empty(&omapdss_devices_list)) {
140 dssdev = NULL;
141 goto done;
142 }
143
144 /*
145 * Start from the from entry if given or from omapdss_devices_list
146 * otherwise.
147 */
148 list = from ? &from->list : &omapdss_devices_list;
149
150 list_for_each_entry(dssdev, list, list) {
151 /*
152 * Stop if we reach the omapdss_devices_list, that's the end of
153 * the list.
154 */
155 if (&dssdev->list == &omapdss_devices_list) {
156 dssdev = NULL;
157 goto done;
158 }
159
Laurent Pinchart30b71762018-12-07 23:08:35 +0200160 if (dssdev->id &&
161 (dssdev->next || dssdev->bridge || dssdev->panel))
Laurent Pinchartb9f4d2e2018-03-02 02:07:34 +0200162 goto done;
163 }
164
165 dssdev = NULL;
166
167done:
168 if (from)
Laurent Pinchartc1dfe722018-03-02 02:43:45 +0200169 omapdss_device_put(from);
Laurent Pinchartb9f4d2e2018-03-02 02:07:34 +0200170 if (dssdev)
Laurent Pinchartc1dfe722018-03-02 02:43:45 +0200171 omapdss_device_get(dssdev);
Laurent Pinchartb9f4d2e2018-03-02 02:07:34 +0200172
173 mutex_unlock(&omapdss_devices_lock);
174 return dssdev;
175}
Laurent Pinchart19b42002018-08-24 19:38:07 +0300176EXPORT_SYMBOL(omapdss_device_next_output);
Laurent Pinchartb9f4d2e2018-03-02 02:07:34 +0200177
Laurent Pinchartb49a2132018-09-04 23:53:34 +0300178static bool omapdss_device_is_connected(struct omap_dss_device *dssdev)
179{
Laurent Pinchartdf6682b2018-09-13 03:48:02 +0300180 return dssdev->dss;
Laurent Pinchartb49a2132018-09-04 23:53:34 +0300181}
182
Laurent Pinchartf324b272018-03-02 02:54:16 +0200183int omapdss_device_connect(struct dss_device *dss,
184 struct omap_dss_device *src,
Laurent Pinchartec727e32018-02-28 17:30:30 +0200185 struct omap_dss_device *dst)
186{
Laurent Pinchartfb557172018-02-28 17:30:30 +0200187 int ret;
188
Laurent Pinchart79107f22018-09-23 12:58:15 +0300189 dev_dbg(&dss->pdev->dev, "connect(%s, %s)\n",
190 src ? dev_name(src->dev) : "NULL",
191 dst ? dev_name(dst->dev) : "NULL");
192
193 if (!dst) {
194 /*
195 * The destination is NULL when the source is connected to a
Laurent Pinchart30b71762018-12-07 23:08:35 +0200196 * bridge or panel instead of a DSS device. Stop here, we will
197 * attach the bridge or panel later when we will have a DRM
198 * encoder.
Laurent Pinchart79107f22018-09-23 12:58:15 +0300199 */
Laurent Pinchart30b71762018-12-07 23:08:35 +0200200 return src && (src->bridge || src->panel) ? 0 : -EINVAL;
Laurent Pinchart79107f22018-09-23 12:58:15 +0300201 }
Laurent Pinchart1f507962018-02-28 17:30:30 +0200202
Laurent Pinchart511afb42018-03-04 23:42:36 +0200203 if (omapdss_device_is_connected(dst))
Laurent Pinchart1f507962018-02-28 17:30:30 +0200204 return -EBUSY;
205
Laurent Pinchart511afb42018-03-04 23:42:36 +0200206 dst->dss = dss;
Laurent Pinchartf324b272018-03-02 02:54:16 +0200207
Laurent Pinchart83910ad2018-06-01 19:45:01 +0300208 ret = dst->ops->connect(src, dst);
Laurent Pinchartf324b272018-03-02 02:54:16 +0200209 if (ret < 0) {
Laurent Pinchart511afb42018-03-04 23:42:36 +0200210 dst->dss = NULL;
Laurent Pinchartfb557172018-02-28 17:30:30 +0200211 return ret;
Laurent Pinchartf324b272018-03-02 02:54:16 +0200212 }
Laurent Pinchartfb557172018-02-28 17:30:30 +0200213
Laurent Pinchartfb557172018-02-28 17:30:30 +0200214 return 0;
Laurent Pinchartec727e32018-02-28 17:30:30 +0200215}
216EXPORT_SYMBOL_GPL(omapdss_device_connect);
217
218void omapdss_device_disconnect(struct omap_dss_device *src,
219 struct omap_dss_device *dst)
220{
Laurent Pinchart79107f22018-09-23 12:58:15 +0300221 struct dss_device *dss = src ? src->dss : dst->dss;
222
223 dev_dbg(&dss->pdev->dev, "disconnect(%s, %s)\n",
224 src ? dev_name(src->dev) : "NULL",
225 dst ? dev_name(dst->dev) : "NULL");
226
227 if (!dst) {
Laurent Pinchart30b71762018-12-07 23:08:35 +0200228 WARN_ON(!src->bridge && !src->panel);
Laurent Pinchart79107f22018-09-23 12:58:15 +0300229 return;
230 }
Laurent Pinchart1f507962018-02-28 17:30:30 +0200231
Laurent Pinchart511afb42018-03-04 23:42:36 +0200232 if (!dst->id && !omapdss_device_is_connected(dst)) {
Laurent Pinchart0dbfc392018-12-10 14:00:38 +0200233 WARN_ON(!dst->display);
Laurent Pinchart1f507962018-02-28 17:30:30 +0200234 return;
235 }
236
Laurent Pinchart3be0f152018-03-06 01:51:31 +0200237 WARN_ON(dst->state != OMAP_DSS_DISPLAY_DISABLED);
238
Laurent Pinchart83910ad2018-06-01 19:45:01 +0300239 dst->ops->disconnect(src, dst);
Laurent Pinchart511afb42018-03-04 23:42:36 +0200240 dst->dss = NULL;
Laurent Pinchartec727e32018-02-28 17:30:30 +0200241}
242EXPORT_SYMBOL_GPL(omapdss_device_disconnect);
243
Laurent Pinchart19b42002018-08-24 19:38:07 +0300244void omapdss_device_pre_enable(struct omap_dss_device *dssdev)
245{
246 if (!dssdev)
247 return;
248
249 omapdss_device_pre_enable(dssdev->next);
250
251 if (dssdev->ops->pre_enable)
252 dssdev->ops->pre_enable(dssdev);
253}
254EXPORT_SYMBOL_GPL(omapdss_device_pre_enable);
255
256void omapdss_device_enable(struct omap_dss_device *dssdev)
257{
258 if (!dssdev)
259 return;
260
261 if (dssdev->ops->enable)
262 dssdev->ops->enable(dssdev);
263
264 omapdss_device_enable(dssdev->next);
265
266 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
267}
268EXPORT_SYMBOL_GPL(omapdss_device_enable);
269
270void omapdss_device_disable(struct omap_dss_device *dssdev)
271{
272 if (!dssdev)
273 return;
274
275 omapdss_device_disable(dssdev->next);
276
277 if (dssdev->ops->disable)
278 dssdev->ops->disable(dssdev);
279}
280EXPORT_SYMBOL_GPL(omapdss_device_disable);
281
282void omapdss_device_post_disable(struct omap_dss_device *dssdev)
283{
284 if (!dssdev)
285 return;
286
287 if (dssdev->ops->post_disable)
288 dssdev->ops->post_disable(dssdev);
289
290 omapdss_device_post_disable(dssdev->next);
291
292 dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
293}
294EXPORT_SYMBOL_GPL(omapdss_device_post_disable);
295
Laurent Pinchart6a7c5a22018-02-28 23:49:24 +0200296/* -----------------------------------------------------------------------------
297 * Components Handling
298 */
299
300static struct list_head omapdss_comp_list;
301
302struct omapdss_comp_node {
303 struct list_head list;
304 struct device_node *node;
305 bool dss_core_component;
Laurent Pinchart4e177632018-09-23 15:05:10 +0300306 const char *compat;
Laurent Pinchart6a7c5a22018-02-28 23:49:24 +0200307};
308
Peter Ujfalusi1e08c822016-05-03 22:07:10 +0300309static bool omapdss_list_contains(const struct device_node *node)
310{
311 struct omapdss_comp_node *comp;
312
313 list_for_each_entry(comp, &omapdss_comp_list, list) {
314 if (comp->node == node)
315 return true;
316 }
317
318 return false;
319}
320
321static void omapdss_walk_device(struct device *dev, struct device_node *node,
322 bool dss_core)
323{
Laurent Pinchart4e177632018-09-23 15:05:10 +0300324 struct omapdss_comp_node *comp;
Peter Ujfalusi1e08c822016-05-03 22:07:10 +0300325 struct device_node *n;
Laurent Pinchart4e177632018-09-23 15:05:10 +0300326 const char *compat;
327 int ret;
Peter Ujfalusi1e08c822016-05-03 22:07:10 +0300328
Laurent Pinchart4e177632018-09-23 15:05:10 +0300329 ret = of_property_read_string(node, "compatible", &compat);
330 if (ret < 0)
331 return;
332
333 comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL);
Peter Ujfalusi1e08c822016-05-03 22:07:10 +0300334 if (comp) {
335 comp->node = node;
336 comp->dss_core_component = dss_core;
Laurent Pinchart4e177632018-09-23 15:05:10 +0300337 comp->compat = compat;
Peter Ujfalusi1e08c822016-05-03 22:07:10 +0300338 list_add(&comp->list, &omapdss_comp_list);
339 }
340
341 /*
342 * of_graph_get_remote_port_parent() prints an error if there is no
343 * port/ports node. To avoid that, check first that there's the node.
344 */
345 n = of_get_child_by_name(node, "ports");
346 if (!n)
347 n = of_get_child_by_name(node, "port");
348 if (!n)
349 return;
350
351 of_node_put(n);
352
353 n = NULL;
354 while ((n = of_graph_get_next_endpoint(node, n)) != NULL) {
355 struct device_node *pn = of_graph_get_remote_port_parent(n);
356
357 if (!pn)
358 continue;
359
360 if (!of_device_is_available(pn) || omapdss_list_contains(pn)) {
361 of_node_put(pn);
362 continue;
363 }
364
365 omapdss_walk_device(dev, pn, false);
366 }
367}
368
369void omapdss_gather_components(struct device *dev)
370{
371 struct device_node *child;
372
373 INIT_LIST_HEAD(&omapdss_comp_list);
374
375 omapdss_walk_device(dev, dev->of_node, true);
376
Laurent Pinchart4e177632018-09-23 15:05:10 +0300377 for_each_available_child_of_node(dev->of_node, child)
Peter Ujfalusi1e08c822016-05-03 22:07:10 +0300378 omapdss_walk_device(dev, child, true);
Peter Ujfalusi1e08c822016-05-03 22:07:10 +0300379}
380EXPORT_SYMBOL(omapdss_gather_components);
381
382static bool omapdss_component_is_loaded(struct omapdss_comp_node *comp)
383{
384 if (comp->dss_core_component)
385 return true;
Laurent Pinchart4e177632018-09-23 15:05:10 +0300386 if (!strstarts(comp->compat, "omapdss,"))
387 return true;
Laurent Pinchart9184f8d2018-02-28 23:53:16 +0200388 if (omapdss_device_is_registered(comp->node))
Peter Ujfalusi1e08c822016-05-03 22:07:10 +0300389 return true;
390
391 return false;
392}
393
394bool omapdss_stack_is_ready(void)
395{
396 struct omapdss_comp_node *comp;
397
398 list_for_each_entry(comp, &omapdss_comp_list, list) {
399 if (!omapdss_component_is_loaded(comp))
400 return false;
401 }
402
403 return true;
404}
405EXPORT_SYMBOL(omapdss_stack_is_ready);
406
Tomi Valkeinena99ac0d2015-11-05 17:19:32 +0200407MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
408MODULE_DESCRIPTION("OMAP Display Subsystem Base");
409MODULE_LICENSE("GPL v2");