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 - https://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
  19struct dispc_device *dispc_get_dispc(struct dss_device *dss)
  20{
  21        return dss->dispc;
  22}
  23
  24/* -----------------------------------------------------------------------------
  25 * OMAP DSS Devices Handling
  26 */
  27
  28static LIST_HEAD(omapdss_devices_list);
  29static DEFINE_MUTEX(omapdss_devices_lock);
  30
  31void omapdss_device_register(struct omap_dss_device *dssdev)
  32{
  33        mutex_lock(&omapdss_devices_lock);
  34        list_add_tail(&dssdev->list, &omapdss_devices_list);
  35        mutex_unlock(&omapdss_devices_lock);
  36}
  37
  38void omapdss_device_unregister(struct omap_dss_device *dssdev)
  39{
  40        mutex_lock(&omapdss_devices_lock);
  41        list_del(&dssdev->list);
  42        mutex_unlock(&omapdss_devices_lock);
  43}
  44
  45static bool omapdss_device_is_registered(struct device_node *node)
  46{
  47        struct omap_dss_device *dssdev;
  48        bool found = false;
  49
  50        mutex_lock(&omapdss_devices_lock);
  51
  52        list_for_each_entry(dssdev, &omapdss_devices_list, list) {
  53                if (dssdev->dev->of_node == node) {
  54                        found = true;
  55                        break;
  56                }
  57        }
  58
  59        mutex_unlock(&omapdss_devices_lock);
  60        return found;
  61}
  62
  63struct omap_dss_device *omapdss_device_get(struct omap_dss_device *dssdev)
  64{
  65        if (get_device(dssdev->dev) == NULL)
  66                return NULL;
  67
  68        return dssdev;
  69}
  70
  71void omapdss_device_put(struct omap_dss_device *dssdev)
  72{
  73        put_device(dssdev->dev);
  74}
  75
  76struct omap_dss_device *omapdss_find_device_by_node(struct device_node *node)
  77{
  78        struct omap_dss_device *dssdev;
  79
  80        list_for_each_entry(dssdev, &omapdss_devices_list, list) {
  81                if (dssdev->dev->of_node == node)
  82                        return omapdss_device_get(dssdev);
  83        }
  84
  85        return NULL;
  86}
  87
  88/*
  89 * Search for the next output device starting at @from. Release the reference to
  90 * the @from device, and acquire a reference to the returned device if found.
  91 */
  92struct omap_dss_device *omapdss_device_next_output(struct omap_dss_device *from)
  93{
  94        struct omap_dss_device *dssdev;
  95        struct list_head *list;
  96
  97        mutex_lock(&omapdss_devices_lock);
  98
  99        if (list_empty(&omapdss_devices_list)) {
 100                dssdev = NULL;
 101                goto done;
 102        }
 103
 104        /*
 105         * Start from the from entry if given or from omapdss_devices_list
 106         * otherwise.
 107         */
 108        list = from ? &from->list : &omapdss_devices_list;
 109
 110        list_for_each_entry(dssdev, list, list) {
 111                /*
 112                 * Stop if we reach the omapdss_devices_list, that's the end of
 113                 * the list.
 114                 */
 115                if (&dssdev->list == &omapdss_devices_list) {
 116                        dssdev = NULL;
 117                        goto done;
 118                }
 119
 120                if (dssdev->id && dssdev->bridge)
 121                        goto done;
 122        }
 123
 124        dssdev = NULL;
 125
 126done:
 127        if (from)
 128                omapdss_device_put(from);
 129        if (dssdev)
 130                omapdss_device_get(dssdev);
 131
 132        mutex_unlock(&omapdss_devices_lock);
 133        return dssdev;
 134}
 135
 136static bool omapdss_device_is_connected(struct omap_dss_device *dssdev)
 137{
 138        return dssdev->dss;
 139}
 140
 141int omapdss_device_connect(struct dss_device *dss,
 142                           struct omap_dss_device *src,
 143                           struct omap_dss_device *dst)
 144{
 145        dev_dbg(&dss->pdev->dev, "connect(%s, %s)\n",
 146                src ? dev_name(src->dev) : "NULL",
 147                dst ? dev_name(dst->dev) : "NULL");
 148
 149        if (!dst) {
 150                /*
 151                 * The destination is NULL when the source is connected to a
 152                 * bridge instead of a DSS device. Stop here, we will attach
 153                 * the bridge later when we will have a DRM encoder.
 154                 */
 155                return src && src->bridge ? 0 : -EINVAL;
 156        }
 157
 158        if (omapdss_device_is_connected(dst))
 159                return -EBUSY;
 160
 161        dst->dss = dss;
 162
 163        return 0;
 164}
 165
 166void omapdss_device_disconnect(struct omap_dss_device *src,
 167                               struct omap_dss_device *dst)
 168{
 169        struct dss_device *dss = src ? src->dss : dst->dss;
 170
 171        dev_dbg(&dss->pdev->dev, "disconnect(%s, %s)\n",
 172                src ? dev_name(src->dev) : "NULL",
 173                dst ? dev_name(dst->dev) : "NULL");
 174
 175        if (!dst) {
 176                WARN_ON(!src->bridge);
 177                return;
 178        }
 179
 180        if (!dst->id && !omapdss_device_is_connected(dst)) {
 181                WARN_ON(1);
 182                return;
 183        }
 184
 185        dst->dss = NULL;
 186}
 187
 188/* -----------------------------------------------------------------------------
 189 * Components Handling
 190 */
 191
 192static struct list_head omapdss_comp_list;
 193
 194struct omapdss_comp_node {
 195        struct list_head list;
 196        struct device_node *node;
 197        bool dss_core_component;
 198        const char *compat;
 199};
 200
 201static bool omapdss_list_contains(const struct device_node *node)
 202{
 203        struct omapdss_comp_node *comp;
 204
 205        list_for_each_entry(comp, &omapdss_comp_list, list) {
 206                if (comp->node == node)
 207                        return true;
 208        }
 209
 210        return false;
 211}
 212
 213static void omapdss_walk_device(struct device *dev, struct device_node *node,
 214                                bool dss_core)
 215{
 216        struct omapdss_comp_node *comp;
 217        struct device_node *n;
 218        const char *compat;
 219        int ret;
 220
 221        ret = of_property_read_string(node, "compatible", &compat);
 222        if (ret < 0)
 223                return;
 224
 225        comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL);
 226        if (comp) {
 227                comp->node = node;
 228                comp->dss_core_component = dss_core;
 229                comp->compat = compat;
 230                list_add(&comp->list, &omapdss_comp_list);
 231        }
 232
 233        /*
 234         * of_graph_get_remote_port_parent() prints an error if there is no
 235         * port/ports node. To avoid that, check first that there's the node.
 236         */
 237        n = of_get_child_by_name(node, "ports");
 238        if (!n)
 239                n = of_get_child_by_name(node, "port");
 240        if (!n)
 241                return;
 242
 243        of_node_put(n);
 244
 245        n = NULL;
 246        while ((n = of_graph_get_next_endpoint(node, n)) != NULL) {
 247                struct device_node *pn = of_graph_get_remote_port_parent(n);
 248
 249                if (!pn)
 250                        continue;
 251
 252                if (!of_device_is_available(pn) || omapdss_list_contains(pn)) {
 253                        of_node_put(pn);
 254                        continue;
 255                }
 256
 257                omapdss_walk_device(dev, pn, false);
 258        }
 259}
 260
 261void omapdss_gather_components(struct device *dev)
 262{
 263        struct device_node *child;
 264
 265        INIT_LIST_HEAD(&omapdss_comp_list);
 266
 267        omapdss_walk_device(dev, dev->of_node, true);
 268
 269        for_each_available_child_of_node(dev->of_node, child)
 270                omapdss_walk_device(dev, child, true);
 271}
 272
 273static bool omapdss_component_is_loaded(struct omapdss_comp_node *comp)
 274{
 275        if (comp->dss_core_component)
 276                return true;
 277        if (!strstarts(comp->compat, "omapdss,"))
 278                return true;
 279        if (omapdss_device_is_registered(comp->node))
 280                return true;
 281
 282        return false;
 283}
 284
 285bool omapdss_stack_is_ready(void)
 286{
 287        struct omapdss_comp_node *comp;
 288
 289        list_for_each_entry(comp, &omapdss_comp_list, list) {
 290                if (!omapdss_component_is_loaded(comp))
 291                        return false;
 292        }
 293
 294        return true;
 295}
 296