linux/drivers/gpu/drm/omapdrm/dss/base.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * OMAP Display Subsystem Base
   4 *
   5 * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com/
   6 */
   7
   8#include <linux/kernel.h>
   9#include <linux/list.h>
  10#include <linux/module.h>
  11#include <linux/mutex.h>
  12#include <linux/of.h>
  13#include <linux/of_graph.h>
  14#include <linux/platform_device.h>
  15
  16#include "dss.h"
  17#include "omapdss.h"
  18
  19static struct dss_device *dss_device;
  20
  21struct dss_device *omapdss_get_dss(void)
  22{
  23        return dss_device;
  24}
  25EXPORT_SYMBOL(omapdss_get_dss);
  26
  27void omapdss_set_dss(struct dss_device *dss)
  28{
  29        dss_device = dss;
  30}
  31EXPORT_SYMBOL(omapdss_set_dss);
  32
  33struct dispc_device *dispc_get_dispc(struct dss_device *dss)
  34{
  35        return dss->dispc;
  36}
  37EXPORT_SYMBOL(dispc_get_dispc);
  38
  39const struct dispc_ops *dispc_get_ops(struct dss_device *dss)
  40{
  41        return dss->dispc_ops;
  42}
  43EXPORT_SYMBOL(dispc_get_ops);
  44
  45
  46/* -----------------------------------------------------------------------------
  47 * OMAP DSS Devices Handling
  48 */
  49
  50static LIST_HEAD(omapdss_devices_list);
  51static DEFINE_MUTEX(omapdss_devices_lock);
  52
  53void omapdss_device_register(struct omap_dss_device *dssdev)
  54{
  55        mutex_lock(&omapdss_devices_lock);
  56        list_add_tail(&dssdev->list, &omapdss_devices_list);
  57        mutex_unlock(&omapdss_devices_lock);
  58}
  59EXPORT_SYMBOL_GPL(omapdss_device_register);
  60
  61void omapdss_device_unregister(struct omap_dss_device *dssdev)
  62{
  63        mutex_lock(&omapdss_devices_lock);
  64        list_del(&dssdev->list);
  65        mutex_unlock(&omapdss_devices_lock);
  66}
  67EXPORT_SYMBOL_GPL(omapdss_device_unregister);
  68
  69static bool omapdss_device_is_registered(struct device_node *node)
  70{
  71        struct omap_dss_device *dssdev;
  72        bool found = false;
  73
  74        mutex_lock(&omapdss_devices_lock);
  75
  76        list_for_each_entry(dssdev, &omapdss_devices_list, list) {
  77                if (dssdev->dev->of_node == node) {
  78                        found = true;
  79                        break;
  80                }
  81        }
  82
  83        mutex_unlock(&omapdss_devices_lock);
  84        return found;
  85}
  86
  87struct omap_dss_device *omapdss_device_get(struct omap_dss_device *dssdev)
  88{
  89        if (!try_module_get(dssdev->owner))
  90                return NULL;
  91
  92        if (get_device(dssdev->dev) == NULL) {
  93                module_put(dssdev->owner);
  94                return NULL;
  95        }
  96
  97        return dssdev;
  98}
  99EXPORT_SYMBOL(omapdss_device_get);
 100
 101void omapdss_device_put(struct omap_dss_device *dssdev)
 102{
 103        put_device(dssdev->dev);
 104        module_put(dssdev->owner);
 105}
 106EXPORT_SYMBOL(omapdss_device_put);
 107
 108struct omap_dss_device *omapdss_find_device_by_node(struct device_node *node)
 109{
 110        struct omap_dss_device *dssdev;
 111
 112        list_for_each_entry(dssdev, &omapdss_devices_list, list) {
 113                if (dssdev->dev->of_node == node)
 114                        return omapdss_device_get(dssdev);
 115        }
 116
 117        return NULL;
 118}
 119
 120/*
 121 * Search for the next output device starting at @from. Release the reference to
 122 * the @from device, and acquire a reference to the returned device if found.
 123 */
 124struct omap_dss_device *omapdss_device_next_output(struct omap_dss_device *from)
 125{
 126        struct omap_dss_device *dssdev;
 127        struct list_head *list;
 128
 129        mutex_lock(&omapdss_devices_lock);
 130
 131        if (list_empty(&omapdss_devices_list)) {
 132                dssdev = NULL;
 133                goto done;
 134        }
 135
 136        /*
 137         * Start from the from entry if given or from omapdss_devices_list
 138         * otherwise.
 139         */
 140        list = from ? &from->list : &omapdss_devices_list;
 141
 142        list_for_each_entry(dssdev, list, list) {
 143                /*
 144                 * Stop if we reach the omapdss_devices_list, that's the end of
 145                 * the list.
 146                 */
 147                if (&dssdev->list == &omapdss_devices_list) {
 148                        dssdev = NULL;
 149                        goto done;
 150                }
 151
 152                if (dssdev->id &&
 153                    (dssdev->next || dssdev->bridge || dssdev->panel))
 154                        goto done;
 155        }
 156
 157        dssdev = NULL;
 158
 159done:
 160        if (from)
 161                omapdss_device_put(from);
 162        if (dssdev)
 163                omapdss_device_get(dssdev);
 164
 165        mutex_unlock(&omapdss_devices_lock);
 166        return dssdev;
 167}
 168EXPORT_SYMBOL(omapdss_device_next_output);
 169
 170static bool omapdss_device_is_connected(struct omap_dss_device *dssdev)
 171{
 172        return dssdev->dss;
 173}
 174
 175int omapdss_device_connect(struct dss_device *dss,
 176                           struct omap_dss_device *src,
 177                           struct omap_dss_device *dst)
 178{
 179        int ret;
 180
 181        dev_dbg(&dss->pdev->dev, "connect(%s, %s)\n",
 182                src ? dev_name(src->dev) : "NULL",
 183                dst ? dev_name(dst->dev) : "NULL");
 184
 185        if (!dst) {
 186                /*
 187                 * The destination is NULL when the source is connected to a
 188                 * bridge or panel instead of a DSS device. Stop here, we will
 189                 * attach the bridge or panel later when we will have a DRM
 190                 * encoder.
 191                 */
 192                return src && (src->bridge || src->panel) ? 0 : -EINVAL;
 193        }
 194
 195        if (omapdss_device_is_connected(dst))
 196                return -EBUSY;
 197
 198        dst->dss = dss;
 199
 200        ret = dst->ops->connect(src, dst);
 201        if (ret < 0) {
 202                dst->dss = NULL;
 203                return ret;
 204        }
 205
 206        return 0;
 207}
 208EXPORT_SYMBOL_GPL(omapdss_device_connect);
 209
 210void omapdss_device_disconnect(struct omap_dss_device *src,
 211                               struct omap_dss_device *dst)
 212{
 213        struct dss_device *dss = src ? src->dss : dst->dss;
 214
 215        dev_dbg(&dss->pdev->dev, "disconnect(%s, %s)\n",
 216                src ? dev_name(src->dev) : "NULL",
 217                dst ? dev_name(dst->dev) : "NULL");
 218
 219        if (!dst) {
 220                WARN_ON(!src->bridge && !src->panel);
 221                return;
 222        }
 223
 224        if (!dst->id && !omapdss_device_is_connected(dst)) {
 225                WARN_ON(!dst->display);
 226                return;
 227        }
 228
 229        WARN_ON(dst->state != OMAP_DSS_DISPLAY_DISABLED);
 230
 231        dst->ops->disconnect(src, dst);
 232        dst->dss = NULL;
 233}
 234EXPORT_SYMBOL_GPL(omapdss_device_disconnect);
 235
 236void omapdss_device_pre_enable(struct omap_dss_device *dssdev)
 237{
 238        if (!dssdev)
 239                return;
 240
 241        omapdss_device_pre_enable(dssdev->next);
 242
 243        if (dssdev->ops->pre_enable)
 244                dssdev->ops->pre_enable(dssdev);
 245}
 246EXPORT_SYMBOL_GPL(omapdss_device_pre_enable);
 247
 248void omapdss_device_enable(struct omap_dss_device *dssdev)
 249{
 250        if (!dssdev)
 251                return;
 252
 253        if (dssdev->ops->enable)
 254                dssdev->ops->enable(dssdev);
 255
 256        omapdss_device_enable(dssdev->next);
 257
 258        dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
 259}
 260EXPORT_SYMBOL_GPL(omapdss_device_enable);
 261
 262void omapdss_device_disable(struct omap_dss_device *dssdev)
 263{
 264        if (!dssdev)
 265                return;
 266
 267        omapdss_device_disable(dssdev->next);
 268
 269        if (dssdev->ops->disable)
 270                dssdev->ops->disable(dssdev);
 271}
 272EXPORT_SYMBOL_GPL(omapdss_device_disable);
 273
 274void omapdss_device_post_disable(struct omap_dss_device *dssdev)
 275{
 276        if (!dssdev)
 277                return;
 278
 279        if (dssdev->ops->post_disable)
 280                dssdev->ops->post_disable(dssdev);
 281
 282        omapdss_device_post_disable(dssdev->next);
 283
 284        dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
 285}
 286EXPORT_SYMBOL_GPL(omapdss_device_post_disable);
 287
 288/* -----------------------------------------------------------------------------
 289 * Components Handling
 290 */
 291
 292static struct list_head omapdss_comp_list;
 293
 294struct omapdss_comp_node {
 295        struct list_head list;
 296        struct device_node *node;
 297        bool dss_core_component;
 298        const char *compat;
 299};
 300
 301static bool omapdss_list_contains(const struct device_node *node)
 302{
 303        struct omapdss_comp_node *comp;
 304
 305        list_for_each_entry(comp, &omapdss_comp_list, list) {
 306                if (comp->node == node)
 307                        return true;
 308        }
 309
 310        return false;
 311}
 312
 313static void omapdss_walk_device(struct device *dev, struct device_node *node,
 314                                bool dss_core)
 315{
 316        struct omapdss_comp_node *comp;
 317        struct device_node *n;
 318        const char *compat;
 319        int ret;
 320
 321        ret = of_property_read_string(node, "compatible", &compat);
 322        if (ret < 0)
 323                return;
 324
 325        comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL);
 326        if (comp) {
 327                comp->node = node;
 328                comp->dss_core_component = dss_core;
 329                comp->compat = compat;
 330                list_add(&comp->list, &omapdss_comp_list);
 331        }
 332
 333        /*
 334         * of_graph_get_remote_port_parent() prints an error if there is no
 335         * port/ports node. To avoid that, check first that there's the node.
 336         */
 337        n = of_get_child_by_name(node, "ports");
 338        if (!n)
 339                n = of_get_child_by_name(node, "port");
 340        if (!n)
 341                return;
 342
 343        of_node_put(n);
 344
 345        n = NULL;
 346        while ((n = of_graph_get_next_endpoint(node, n)) != NULL) {
 347                struct device_node *pn = of_graph_get_remote_port_parent(n);
 348
 349                if (!pn)
 350                        continue;
 351
 352                if (!of_device_is_available(pn) || omapdss_list_contains(pn)) {
 353                        of_node_put(pn);
 354                        continue;
 355                }
 356
 357                omapdss_walk_device(dev, pn, false);
 358        }
 359}
 360
 361void omapdss_gather_components(struct device *dev)
 362{
 363        struct device_node *child;
 364
 365        INIT_LIST_HEAD(&omapdss_comp_list);
 366
 367        omapdss_walk_device(dev, dev->of_node, true);
 368
 369        for_each_available_child_of_node(dev->of_node, child)
 370                omapdss_walk_device(dev, child, true);
 371}
 372EXPORT_SYMBOL(omapdss_gather_components);
 373
 374static bool omapdss_component_is_loaded(struct omapdss_comp_node *comp)
 375{
 376        if (comp->dss_core_component)
 377                return true;
 378        if (!strstarts(comp->compat, "omapdss,"))
 379                return true;
 380        if (omapdss_device_is_registered(comp->node))
 381                return true;
 382
 383        return false;
 384}
 385
 386bool omapdss_stack_is_ready(void)
 387{
 388        struct omapdss_comp_node *comp;
 389
 390        list_for_each_entry(comp, &omapdss_comp_list, list) {
 391                if (!omapdss_component_is_loaded(comp))
 392                        return false;
 393        }
 394
 395        return true;
 396}
 397EXPORT_SYMBOL(omapdss_stack_is_ready);
 398
 399MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
 400MODULE_DESCRIPTION("OMAP Display Subsystem Base");
 401MODULE_LICENSE("GPL v2");
 402