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 246a7c5a22SLaurent Pinchart /* ----------------------------------------------------------------------------- 256a7c5a22SLaurent Pinchart * OMAP DSS Devices Handling 266a7c5a22SLaurent Pinchart */ 276a7c5a22SLaurent Pinchart 286a7c5a22SLaurent Pinchart static LIST_HEAD(omapdss_devices_list); 296a7c5a22SLaurent Pinchart static DEFINE_MUTEX(omapdss_devices_lock); 306a7c5a22SLaurent Pinchart 316a7c5a22SLaurent Pinchart void omapdss_device_register(struct omap_dss_device *dssdev) 326a7c5a22SLaurent Pinchart { 336a7c5a22SLaurent Pinchart mutex_lock(&omapdss_devices_lock); 346a7c5a22SLaurent Pinchart list_add_tail(&dssdev->list, &omapdss_devices_list); 356a7c5a22SLaurent Pinchart mutex_unlock(&omapdss_devices_lock); 366a7c5a22SLaurent Pinchart } 376a7c5a22SLaurent Pinchart 386a7c5a22SLaurent Pinchart void omapdss_device_unregister(struct omap_dss_device *dssdev) 396a7c5a22SLaurent Pinchart { 406a7c5a22SLaurent Pinchart mutex_lock(&omapdss_devices_lock); 416a7c5a22SLaurent Pinchart list_del(&dssdev->list); 426a7c5a22SLaurent Pinchart mutex_unlock(&omapdss_devices_lock); 436a7c5a22SLaurent Pinchart } 446a7c5a22SLaurent Pinchart 459184f8d9SLaurent Pinchart static bool omapdss_device_is_registered(struct device_node *node) 469184f8d9SLaurent Pinchart { 479184f8d9SLaurent Pinchart struct omap_dss_device *dssdev; 489184f8d9SLaurent Pinchart bool found = false; 499184f8d9SLaurent Pinchart 509184f8d9SLaurent Pinchart mutex_lock(&omapdss_devices_lock); 519184f8d9SLaurent Pinchart 529184f8d9SLaurent Pinchart list_for_each_entry(dssdev, &omapdss_devices_list, list) { 539184f8d9SLaurent Pinchart if (dssdev->dev->of_node == node) { 549184f8d9SLaurent Pinchart found = true; 559184f8d9SLaurent Pinchart break; 569184f8d9SLaurent Pinchart } 579184f8d9SLaurent Pinchart } 589184f8d9SLaurent Pinchart 599184f8d9SLaurent Pinchart mutex_unlock(&omapdss_devices_lock); 609184f8d9SLaurent Pinchart return found; 619184f8d9SLaurent Pinchart } 629184f8d9SLaurent Pinchart 63c1dfe721SLaurent Pinchart struct omap_dss_device *omapdss_device_get(struct omap_dss_device *dssdev) 64c1dfe721SLaurent Pinchart { 6555b68fb8STomi Valkeinen if (get_device(dssdev->dev) == NULL) 66c1dfe721SLaurent Pinchart return NULL; 67c1dfe721SLaurent Pinchart 68c1dfe721SLaurent Pinchart return dssdev; 69c1dfe721SLaurent Pinchart } 70c1dfe721SLaurent Pinchart 71c1dfe721SLaurent Pinchart void omapdss_device_put(struct omap_dss_device *dssdev) 72c1dfe721SLaurent Pinchart { 73c1dfe721SLaurent Pinchart put_device(dssdev->dev); 74c1dfe721SLaurent Pinchart } 75c1dfe721SLaurent Pinchart 76ce69aac8SLaurent Pinchart struct omap_dss_device *omapdss_find_device_by_node(struct device_node *node) 77e10bd354SLaurent Pinchart { 78e10bd354SLaurent Pinchart struct omap_dss_device *dssdev; 79e10bd354SLaurent Pinchart 80e10bd354SLaurent Pinchart list_for_each_entry(dssdev, &omapdss_devices_list, list) { 81ce69aac8SLaurent Pinchart if (dssdev->dev->of_node == node) 82c1dfe721SLaurent Pinchart return omapdss_device_get(dssdev); 83e10bd354SLaurent Pinchart } 84e10bd354SLaurent Pinchart 85e10bd354SLaurent Pinchart return NULL; 86e10bd354SLaurent Pinchart } 87e10bd354SLaurent Pinchart 88b9f4d2ebSLaurent Pinchart /* 8919b4200dSLaurent Pinchart * Search for the next output device starting at @from. Release the reference to 9019b4200dSLaurent Pinchart * the @from device, and acquire a reference to the returned device if found. 91b9f4d2ebSLaurent Pinchart */ 9219b4200dSLaurent Pinchart struct omap_dss_device *omapdss_device_next_output(struct omap_dss_device *from) 93b9f4d2ebSLaurent Pinchart { 94b9f4d2ebSLaurent Pinchart struct omap_dss_device *dssdev; 95b9f4d2ebSLaurent Pinchart struct list_head *list; 96b9f4d2ebSLaurent Pinchart 97b9f4d2ebSLaurent Pinchart mutex_lock(&omapdss_devices_lock); 98b9f4d2ebSLaurent Pinchart 99b9f4d2ebSLaurent Pinchart if (list_empty(&omapdss_devices_list)) { 100b9f4d2ebSLaurent Pinchart dssdev = NULL; 101b9f4d2ebSLaurent Pinchart goto done; 102b9f4d2ebSLaurent Pinchart } 103b9f4d2ebSLaurent Pinchart 104b9f4d2ebSLaurent Pinchart /* 105b9f4d2ebSLaurent Pinchart * Start from the from entry if given or from omapdss_devices_list 106b9f4d2ebSLaurent Pinchart * otherwise. 107b9f4d2ebSLaurent Pinchart */ 108b9f4d2ebSLaurent Pinchart list = from ? &from->list : &omapdss_devices_list; 109b9f4d2ebSLaurent Pinchart 110b9f4d2ebSLaurent Pinchart list_for_each_entry(dssdev, list, list) { 111b9f4d2ebSLaurent Pinchart /* 112b9f4d2ebSLaurent Pinchart * Stop if we reach the omapdss_devices_list, that's the end of 113b9f4d2ebSLaurent Pinchart * the list. 114b9f4d2ebSLaurent Pinchart */ 115b9f4d2ebSLaurent Pinchart if (&dssdev->list == &omapdss_devices_list) { 116b9f4d2ebSLaurent Pinchart dssdev = NULL; 117b9f4d2ebSLaurent Pinchart goto done; 118b9f4d2ebSLaurent Pinchart } 119b9f4d2ebSLaurent Pinchart 120811860ddSSebastian Reichel if (dssdev->id && dssdev->bridge) 121b9f4d2ebSLaurent Pinchart goto done; 122b9f4d2ebSLaurent Pinchart } 123b9f4d2ebSLaurent Pinchart 124b9f4d2ebSLaurent Pinchart dssdev = NULL; 125b9f4d2ebSLaurent Pinchart 126b9f4d2ebSLaurent Pinchart done: 127b9f4d2ebSLaurent Pinchart if (from) 128c1dfe721SLaurent Pinchart omapdss_device_put(from); 129b9f4d2ebSLaurent Pinchart if (dssdev) 130c1dfe721SLaurent Pinchart omapdss_device_get(dssdev); 131b9f4d2ebSLaurent Pinchart 132b9f4d2ebSLaurent Pinchart mutex_unlock(&omapdss_devices_lock); 133b9f4d2ebSLaurent Pinchart return dssdev; 134b9f4d2ebSLaurent Pinchart } 135b9f4d2ebSLaurent Pinchart 136b49a2139SLaurent Pinchart static bool omapdss_device_is_connected(struct omap_dss_device *dssdev) 137b49a2139SLaurent Pinchart { 138df6682b4SLaurent Pinchart return dssdev->dss; 139b49a2139SLaurent Pinchart } 140b49a2139SLaurent Pinchart 141f324b279SLaurent Pinchart int omapdss_device_connect(struct dss_device *dss, 142ec727e3fSLaurent Pinchart struct omap_dss_device *dst) 143ec727e3fSLaurent Pinchart { 144*4da37fbaSTomi Valkeinen dev_dbg(&dss->pdev->dev, "connect(%s)\n", 14579107f27SLaurent Pinchart dst ? dev_name(dst->dev) : "NULL"); 14679107f27SLaurent Pinchart 147*4da37fbaSTomi Valkeinen if (!dst) 148*4da37fbaSTomi Valkeinen return -EINVAL; 1491f507968SLaurent Pinchart 150511afb44SLaurent Pinchart if (omapdss_device_is_connected(dst)) 1511f507968SLaurent Pinchart return -EBUSY; 1521f507968SLaurent Pinchart 153511afb44SLaurent Pinchart dst->dss = dss; 154f324b279SLaurent Pinchart 155fb557171SLaurent Pinchart return 0; 156ec727e3fSLaurent Pinchart } 157ec727e3fSLaurent Pinchart 158*4da37fbaSTomi Valkeinen void omapdss_device_disconnect(struct dss_device *dss, 159ec727e3fSLaurent Pinchart struct omap_dss_device *dst) 160ec727e3fSLaurent Pinchart { 161*4da37fbaSTomi Valkeinen dev_dbg(&dss->pdev->dev, "disconnect(%s)\n", 16279107f27SLaurent Pinchart dst ? dev_name(dst->dev) : "NULL"); 16379107f27SLaurent Pinchart 164*4da37fbaSTomi Valkeinen if (WARN_ON(!dst)) 16579107f27SLaurent Pinchart return; 1661f507968SLaurent Pinchart 167511afb44SLaurent Pinchart if (!dst->id && !omapdss_device_is_connected(dst)) { 1682390fadbSSebastian Reichel WARN_ON(1); 1691f507968SLaurent Pinchart return; 1701f507968SLaurent Pinchart } 1711f507968SLaurent Pinchart 172511afb44SLaurent Pinchart dst->dss = NULL; 173ec727e3fSLaurent Pinchart } 174ec727e3fSLaurent Pinchart 1756a7c5a22SLaurent Pinchart /* ----------------------------------------------------------------------------- 1766a7c5a22SLaurent Pinchart * Components Handling 1776a7c5a22SLaurent Pinchart */ 1786a7c5a22SLaurent Pinchart 1796a7c5a22SLaurent Pinchart static struct list_head omapdss_comp_list; 1806a7c5a22SLaurent Pinchart 1816a7c5a22SLaurent Pinchart struct omapdss_comp_node { 1826a7c5a22SLaurent Pinchart struct list_head list; 1836a7c5a22SLaurent Pinchart struct device_node *node; 1846a7c5a22SLaurent Pinchart bool dss_core_component; 1854e17763cSLaurent Pinchart const char *compat; 1866a7c5a22SLaurent Pinchart }; 1876a7c5a22SLaurent Pinchart 1881e08c822SPeter Ujfalusi static bool omapdss_list_contains(const struct device_node *node) 1891e08c822SPeter Ujfalusi { 1901e08c822SPeter Ujfalusi struct omapdss_comp_node *comp; 1911e08c822SPeter Ujfalusi 1921e08c822SPeter Ujfalusi list_for_each_entry(comp, &omapdss_comp_list, list) { 1931e08c822SPeter Ujfalusi if (comp->node == node) 1941e08c822SPeter Ujfalusi return true; 1951e08c822SPeter Ujfalusi } 1961e08c822SPeter Ujfalusi 1971e08c822SPeter Ujfalusi return false; 1981e08c822SPeter Ujfalusi } 1991e08c822SPeter Ujfalusi 2001e08c822SPeter Ujfalusi static void omapdss_walk_device(struct device *dev, struct device_node *node, 2011e08c822SPeter Ujfalusi bool dss_core) 2021e08c822SPeter Ujfalusi { 2034e17763cSLaurent Pinchart struct omapdss_comp_node *comp; 2041e08c822SPeter Ujfalusi struct device_node *n; 2054e17763cSLaurent Pinchart const char *compat; 2064e17763cSLaurent Pinchart int ret; 2071e08c822SPeter Ujfalusi 2084e17763cSLaurent Pinchart ret = of_property_read_string(node, "compatible", &compat); 2094e17763cSLaurent Pinchart if (ret < 0) 2104e17763cSLaurent Pinchart return; 2114e17763cSLaurent Pinchart 2124e17763cSLaurent Pinchart comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL); 2131e08c822SPeter Ujfalusi if (comp) { 2141e08c822SPeter Ujfalusi comp->node = node; 2151e08c822SPeter Ujfalusi comp->dss_core_component = dss_core; 2164e17763cSLaurent Pinchart comp->compat = compat; 2171e08c822SPeter Ujfalusi list_add(&comp->list, &omapdss_comp_list); 2181e08c822SPeter Ujfalusi } 2191e08c822SPeter Ujfalusi 2201e08c822SPeter Ujfalusi /* 2211e08c822SPeter Ujfalusi * of_graph_get_remote_port_parent() prints an error if there is no 2221e08c822SPeter Ujfalusi * port/ports node. To avoid that, check first that there's the node. 2231e08c822SPeter Ujfalusi */ 2241e08c822SPeter Ujfalusi n = of_get_child_by_name(node, "ports"); 2251e08c822SPeter Ujfalusi if (!n) 2261e08c822SPeter Ujfalusi n = of_get_child_by_name(node, "port"); 2271e08c822SPeter Ujfalusi if (!n) 2281e08c822SPeter Ujfalusi return; 2291e08c822SPeter Ujfalusi 2301e08c822SPeter Ujfalusi of_node_put(n); 2311e08c822SPeter Ujfalusi 2321e08c822SPeter Ujfalusi n = NULL; 2331e08c822SPeter Ujfalusi while ((n = of_graph_get_next_endpoint(node, n)) != NULL) { 2341e08c822SPeter Ujfalusi struct device_node *pn = of_graph_get_remote_port_parent(n); 2351e08c822SPeter Ujfalusi 2361e08c822SPeter Ujfalusi if (!pn) 2371e08c822SPeter Ujfalusi continue; 2381e08c822SPeter Ujfalusi 2391e08c822SPeter Ujfalusi if (!of_device_is_available(pn) || omapdss_list_contains(pn)) { 2401e08c822SPeter Ujfalusi of_node_put(pn); 2411e08c822SPeter Ujfalusi continue; 2421e08c822SPeter Ujfalusi } 2431e08c822SPeter Ujfalusi 2441e08c822SPeter Ujfalusi omapdss_walk_device(dev, pn, false); 2451e08c822SPeter Ujfalusi } 2461e08c822SPeter Ujfalusi } 2471e08c822SPeter Ujfalusi 2481e08c822SPeter Ujfalusi void omapdss_gather_components(struct device *dev) 2491e08c822SPeter Ujfalusi { 2501e08c822SPeter Ujfalusi struct device_node *child; 2511e08c822SPeter Ujfalusi 2521e08c822SPeter Ujfalusi INIT_LIST_HEAD(&omapdss_comp_list); 2531e08c822SPeter Ujfalusi 2541e08c822SPeter Ujfalusi omapdss_walk_device(dev, dev->of_node, true); 2551e08c822SPeter Ujfalusi 2564e17763cSLaurent Pinchart for_each_available_child_of_node(dev->of_node, child) 2571e08c822SPeter Ujfalusi omapdss_walk_device(dev, child, true); 2581e08c822SPeter Ujfalusi } 2591e08c822SPeter Ujfalusi 2601e08c822SPeter Ujfalusi static bool omapdss_component_is_loaded(struct omapdss_comp_node *comp) 2611e08c822SPeter Ujfalusi { 2621e08c822SPeter Ujfalusi if (comp->dss_core_component) 2631e08c822SPeter Ujfalusi return true; 2644e17763cSLaurent Pinchart if (!strstarts(comp->compat, "omapdss,")) 2654e17763cSLaurent Pinchart return true; 2669184f8d9SLaurent Pinchart if (omapdss_device_is_registered(comp->node)) 2671e08c822SPeter Ujfalusi return true; 2681e08c822SPeter Ujfalusi 2691e08c822SPeter Ujfalusi return false; 2701e08c822SPeter Ujfalusi } 2711e08c822SPeter Ujfalusi 2721e08c822SPeter Ujfalusi bool omapdss_stack_is_ready(void) 2731e08c822SPeter Ujfalusi { 2741e08c822SPeter Ujfalusi struct omapdss_comp_node *comp; 2751e08c822SPeter Ujfalusi 2761e08c822SPeter Ujfalusi list_for_each_entry(comp, &omapdss_comp_list, list) { 2771e08c822SPeter Ujfalusi if (!omapdss_component_is_loaded(comp)) 2781e08c822SPeter Ujfalusi return false; 2791e08c822SPeter Ujfalusi } 2801e08c822SPeter Ujfalusi 2811e08c822SPeter Ujfalusi return true; 2821e08c822SPeter Ujfalusi } 283