11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2bb5cdf8dSAndrew F. Davis /* 3bb5cdf8dSAndrew F. Davis * OMAP Display Subsystem Base 4bb5cdf8dSAndrew F. Davis * 51b409fdaSAlexander A. Klimov * Copyright (C) 2015-2017 Texas Instruments Incorporated - https://www.ti.com/ 6bb5cdf8dSAndrew F. Davis */ 7bb5cdf8dSAndrew F. Davis 8a99ac0d9STomi Valkeinen #include <linux/kernel.h> 96a7c5a22SLaurent Pinchart #include <linux/list.h> 10a99ac0d9STomi Valkeinen #include <linux/module.h> 116a7c5a22SLaurent Pinchart #include <linux/mutex.h> 121e08c822SPeter Ujfalusi #include <linux/of.h> 131e08c822SPeter Ujfalusi #include <linux/of_graph.h> 1479107f27SLaurent Pinchart #include <linux/platform_device.h> 15d3541ca8SLaurent Pinchart 16d3541ca8SLaurent Pinchart #include "dss.h" 171e08c822SPeter Ujfalusi #include "omapdss.h" 18a99ac0d9STomi Valkeinen 1950638ae5SLaurent Pinchart struct dispc_device *dispc_get_dispc(struct dss_device *dss) 2050638ae5SLaurent Pinchart { 2150638ae5SLaurent Pinchart return dss->dispc; 2250638ae5SLaurent Pinchart } 2350638ae5SLaurent Pinchart EXPORT_SYMBOL(dispc_get_dispc); 2450638ae5SLaurent Pinchart 25d3541ca8SLaurent Pinchart const struct dispc_ops *dispc_get_ops(struct dss_device *dss) 268a13398cSTomi Valkeinen { 27d3541ca8SLaurent Pinchart return dss->dispc_ops; 288a13398cSTomi Valkeinen } 298a13398cSTomi Valkeinen EXPORT_SYMBOL(dispc_get_ops); 308a13398cSTomi Valkeinen 316a7c5a22SLaurent Pinchart 326a7c5a22SLaurent Pinchart /* ----------------------------------------------------------------------------- 336a7c5a22SLaurent Pinchart * OMAP DSS Devices Handling 346a7c5a22SLaurent Pinchart */ 356a7c5a22SLaurent Pinchart 366a7c5a22SLaurent Pinchart static LIST_HEAD(omapdss_devices_list); 376a7c5a22SLaurent Pinchart static DEFINE_MUTEX(omapdss_devices_lock); 386a7c5a22SLaurent Pinchart 396a7c5a22SLaurent Pinchart void omapdss_device_register(struct omap_dss_device *dssdev) 406a7c5a22SLaurent Pinchart { 416a7c5a22SLaurent Pinchart mutex_lock(&omapdss_devices_lock); 426a7c5a22SLaurent Pinchart list_add_tail(&dssdev->list, &omapdss_devices_list); 436a7c5a22SLaurent Pinchart mutex_unlock(&omapdss_devices_lock); 446a7c5a22SLaurent Pinchart } 45de57e9dbSLaurent Pinchart EXPORT_SYMBOL_GPL(omapdss_device_register); 466a7c5a22SLaurent Pinchart 476a7c5a22SLaurent Pinchart void omapdss_device_unregister(struct omap_dss_device *dssdev) 486a7c5a22SLaurent Pinchart { 496a7c5a22SLaurent Pinchart mutex_lock(&omapdss_devices_lock); 506a7c5a22SLaurent Pinchart list_del(&dssdev->list); 516a7c5a22SLaurent Pinchart mutex_unlock(&omapdss_devices_lock); 526a7c5a22SLaurent Pinchart } 53de57e9dbSLaurent Pinchart EXPORT_SYMBOL_GPL(omapdss_device_unregister); 546a7c5a22SLaurent Pinchart 559184f8d9SLaurent Pinchart static bool omapdss_device_is_registered(struct device_node *node) 569184f8d9SLaurent Pinchart { 579184f8d9SLaurent Pinchart struct omap_dss_device *dssdev; 589184f8d9SLaurent Pinchart bool found = false; 599184f8d9SLaurent Pinchart 609184f8d9SLaurent Pinchart mutex_lock(&omapdss_devices_lock); 619184f8d9SLaurent Pinchart 629184f8d9SLaurent Pinchart list_for_each_entry(dssdev, &omapdss_devices_list, list) { 639184f8d9SLaurent Pinchart if (dssdev->dev->of_node == node) { 649184f8d9SLaurent Pinchart found = true; 659184f8d9SLaurent Pinchart break; 669184f8d9SLaurent Pinchart } 679184f8d9SLaurent Pinchart } 689184f8d9SLaurent Pinchart 699184f8d9SLaurent Pinchart mutex_unlock(&omapdss_devices_lock); 709184f8d9SLaurent Pinchart return found; 719184f8d9SLaurent Pinchart } 729184f8d9SLaurent Pinchart 73c1dfe721SLaurent Pinchart struct omap_dss_device *omapdss_device_get(struct omap_dss_device *dssdev) 74c1dfe721SLaurent Pinchart { 75c1dfe721SLaurent Pinchart if (!try_module_get(dssdev->owner)) 76c1dfe721SLaurent Pinchart return NULL; 77c1dfe721SLaurent Pinchart 78c1dfe721SLaurent Pinchart if (get_device(dssdev->dev) == NULL) { 79c1dfe721SLaurent Pinchart module_put(dssdev->owner); 80c1dfe721SLaurent Pinchart return NULL; 81c1dfe721SLaurent Pinchart } 82c1dfe721SLaurent Pinchart 83c1dfe721SLaurent Pinchart return dssdev; 84c1dfe721SLaurent Pinchart } 85c1dfe721SLaurent Pinchart EXPORT_SYMBOL(omapdss_device_get); 86c1dfe721SLaurent Pinchart 87c1dfe721SLaurent Pinchart void omapdss_device_put(struct omap_dss_device *dssdev) 88c1dfe721SLaurent Pinchart { 89c1dfe721SLaurent Pinchart put_device(dssdev->dev); 90c1dfe721SLaurent Pinchart module_put(dssdev->owner); 91c1dfe721SLaurent Pinchart } 92c1dfe721SLaurent Pinchart EXPORT_SYMBOL(omapdss_device_put); 93c1dfe721SLaurent Pinchart 94ce69aac8SLaurent Pinchart struct omap_dss_device *omapdss_find_device_by_node(struct device_node *node) 95e10bd354SLaurent Pinchart { 96e10bd354SLaurent Pinchart struct omap_dss_device *dssdev; 97e10bd354SLaurent Pinchart 98e10bd354SLaurent Pinchart list_for_each_entry(dssdev, &omapdss_devices_list, list) { 99ce69aac8SLaurent Pinchart if (dssdev->dev->of_node == node) 100c1dfe721SLaurent Pinchart return omapdss_device_get(dssdev); 101e10bd354SLaurent Pinchart } 102e10bd354SLaurent Pinchart 103e10bd354SLaurent Pinchart return NULL; 104e10bd354SLaurent Pinchart } 105e10bd354SLaurent Pinchart 106b9f4d2ebSLaurent Pinchart /* 10719b4200dSLaurent Pinchart * Search for the next output device starting at @from. Release the reference to 10819b4200dSLaurent Pinchart * the @from device, and acquire a reference to the returned device if found. 109b9f4d2ebSLaurent Pinchart */ 11019b4200dSLaurent Pinchart struct omap_dss_device *omapdss_device_next_output(struct omap_dss_device *from) 111b9f4d2ebSLaurent Pinchart { 112b9f4d2ebSLaurent Pinchart struct omap_dss_device *dssdev; 113b9f4d2ebSLaurent Pinchart struct list_head *list; 114b9f4d2ebSLaurent Pinchart 115b9f4d2ebSLaurent Pinchart mutex_lock(&omapdss_devices_lock); 116b9f4d2ebSLaurent Pinchart 117b9f4d2ebSLaurent Pinchart if (list_empty(&omapdss_devices_list)) { 118b9f4d2ebSLaurent Pinchart dssdev = NULL; 119b9f4d2ebSLaurent Pinchart goto done; 120b9f4d2ebSLaurent Pinchart } 121b9f4d2ebSLaurent Pinchart 122b9f4d2ebSLaurent Pinchart /* 123b9f4d2ebSLaurent Pinchart * Start from the from entry if given or from omapdss_devices_list 124b9f4d2ebSLaurent Pinchart * otherwise. 125b9f4d2ebSLaurent Pinchart */ 126b9f4d2ebSLaurent Pinchart list = from ? &from->list : &omapdss_devices_list; 127b9f4d2ebSLaurent Pinchart 128b9f4d2ebSLaurent Pinchart list_for_each_entry(dssdev, list, list) { 129b9f4d2ebSLaurent Pinchart /* 130b9f4d2ebSLaurent Pinchart * Stop if we reach the omapdss_devices_list, that's the end of 131b9f4d2ebSLaurent Pinchart * the list. 132b9f4d2ebSLaurent Pinchart */ 133b9f4d2ebSLaurent Pinchart if (&dssdev->list == &omapdss_devices_list) { 134b9f4d2ebSLaurent Pinchart dssdev = NULL; 135b9f4d2ebSLaurent Pinchart goto done; 136b9f4d2ebSLaurent Pinchart } 137b9f4d2ebSLaurent Pinchart 138*811860ddSSebastian Reichel if (dssdev->id && dssdev->bridge) 139b9f4d2ebSLaurent Pinchart goto done; 140b9f4d2ebSLaurent Pinchart } 141b9f4d2ebSLaurent Pinchart 142b9f4d2ebSLaurent Pinchart dssdev = NULL; 143b9f4d2ebSLaurent Pinchart 144b9f4d2ebSLaurent Pinchart done: 145b9f4d2ebSLaurent Pinchart if (from) 146c1dfe721SLaurent Pinchart omapdss_device_put(from); 147b9f4d2ebSLaurent Pinchart if (dssdev) 148c1dfe721SLaurent Pinchart omapdss_device_get(dssdev); 149b9f4d2ebSLaurent Pinchart 150b9f4d2ebSLaurent Pinchart mutex_unlock(&omapdss_devices_lock); 151b9f4d2ebSLaurent Pinchart return dssdev; 152b9f4d2ebSLaurent Pinchart } 15319b4200dSLaurent Pinchart EXPORT_SYMBOL(omapdss_device_next_output); 154b9f4d2ebSLaurent Pinchart 155b49a2139SLaurent Pinchart static bool omapdss_device_is_connected(struct omap_dss_device *dssdev) 156b49a2139SLaurent Pinchart { 157df6682b4SLaurent Pinchart return dssdev->dss; 158b49a2139SLaurent Pinchart } 159b49a2139SLaurent Pinchart 160f324b279SLaurent Pinchart int omapdss_device_connect(struct dss_device *dss, 161f324b279SLaurent Pinchart struct omap_dss_device *src, 162ec727e3fSLaurent Pinchart struct omap_dss_device *dst) 163ec727e3fSLaurent Pinchart { 16479107f27SLaurent Pinchart dev_dbg(&dss->pdev->dev, "connect(%s, %s)\n", 16579107f27SLaurent Pinchart src ? dev_name(src->dev) : "NULL", 16679107f27SLaurent Pinchart dst ? dev_name(dst->dev) : "NULL"); 16779107f27SLaurent Pinchart 16879107f27SLaurent Pinchart if (!dst) { 16979107f27SLaurent Pinchart /* 17079107f27SLaurent Pinchart * The destination is NULL when the source is connected to a 171a779618bSLaurent Pinchart * bridge instead of a DSS device. Stop here, we will attach 172a779618bSLaurent Pinchart * the bridge later when we will have a DRM encoder. 17379107f27SLaurent Pinchart */ 174a779618bSLaurent Pinchart return src && src->bridge ? 0 : -EINVAL; 17579107f27SLaurent Pinchart } 1761f507968SLaurent Pinchart 177511afb44SLaurent Pinchart if (omapdss_device_is_connected(dst)) 1781f507968SLaurent Pinchart return -EBUSY; 1791f507968SLaurent Pinchart 180511afb44SLaurent Pinchart dst->dss = dss; 181f324b279SLaurent Pinchart 182fb557171SLaurent Pinchart return 0; 183ec727e3fSLaurent Pinchart } 184ec727e3fSLaurent Pinchart EXPORT_SYMBOL_GPL(omapdss_device_connect); 185ec727e3fSLaurent Pinchart 186ec727e3fSLaurent Pinchart void omapdss_device_disconnect(struct omap_dss_device *src, 187ec727e3fSLaurent Pinchart struct omap_dss_device *dst) 188ec727e3fSLaurent Pinchart { 18979107f27SLaurent Pinchart struct dss_device *dss = src ? src->dss : dst->dss; 19079107f27SLaurent Pinchart 19179107f27SLaurent Pinchart dev_dbg(&dss->pdev->dev, "disconnect(%s, %s)\n", 19279107f27SLaurent Pinchart src ? dev_name(src->dev) : "NULL", 19379107f27SLaurent Pinchart dst ? dev_name(dst->dev) : "NULL"); 19479107f27SLaurent Pinchart 19579107f27SLaurent Pinchart if (!dst) { 196a779618bSLaurent Pinchart WARN_ON(!src->bridge); 19779107f27SLaurent Pinchart return; 19879107f27SLaurent Pinchart } 1991f507968SLaurent Pinchart 200511afb44SLaurent Pinchart if (!dst->id && !omapdss_device_is_connected(dst)) { 2010dbfc396SLaurent Pinchart WARN_ON(!dst->display); 2021f507968SLaurent Pinchart return; 2031f507968SLaurent Pinchart } 2041f507968SLaurent Pinchart 205511afb44SLaurent Pinchart dst->dss = NULL; 206ec727e3fSLaurent Pinchart } 207ec727e3fSLaurent Pinchart EXPORT_SYMBOL_GPL(omapdss_device_disconnect); 208ec727e3fSLaurent Pinchart 2096a7c5a22SLaurent Pinchart /* ----------------------------------------------------------------------------- 2106a7c5a22SLaurent Pinchart * Components Handling 2116a7c5a22SLaurent Pinchart */ 2126a7c5a22SLaurent Pinchart 2136a7c5a22SLaurent Pinchart static struct list_head omapdss_comp_list; 2146a7c5a22SLaurent Pinchart 2156a7c5a22SLaurent Pinchart struct omapdss_comp_node { 2166a7c5a22SLaurent Pinchart struct list_head list; 2176a7c5a22SLaurent Pinchart struct device_node *node; 2186a7c5a22SLaurent Pinchart bool dss_core_component; 2194e17763cSLaurent Pinchart const char *compat; 2206a7c5a22SLaurent Pinchart }; 2216a7c5a22SLaurent Pinchart 2221e08c822SPeter Ujfalusi static bool omapdss_list_contains(const struct device_node *node) 2231e08c822SPeter Ujfalusi { 2241e08c822SPeter Ujfalusi struct omapdss_comp_node *comp; 2251e08c822SPeter Ujfalusi 2261e08c822SPeter Ujfalusi list_for_each_entry(comp, &omapdss_comp_list, list) { 2271e08c822SPeter Ujfalusi if (comp->node == node) 2281e08c822SPeter Ujfalusi return true; 2291e08c822SPeter Ujfalusi } 2301e08c822SPeter Ujfalusi 2311e08c822SPeter Ujfalusi return false; 2321e08c822SPeter Ujfalusi } 2331e08c822SPeter Ujfalusi 2341e08c822SPeter Ujfalusi static void omapdss_walk_device(struct device *dev, struct device_node *node, 2351e08c822SPeter Ujfalusi bool dss_core) 2361e08c822SPeter Ujfalusi { 2374e17763cSLaurent Pinchart struct omapdss_comp_node *comp; 2381e08c822SPeter Ujfalusi struct device_node *n; 2394e17763cSLaurent Pinchart const char *compat; 2404e17763cSLaurent Pinchart int ret; 2411e08c822SPeter Ujfalusi 2424e17763cSLaurent Pinchart ret = of_property_read_string(node, "compatible", &compat); 2434e17763cSLaurent Pinchart if (ret < 0) 2444e17763cSLaurent Pinchart return; 2454e17763cSLaurent Pinchart 2464e17763cSLaurent Pinchart comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL); 2471e08c822SPeter Ujfalusi if (comp) { 2481e08c822SPeter Ujfalusi comp->node = node; 2491e08c822SPeter Ujfalusi comp->dss_core_component = dss_core; 2504e17763cSLaurent Pinchart comp->compat = compat; 2511e08c822SPeter Ujfalusi list_add(&comp->list, &omapdss_comp_list); 2521e08c822SPeter Ujfalusi } 2531e08c822SPeter Ujfalusi 2541e08c822SPeter Ujfalusi /* 2551e08c822SPeter Ujfalusi * of_graph_get_remote_port_parent() prints an error if there is no 2561e08c822SPeter Ujfalusi * port/ports node. To avoid that, check first that there's the node. 2571e08c822SPeter Ujfalusi */ 2581e08c822SPeter Ujfalusi n = of_get_child_by_name(node, "ports"); 2591e08c822SPeter Ujfalusi if (!n) 2601e08c822SPeter Ujfalusi n = of_get_child_by_name(node, "port"); 2611e08c822SPeter Ujfalusi if (!n) 2621e08c822SPeter Ujfalusi return; 2631e08c822SPeter Ujfalusi 2641e08c822SPeter Ujfalusi of_node_put(n); 2651e08c822SPeter Ujfalusi 2661e08c822SPeter Ujfalusi n = NULL; 2671e08c822SPeter Ujfalusi while ((n = of_graph_get_next_endpoint(node, n)) != NULL) { 2681e08c822SPeter Ujfalusi struct device_node *pn = of_graph_get_remote_port_parent(n); 2691e08c822SPeter Ujfalusi 2701e08c822SPeter Ujfalusi if (!pn) 2711e08c822SPeter Ujfalusi continue; 2721e08c822SPeter Ujfalusi 2731e08c822SPeter Ujfalusi if (!of_device_is_available(pn) || omapdss_list_contains(pn)) { 2741e08c822SPeter Ujfalusi of_node_put(pn); 2751e08c822SPeter Ujfalusi continue; 2761e08c822SPeter Ujfalusi } 2771e08c822SPeter Ujfalusi 2781e08c822SPeter Ujfalusi omapdss_walk_device(dev, pn, false); 2791e08c822SPeter Ujfalusi } 2801e08c822SPeter Ujfalusi } 2811e08c822SPeter Ujfalusi 2821e08c822SPeter Ujfalusi void omapdss_gather_components(struct device *dev) 2831e08c822SPeter Ujfalusi { 2841e08c822SPeter Ujfalusi struct device_node *child; 2851e08c822SPeter Ujfalusi 2861e08c822SPeter Ujfalusi INIT_LIST_HEAD(&omapdss_comp_list); 2871e08c822SPeter Ujfalusi 2881e08c822SPeter Ujfalusi omapdss_walk_device(dev, dev->of_node, true); 2891e08c822SPeter Ujfalusi 2904e17763cSLaurent Pinchart for_each_available_child_of_node(dev->of_node, child) 2911e08c822SPeter Ujfalusi omapdss_walk_device(dev, child, true); 2921e08c822SPeter Ujfalusi } 2931e08c822SPeter Ujfalusi EXPORT_SYMBOL(omapdss_gather_components); 2941e08c822SPeter Ujfalusi 2951e08c822SPeter Ujfalusi static bool omapdss_component_is_loaded(struct omapdss_comp_node *comp) 2961e08c822SPeter Ujfalusi { 2971e08c822SPeter Ujfalusi if (comp->dss_core_component) 2981e08c822SPeter Ujfalusi return true; 2994e17763cSLaurent Pinchart if (!strstarts(comp->compat, "omapdss,")) 3004e17763cSLaurent Pinchart return true; 3019184f8d9SLaurent Pinchart if (omapdss_device_is_registered(comp->node)) 3021e08c822SPeter Ujfalusi return true; 3031e08c822SPeter Ujfalusi 3041e08c822SPeter Ujfalusi return false; 3051e08c822SPeter Ujfalusi } 3061e08c822SPeter Ujfalusi 3071e08c822SPeter Ujfalusi bool omapdss_stack_is_ready(void) 3081e08c822SPeter Ujfalusi { 3091e08c822SPeter Ujfalusi struct omapdss_comp_node *comp; 3101e08c822SPeter Ujfalusi 3111e08c822SPeter Ujfalusi list_for_each_entry(comp, &omapdss_comp_list, list) { 3121e08c822SPeter Ujfalusi if (!omapdss_component_is_loaded(comp)) 3131e08c822SPeter Ujfalusi return false; 3141e08c822SPeter Ujfalusi } 3151e08c822SPeter Ujfalusi 3161e08c822SPeter Ujfalusi return true; 3171e08c822SPeter Ujfalusi } 3181e08c822SPeter Ujfalusi EXPORT_SYMBOL(omapdss_stack_is_ready); 3191e08c822SPeter Ujfalusi 320a99ac0d9STomi Valkeinen MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); 321a99ac0d9STomi Valkeinen MODULE_DESCRIPTION("OMAP Display Subsystem Base"); 322a99ac0d9STomi Valkeinen MODULE_LICENSE("GPL v2"); 323