1bb5cdf8dSAndrew F. Davis /* 2bb5cdf8dSAndrew F. Davis * OMAP Display Subsystem Base 3bb5cdf8dSAndrew F. Davis * 4bb5cdf8dSAndrew F. Davis * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com/ 5bb5cdf8dSAndrew F. Davis * 6bb5cdf8dSAndrew F. Davis * This program is free software; you can redistribute it and/or modify 7bb5cdf8dSAndrew F. Davis * it under the terms of the GNU General Public License version 2 as 8bb5cdf8dSAndrew F. Davis * published by the Free Software Foundation. 9bb5cdf8dSAndrew F. Davis * 10bb5cdf8dSAndrew F. Davis * This program is distributed in the hope that it will be useful, but 11bb5cdf8dSAndrew F. Davis * WITHOUT ANY WARRANTY; without even the implied warranty of 12bb5cdf8dSAndrew F. Davis * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13bb5cdf8dSAndrew F. Davis * General Public License for more details. 14bb5cdf8dSAndrew F. Davis */ 15bb5cdf8dSAndrew F. Davis 16a99ac0d9STomi Valkeinen #include <linux/kernel.h> 176a7c5a22SLaurent Pinchart #include <linux/list.h> 18a99ac0d9STomi Valkeinen #include <linux/module.h> 196a7c5a22SLaurent Pinchart #include <linux/mutex.h> 201e08c822SPeter Ujfalusi #include <linux/of.h> 211e08c822SPeter Ujfalusi #include <linux/of_graph.h> 22d3541ca8SLaurent Pinchart 23d3541ca8SLaurent Pinchart #include "dss.h" 241e08c822SPeter Ujfalusi #include "omapdss.h" 25a99ac0d9STomi Valkeinen 2672877cf3SLaurent Pinchart static struct dss_device *dss_device; 277c299716STomi Valkeinen 2872877cf3SLaurent Pinchart struct dss_device *omapdss_get_dss(void) 297c299716STomi Valkeinen { 3072877cf3SLaurent Pinchart return dss_device; 317c299716STomi Valkeinen } 3272877cf3SLaurent Pinchart EXPORT_SYMBOL(omapdss_get_dss); 337c299716STomi Valkeinen 3472877cf3SLaurent Pinchart void omapdss_set_dss(struct dss_device *dss) 357c299716STomi Valkeinen { 3672877cf3SLaurent Pinchart dss_device = dss; 377c299716STomi Valkeinen } 3872877cf3SLaurent Pinchart EXPORT_SYMBOL(omapdss_set_dss); 397c299716STomi Valkeinen 4050638ae5SLaurent Pinchart struct dispc_device *dispc_get_dispc(struct dss_device *dss) 4150638ae5SLaurent Pinchart { 4250638ae5SLaurent Pinchart return dss->dispc; 4350638ae5SLaurent Pinchart } 4450638ae5SLaurent Pinchart EXPORT_SYMBOL(dispc_get_dispc); 4550638ae5SLaurent Pinchart 46d3541ca8SLaurent Pinchart const struct dispc_ops *dispc_get_ops(struct dss_device *dss) 478a13398cSTomi Valkeinen { 48d3541ca8SLaurent Pinchart return dss->dispc_ops; 498a13398cSTomi Valkeinen } 508a13398cSTomi Valkeinen EXPORT_SYMBOL(dispc_get_ops); 518a13398cSTomi Valkeinen 526a7c5a22SLaurent Pinchart 536a7c5a22SLaurent Pinchart /* ----------------------------------------------------------------------------- 546a7c5a22SLaurent Pinchart * OMAP DSS Devices Handling 556a7c5a22SLaurent Pinchart */ 566a7c5a22SLaurent Pinchart 576a7c5a22SLaurent Pinchart static LIST_HEAD(omapdss_devices_list); 586a7c5a22SLaurent Pinchart static DEFINE_MUTEX(omapdss_devices_lock); 596a7c5a22SLaurent Pinchart 606a7c5a22SLaurent Pinchart void omapdss_device_register(struct omap_dss_device *dssdev) 616a7c5a22SLaurent Pinchart { 626a7c5a22SLaurent Pinchart mutex_lock(&omapdss_devices_lock); 636a7c5a22SLaurent Pinchart list_add_tail(&dssdev->list, &omapdss_devices_list); 646a7c5a22SLaurent Pinchart mutex_unlock(&omapdss_devices_lock); 656a7c5a22SLaurent Pinchart } 66de57e9dbSLaurent Pinchart EXPORT_SYMBOL_GPL(omapdss_device_register); 676a7c5a22SLaurent Pinchart 686a7c5a22SLaurent Pinchart void omapdss_device_unregister(struct omap_dss_device *dssdev) 696a7c5a22SLaurent Pinchart { 706a7c5a22SLaurent Pinchart mutex_lock(&omapdss_devices_lock); 716a7c5a22SLaurent Pinchart list_del(&dssdev->list); 726a7c5a22SLaurent Pinchart mutex_unlock(&omapdss_devices_lock); 736a7c5a22SLaurent Pinchart } 74de57e9dbSLaurent Pinchart EXPORT_SYMBOL_GPL(omapdss_device_unregister); 756a7c5a22SLaurent Pinchart 769184f8d9SLaurent Pinchart static bool omapdss_device_is_registered(struct device_node *node) 779184f8d9SLaurent Pinchart { 789184f8d9SLaurent Pinchart struct omap_dss_device *dssdev; 799184f8d9SLaurent Pinchart bool found = false; 809184f8d9SLaurent Pinchart 819184f8d9SLaurent Pinchart mutex_lock(&omapdss_devices_lock); 829184f8d9SLaurent Pinchart 839184f8d9SLaurent Pinchart list_for_each_entry(dssdev, &omapdss_devices_list, list) { 849184f8d9SLaurent Pinchart if (dssdev->dev->of_node == node) { 859184f8d9SLaurent Pinchart found = true; 869184f8d9SLaurent Pinchart break; 879184f8d9SLaurent Pinchart } 889184f8d9SLaurent Pinchart } 899184f8d9SLaurent Pinchart 909184f8d9SLaurent Pinchart mutex_unlock(&omapdss_devices_lock); 919184f8d9SLaurent Pinchart return found; 929184f8d9SLaurent Pinchart } 939184f8d9SLaurent Pinchart 94c1dfe721SLaurent Pinchart struct omap_dss_device *omapdss_device_get(struct omap_dss_device *dssdev) 95c1dfe721SLaurent Pinchart { 96c1dfe721SLaurent Pinchart if (!try_module_get(dssdev->owner)) 97c1dfe721SLaurent Pinchart return NULL; 98c1dfe721SLaurent Pinchart 99c1dfe721SLaurent Pinchart if (get_device(dssdev->dev) == NULL) { 100c1dfe721SLaurent Pinchart module_put(dssdev->owner); 101c1dfe721SLaurent Pinchart return NULL; 102c1dfe721SLaurent Pinchart } 103c1dfe721SLaurent Pinchart 104c1dfe721SLaurent Pinchart return dssdev; 105c1dfe721SLaurent Pinchart } 106c1dfe721SLaurent Pinchart EXPORT_SYMBOL(omapdss_device_get); 107c1dfe721SLaurent Pinchart 108c1dfe721SLaurent Pinchart void omapdss_device_put(struct omap_dss_device *dssdev) 109c1dfe721SLaurent Pinchart { 110c1dfe721SLaurent Pinchart put_device(dssdev->dev); 111c1dfe721SLaurent Pinchart module_put(dssdev->owner); 112c1dfe721SLaurent Pinchart } 113c1dfe721SLaurent Pinchart EXPORT_SYMBOL(omapdss_device_put); 114c1dfe721SLaurent Pinchart 115e10bd354SLaurent Pinchart struct omap_dss_device *omapdss_find_device_by_port(struct device_node *src, 116e10bd354SLaurent Pinchart unsigned int port) 117e10bd354SLaurent Pinchart { 118e10bd354SLaurent Pinchart struct omap_dss_device *dssdev; 119e10bd354SLaurent Pinchart 120e10bd354SLaurent Pinchart list_for_each_entry(dssdev, &omapdss_devices_list, list) { 1214e20bda6SLaurent Pinchart if (dssdev->dev->of_node == src && dssdev->of_ports & BIT(port)) 122c1dfe721SLaurent Pinchart return omapdss_device_get(dssdev); 123e10bd354SLaurent Pinchart } 124e10bd354SLaurent Pinchart 125e10bd354SLaurent Pinchart return NULL; 126e10bd354SLaurent Pinchart } 127e10bd354SLaurent Pinchart 128b9f4d2ebSLaurent Pinchart /* 129f7e376aeSLaurent Pinchart * Search for the next device starting at @from. The type argument specfies 130f7e376aeSLaurent Pinchart * which device types to consider when searching. Searching for multiple types 131f7e376aeSLaurent Pinchart * is supported by and'ing their type flags. Release the reference to the @from 132f7e376aeSLaurent Pinchart * device, and acquire a reference to the returned device if found. 133b9f4d2ebSLaurent Pinchart */ 134b9f4d2ebSLaurent Pinchart struct omap_dss_device *omapdss_device_get_next(struct omap_dss_device *from, 135f7e376aeSLaurent Pinchart enum omap_dss_device_type type) 136b9f4d2ebSLaurent Pinchart { 137b9f4d2ebSLaurent Pinchart struct omap_dss_device *dssdev; 138b9f4d2ebSLaurent Pinchart struct list_head *list; 139b9f4d2ebSLaurent Pinchart 140b9f4d2ebSLaurent Pinchart mutex_lock(&omapdss_devices_lock); 141b9f4d2ebSLaurent Pinchart 142b9f4d2ebSLaurent Pinchart if (list_empty(&omapdss_devices_list)) { 143b9f4d2ebSLaurent Pinchart dssdev = NULL; 144b9f4d2ebSLaurent Pinchart goto done; 145b9f4d2ebSLaurent Pinchart } 146b9f4d2ebSLaurent Pinchart 147b9f4d2ebSLaurent Pinchart /* 148b9f4d2ebSLaurent Pinchart * Start from the from entry if given or from omapdss_devices_list 149b9f4d2ebSLaurent Pinchart * otherwise. 150b9f4d2ebSLaurent Pinchart */ 151b9f4d2ebSLaurent Pinchart list = from ? &from->list : &omapdss_devices_list; 152b9f4d2ebSLaurent Pinchart 153b9f4d2ebSLaurent Pinchart list_for_each_entry(dssdev, list, list) { 154b9f4d2ebSLaurent Pinchart /* 155b9f4d2ebSLaurent Pinchart * Stop if we reach the omapdss_devices_list, that's the end of 156b9f4d2ebSLaurent Pinchart * the list. 157b9f4d2ebSLaurent Pinchart */ 158b9f4d2ebSLaurent Pinchart if (&dssdev->list == &omapdss_devices_list) { 159b9f4d2ebSLaurent Pinchart dssdev = NULL; 160b9f4d2ebSLaurent Pinchart goto done; 161b9f4d2ebSLaurent Pinchart } 162b9f4d2ebSLaurent Pinchart 163f7e376aeSLaurent Pinchart /* 164f7e376aeSLaurent Pinchart * Accept display entities if the display type is requested, 165f7e376aeSLaurent Pinchart * and output entities if the output type is requested. 166f7e376aeSLaurent Pinchart */ 167f7e376aeSLaurent Pinchart if ((type & OMAP_DSS_DEVICE_TYPE_DISPLAY) && dssdev->driver) 168f7e376aeSLaurent Pinchart goto done; 169f7e376aeSLaurent Pinchart if ((type & OMAP_DSS_DEVICE_TYPE_OUTPUT) && dssdev->id && 170f7e376aeSLaurent Pinchart dssdev->next) 171b9f4d2ebSLaurent Pinchart goto done; 172b9f4d2ebSLaurent Pinchart } 173b9f4d2ebSLaurent Pinchart 174b9f4d2ebSLaurent Pinchart dssdev = NULL; 175b9f4d2ebSLaurent Pinchart 176b9f4d2ebSLaurent Pinchart done: 177b9f4d2ebSLaurent Pinchart if (from) 178c1dfe721SLaurent Pinchart omapdss_device_put(from); 179b9f4d2ebSLaurent Pinchart if (dssdev) 180c1dfe721SLaurent Pinchart omapdss_device_get(dssdev); 181b9f4d2ebSLaurent Pinchart 182b9f4d2ebSLaurent Pinchart mutex_unlock(&omapdss_devices_lock); 183b9f4d2ebSLaurent Pinchart return dssdev; 184b9f4d2ebSLaurent Pinchart } 185b9f4d2ebSLaurent Pinchart EXPORT_SYMBOL(omapdss_device_get_next); 186b9f4d2ebSLaurent Pinchart 187f324b279SLaurent Pinchart int omapdss_device_connect(struct dss_device *dss, 188f324b279SLaurent Pinchart struct omap_dss_device *src, 189ec727e3fSLaurent Pinchart struct omap_dss_device *dst) 190ec727e3fSLaurent Pinchart { 191fb557171SLaurent Pinchart int ret; 192fb557171SLaurent Pinchart 193*511afb44SLaurent Pinchart dev_dbg(dst->dev, "connect\n"); 1941f507968SLaurent Pinchart 195*511afb44SLaurent Pinchart if (omapdss_device_is_connected(dst)) 1961f507968SLaurent Pinchart return -EBUSY; 1971f507968SLaurent Pinchart 198*511afb44SLaurent Pinchart dst->dss = dss; 199f324b279SLaurent Pinchart 200*511afb44SLaurent Pinchart if (dst->driver) 201*511afb44SLaurent Pinchart ret = dst->driver->connect(src, dst); 202ec727e3fSLaurent Pinchart else 203*511afb44SLaurent Pinchart ret = dst->ops->connect(src, dst); 204fb557171SLaurent Pinchart 205f324b279SLaurent Pinchart if (ret < 0) { 206*511afb44SLaurent Pinchart dst->dss = NULL; 207fb557171SLaurent Pinchart return ret; 208f324b279SLaurent Pinchart } 209fb557171SLaurent Pinchart 210*511afb44SLaurent Pinchart if (src) { 211fb557171SLaurent Pinchart dst->src = src; 212fb557171SLaurent Pinchart src->dst = dst; 213fb557171SLaurent Pinchart } 214fb557171SLaurent Pinchart 215fb557171SLaurent Pinchart return 0; 216ec727e3fSLaurent Pinchart } 217ec727e3fSLaurent Pinchart EXPORT_SYMBOL_GPL(omapdss_device_connect); 218ec727e3fSLaurent Pinchart 219ec727e3fSLaurent Pinchart void omapdss_device_disconnect(struct omap_dss_device *src, 220ec727e3fSLaurent Pinchart struct omap_dss_device *dst) 221ec727e3fSLaurent Pinchart { 222*511afb44SLaurent Pinchart dev_dbg(dst->dev, "disconnect\n"); 2231f507968SLaurent Pinchart 224*511afb44SLaurent Pinchart if (!dst->id && !omapdss_device_is_connected(dst)) { 225*511afb44SLaurent Pinchart WARN_ON(!dst->driver); 2261f507968SLaurent Pinchart return; 2271f507968SLaurent Pinchart } 2281f507968SLaurent Pinchart 229*511afb44SLaurent Pinchart if (src) { 230fb557171SLaurent Pinchart if (WARN_ON(dst != src->dst)) 231fb557171SLaurent Pinchart return; 232fb557171SLaurent Pinchart 233fb557171SLaurent Pinchart dst->src = NULL; 234fb557171SLaurent Pinchart src->dst = NULL; 235fb557171SLaurent Pinchart } 236fb557171SLaurent Pinchart 237*511afb44SLaurent Pinchart if (dst->driver) 238*511afb44SLaurent Pinchart dst->driver->disconnect(src, dst); 239ec727e3fSLaurent Pinchart else 240*511afb44SLaurent Pinchart dst->ops->disconnect(src, dst); 241f324b279SLaurent Pinchart 242*511afb44SLaurent Pinchart dst->dss = NULL; 243ec727e3fSLaurent Pinchart } 244ec727e3fSLaurent Pinchart EXPORT_SYMBOL_GPL(omapdss_device_disconnect); 245ec727e3fSLaurent Pinchart 2465c718e01SLaurent Pinchart enum omap_channel omapdss_device_get_dispc_channel(struct omap_dss_device *dssdev) 2475c718e01SLaurent Pinchart { 2485c718e01SLaurent Pinchart while (dssdev->src) 2495c718e01SLaurent Pinchart dssdev = dssdev->src; 2505c718e01SLaurent Pinchart 2515c718e01SLaurent Pinchart return dssdev->dispc_channel; 2525c718e01SLaurent Pinchart } 2535c718e01SLaurent Pinchart EXPORT_SYMBOL(omapdss_device_get_dispc_channel); 2545c718e01SLaurent Pinchart 2556a7c5a22SLaurent Pinchart /* ----------------------------------------------------------------------------- 2566a7c5a22SLaurent Pinchart * Components Handling 2576a7c5a22SLaurent Pinchart */ 2586a7c5a22SLaurent Pinchart 2596a7c5a22SLaurent Pinchart static struct list_head omapdss_comp_list; 2606a7c5a22SLaurent Pinchart 2616a7c5a22SLaurent Pinchart struct omapdss_comp_node { 2626a7c5a22SLaurent Pinchart struct list_head list; 2636a7c5a22SLaurent Pinchart struct device_node *node; 2646a7c5a22SLaurent Pinchart bool dss_core_component; 2656a7c5a22SLaurent Pinchart }; 2666a7c5a22SLaurent Pinchart 2671e08c822SPeter Ujfalusi static bool omapdss_list_contains(const struct device_node *node) 2681e08c822SPeter Ujfalusi { 2691e08c822SPeter Ujfalusi struct omapdss_comp_node *comp; 2701e08c822SPeter Ujfalusi 2711e08c822SPeter Ujfalusi list_for_each_entry(comp, &omapdss_comp_list, list) { 2721e08c822SPeter Ujfalusi if (comp->node == node) 2731e08c822SPeter Ujfalusi return true; 2741e08c822SPeter Ujfalusi } 2751e08c822SPeter Ujfalusi 2761e08c822SPeter Ujfalusi return false; 2771e08c822SPeter Ujfalusi } 2781e08c822SPeter Ujfalusi 2791e08c822SPeter Ujfalusi static void omapdss_walk_device(struct device *dev, struct device_node *node, 2801e08c822SPeter Ujfalusi bool dss_core) 2811e08c822SPeter Ujfalusi { 2821e08c822SPeter Ujfalusi struct device_node *n; 2831e08c822SPeter Ujfalusi struct omapdss_comp_node *comp = devm_kzalloc(dev, sizeof(*comp), 2841e08c822SPeter Ujfalusi GFP_KERNEL); 2851e08c822SPeter Ujfalusi 2861e08c822SPeter Ujfalusi if (comp) { 2871e08c822SPeter Ujfalusi comp->node = node; 2881e08c822SPeter Ujfalusi comp->dss_core_component = dss_core; 2891e08c822SPeter Ujfalusi list_add(&comp->list, &omapdss_comp_list); 2901e08c822SPeter Ujfalusi } 2911e08c822SPeter Ujfalusi 2921e08c822SPeter Ujfalusi /* 2931e08c822SPeter Ujfalusi * of_graph_get_remote_port_parent() prints an error if there is no 2941e08c822SPeter Ujfalusi * port/ports node. To avoid that, check first that there's the node. 2951e08c822SPeter Ujfalusi */ 2961e08c822SPeter Ujfalusi n = of_get_child_by_name(node, "ports"); 2971e08c822SPeter Ujfalusi if (!n) 2981e08c822SPeter Ujfalusi n = of_get_child_by_name(node, "port"); 2991e08c822SPeter Ujfalusi if (!n) 3001e08c822SPeter Ujfalusi return; 3011e08c822SPeter Ujfalusi 3021e08c822SPeter Ujfalusi of_node_put(n); 3031e08c822SPeter Ujfalusi 3041e08c822SPeter Ujfalusi n = NULL; 3051e08c822SPeter Ujfalusi while ((n = of_graph_get_next_endpoint(node, n)) != NULL) { 3061e08c822SPeter Ujfalusi struct device_node *pn = of_graph_get_remote_port_parent(n); 3071e08c822SPeter Ujfalusi 3081e08c822SPeter Ujfalusi if (!pn) 3091e08c822SPeter Ujfalusi continue; 3101e08c822SPeter Ujfalusi 3111e08c822SPeter Ujfalusi if (!of_device_is_available(pn) || omapdss_list_contains(pn)) { 3121e08c822SPeter Ujfalusi of_node_put(pn); 3131e08c822SPeter Ujfalusi continue; 3141e08c822SPeter Ujfalusi } 3151e08c822SPeter Ujfalusi 3161e08c822SPeter Ujfalusi omapdss_walk_device(dev, pn, false); 3171e08c822SPeter Ujfalusi } 3181e08c822SPeter Ujfalusi } 3191e08c822SPeter Ujfalusi 3201e08c822SPeter Ujfalusi void omapdss_gather_components(struct device *dev) 3211e08c822SPeter Ujfalusi { 3221e08c822SPeter Ujfalusi struct device_node *child; 3231e08c822SPeter Ujfalusi 3241e08c822SPeter Ujfalusi INIT_LIST_HEAD(&omapdss_comp_list); 3251e08c822SPeter Ujfalusi 3261e08c822SPeter Ujfalusi omapdss_walk_device(dev, dev->of_node, true); 3271e08c822SPeter Ujfalusi 3281e08c822SPeter Ujfalusi for_each_available_child_of_node(dev->of_node, child) { 3291e08c822SPeter Ujfalusi if (!of_find_property(child, "compatible", NULL)) 3301e08c822SPeter Ujfalusi continue; 3311e08c822SPeter Ujfalusi 3321e08c822SPeter Ujfalusi omapdss_walk_device(dev, child, true); 3331e08c822SPeter Ujfalusi } 3341e08c822SPeter Ujfalusi } 3351e08c822SPeter Ujfalusi EXPORT_SYMBOL(omapdss_gather_components); 3361e08c822SPeter Ujfalusi 3371e08c822SPeter Ujfalusi static bool omapdss_component_is_loaded(struct omapdss_comp_node *comp) 3381e08c822SPeter Ujfalusi { 3391e08c822SPeter Ujfalusi if (comp->dss_core_component) 3401e08c822SPeter Ujfalusi return true; 3419184f8d9SLaurent Pinchart if (omapdss_device_is_registered(comp->node)) 3421e08c822SPeter Ujfalusi return true; 3431e08c822SPeter Ujfalusi 3441e08c822SPeter Ujfalusi return false; 3451e08c822SPeter Ujfalusi } 3461e08c822SPeter Ujfalusi 3471e08c822SPeter Ujfalusi bool omapdss_stack_is_ready(void) 3481e08c822SPeter Ujfalusi { 3491e08c822SPeter Ujfalusi struct omapdss_comp_node *comp; 3501e08c822SPeter Ujfalusi 3511e08c822SPeter Ujfalusi list_for_each_entry(comp, &omapdss_comp_list, list) { 3521e08c822SPeter Ujfalusi if (!omapdss_component_is_loaded(comp)) 3531e08c822SPeter Ujfalusi return false; 3541e08c822SPeter Ujfalusi } 3551e08c822SPeter Ujfalusi 3561e08c822SPeter Ujfalusi return true; 3571e08c822SPeter Ujfalusi } 3581e08c822SPeter Ujfalusi EXPORT_SYMBOL(omapdss_stack_is_ready); 3591e08c822SPeter Ujfalusi 360a99ac0d9STomi Valkeinen MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); 361a99ac0d9STomi Valkeinen MODULE_DESCRIPTION("OMAP Display Subsystem Base"); 362a99ac0d9STomi Valkeinen MODULE_LICENSE("GPL v2"); 363