1 /* 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 16 #include <linux/kernel.h> 17 #include <linux/list.h> 18 #include <linux/module.h> 19 #include <linux/mutex.h> 20 #include <linux/of.h> 21 #include <linux/of_graph.h> 22 #include <linux/platform_device.h> 23 24 #include "dss.h" 25 #include "omapdss.h" 26 27 static struct dss_device *dss_device; 28 29 struct dss_device *omapdss_get_dss(void) 30 { 31 return dss_device; 32 } 33 EXPORT_SYMBOL(omapdss_get_dss); 34 35 void omapdss_set_dss(struct dss_device *dss) 36 { 37 dss_device = dss; 38 } 39 EXPORT_SYMBOL(omapdss_set_dss); 40 41 struct dispc_device *dispc_get_dispc(struct dss_device *dss) 42 { 43 return dss->dispc; 44 } 45 EXPORT_SYMBOL(dispc_get_dispc); 46 47 const struct dispc_ops *dispc_get_ops(struct dss_device *dss) 48 { 49 return dss->dispc_ops; 50 } 51 EXPORT_SYMBOL(dispc_get_ops); 52 53 54 /* ----------------------------------------------------------------------------- 55 * OMAP DSS Devices Handling 56 */ 57 58 static LIST_HEAD(omapdss_devices_list); 59 static DEFINE_MUTEX(omapdss_devices_lock); 60 61 void 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 } 67 EXPORT_SYMBOL_GPL(omapdss_device_register); 68 69 void 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 } 75 EXPORT_SYMBOL_GPL(omapdss_device_unregister); 76 77 static 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 95 struct 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 } 107 EXPORT_SYMBOL(omapdss_device_get); 108 109 void omapdss_device_put(struct omap_dss_device *dssdev) 110 { 111 put_device(dssdev->dev); 112 module_put(dssdev->owner); 113 } 114 EXPORT_SYMBOL(omapdss_device_put); 115 116 struct omap_dss_device *omapdss_find_device_by_node(struct device_node *node) 117 { 118 struct omap_dss_device *dssdev; 119 120 list_for_each_entry(dssdev, &omapdss_devices_list, list) { 121 if (dssdev->dev->of_node == node) 122 return omapdss_device_get(dssdev); 123 } 124 125 return NULL; 126 } 127 128 /* 129 * 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. 131 */ 132 struct omap_dss_device *omapdss_device_next_output(struct omap_dss_device *from) 133 { 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 160 if (dssdev->id && 161 (dssdev->next || dssdev->bridge || dssdev->panel)) 162 goto done; 163 } 164 165 dssdev = NULL; 166 167 done: 168 if (from) 169 omapdss_device_put(from); 170 if (dssdev) 171 omapdss_device_get(dssdev); 172 173 mutex_unlock(&omapdss_devices_lock); 174 return dssdev; 175 } 176 EXPORT_SYMBOL(omapdss_device_next_output); 177 178 static bool omapdss_device_is_connected(struct omap_dss_device *dssdev) 179 { 180 return dssdev->dss; 181 } 182 183 int omapdss_device_connect(struct dss_device *dss, 184 struct omap_dss_device *src, 185 struct omap_dss_device *dst) 186 { 187 int ret; 188 189 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 196 * 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. 199 */ 200 return src && (src->bridge || src->panel) ? 0 : -EINVAL; 201 } 202 203 if (omapdss_device_is_connected(dst)) 204 return -EBUSY; 205 206 dst->dss = dss; 207 208 ret = dst->ops->connect(src, dst); 209 if (ret < 0) { 210 dst->dss = NULL; 211 return ret; 212 } 213 214 return 0; 215 } 216 EXPORT_SYMBOL_GPL(omapdss_device_connect); 217 218 void omapdss_device_disconnect(struct omap_dss_device *src, 219 struct omap_dss_device *dst) 220 { 221 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) { 228 WARN_ON(!src->bridge && !src->panel); 229 return; 230 } 231 232 if (!dst->id && !omapdss_device_is_connected(dst)) { 233 WARN_ON(!dst->display); 234 return; 235 } 236 237 WARN_ON(dst->state != OMAP_DSS_DISPLAY_DISABLED); 238 239 dst->ops->disconnect(src, dst); 240 dst->dss = NULL; 241 } 242 EXPORT_SYMBOL_GPL(omapdss_device_disconnect); 243 244 void 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 } 254 EXPORT_SYMBOL_GPL(omapdss_device_pre_enable); 255 256 void 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 } 268 EXPORT_SYMBOL_GPL(omapdss_device_enable); 269 270 void 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 } 280 EXPORT_SYMBOL_GPL(omapdss_device_disable); 281 282 void 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 } 294 EXPORT_SYMBOL_GPL(omapdss_device_post_disable); 295 296 /* ----------------------------------------------------------------------------- 297 * Components Handling 298 */ 299 300 static struct list_head omapdss_comp_list; 301 302 struct omapdss_comp_node { 303 struct list_head list; 304 struct device_node *node; 305 bool dss_core_component; 306 const char *compat; 307 }; 308 309 static 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 321 static void omapdss_walk_device(struct device *dev, struct device_node *node, 322 bool dss_core) 323 { 324 struct omapdss_comp_node *comp; 325 struct device_node *n; 326 const char *compat; 327 int ret; 328 329 ret = of_property_read_string(node, "compatible", &compat); 330 if (ret < 0) 331 return; 332 333 comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL); 334 if (comp) { 335 comp->node = node; 336 comp->dss_core_component = dss_core; 337 comp->compat = compat; 338 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 369 void 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 377 for_each_available_child_of_node(dev->of_node, child) 378 omapdss_walk_device(dev, child, true); 379 } 380 EXPORT_SYMBOL(omapdss_gather_components); 381 382 static bool omapdss_component_is_loaded(struct omapdss_comp_node *comp) 383 { 384 if (comp->dss_core_component) 385 return true; 386 if (!strstarts(comp->compat, "omapdss,")) 387 return true; 388 if (omapdss_device_is_registered(comp->node)) 389 return true; 390 391 return false; 392 } 393 394 bool 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 } 405 EXPORT_SYMBOL(omapdss_stack_is_ready); 406 407 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); 408 MODULE_DESCRIPTION("OMAP Display Subsystem Base"); 409 MODULE_LICENSE("GPL v2"); 410