162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * OMAP Display Subsystem Base
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2015-2017 Texas Instruments Incorporated - https://www.ti.com/
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/list.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/mutex.h>
1262306a36Sopenharmony_ci#include <linux/of.h>
1362306a36Sopenharmony_ci#include <linux/of_graph.h>
1462306a36Sopenharmony_ci#include <linux/platform_device.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "dss.h"
1762306a36Sopenharmony_ci#include "omapdss.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistruct dispc_device *dispc_get_dispc(struct dss_device *dss)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	return dss->dispc;
2262306a36Sopenharmony_ci}
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/* -----------------------------------------------------------------------------
2562306a36Sopenharmony_ci * OMAP DSS Devices Handling
2662306a36Sopenharmony_ci */
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic LIST_HEAD(omapdss_devices_list);
2962306a36Sopenharmony_cistatic DEFINE_MUTEX(omapdss_devices_lock);
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_civoid omapdss_device_register(struct omap_dss_device *dssdev)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	mutex_lock(&omapdss_devices_lock);
3462306a36Sopenharmony_ci	list_add_tail(&dssdev->list, &omapdss_devices_list);
3562306a36Sopenharmony_ci	mutex_unlock(&omapdss_devices_lock);
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_civoid omapdss_device_unregister(struct omap_dss_device *dssdev)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	mutex_lock(&omapdss_devices_lock);
4162306a36Sopenharmony_ci	list_del(&dssdev->list);
4262306a36Sopenharmony_ci	mutex_unlock(&omapdss_devices_lock);
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic bool omapdss_device_is_registered(struct device_node *node)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	struct omap_dss_device *dssdev;
4862306a36Sopenharmony_ci	bool found = false;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	mutex_lock(&omapdss_devices_lock);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	list_for_each_entry(dssdev, &omapdss_devices_list, list) {
5362306a36Sopenharmony_ci		if (dssdev->dev->of_node == node) {
5462306a36Sopenharmony_ci			found = true;
5562306a36Sopenharmony_ci			break;
5662306a36Sopenharmony_ci		}
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	mutex_unlock(&omapdss_devices_lock);
6062306a36Sopenharmony_ci	return found;
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistruct omap_dss_device *omapdss_device_get(struct omap_dss_device *dssdev)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	if (get_device(dssdev->dev) == NULL)
6662306a36Sopenharmony_ci		return NULL;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	return dssdev;
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_civoid omapdss_device_put(struct omap_dss_device *dssdev)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	put_device(dssdev->dev);
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistruct omap_dss_device *omapdss_find_device_by_node(struct device_node *node)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	struct omap_dss_device *dssdev;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	list_for_each_entry(dssdev, &omapdss_devices_list, list) {
8162306a36Sopenharmony_ci		if (dssdev->dev->of_node == node)
8262306a36Sopenharmony_ci			return omapdss_device_get(dssdev);
8362306a36Sopenharmony_ci	}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	return NULL;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci/*
8962306a36Sopenharmony_ci * Search for the next output device starting at @from. Release the reference to
9062306a36Sopenharmony_ci * the @from device, and acquire a reference to the returned device if found.
9162306a36Sopenharmony_ci */
9262306a36Sopenharmony_cistruct omap_dss_device *omapdss_device_next_output(struct omap_dss_device *from)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	struct omap_dss_device *dssdev;
9562306a36Sopenharmony_ci	struct list_head *list;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	mutex_lock(&omapdss_devices_lock);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	if (list_empty(&omapdss_devices_list)) {
10062306a36Sopenharmony_ci		dssdev = NULL;
10162306a36Sopenharmony_ci		goto done;
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	/*
10562306a36Sopenharmony_ci	 * Start from the from entry if given or from omapdss_devices_list
10662306a36Sopenharmony_ci	 * otherwise.
10762306a36Sopenharmony_ci	 */
10862306a36Sopenharmony_ci	list = from ? &from->list : &omapdss_devices_list;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	list_for_each_entry(dssdev, list, list) {
11162306a36Sopenharmony_ci		/*
11262306a36Sopenharmony_ci		 * Stop if we reach the omapdss_devices_list, that's the end of
11362306a36Sopenharmony_ci		 * the list.
11462306a36Sopenharmony_ci		 */
11562306a36Sopenharmony_ci		if (&dssdev->list == &omapdss_devices_list) {
11662306a36Sopenharmony_ci			dssdev = NULL;
11762306a36Sopenharmony_ci			goto done;
11862306a36Sopenharmony_ci		}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci		if (dssdev->id && dssdev->bridge)
12162306a36Sopenharmony_ci			goto done;
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	dssdev = NULL;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cidone:
12762306a36Sopenharmony_ci	if (from)
12862306a36Sopenharmony_ci		omapdss_device_put(from);
12962306a36Sopenharmony_ci	if (dssdev)
13062306a36Sopenharmony_ci		omapdss_device_get(dssdev);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	mutex_unlock(&omapdss_devices_lock);
13362306a36Sopenharmony_ci	return dssdev;
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic bool omapdss_device_is_connected(struct omap_dss_device *dssdev)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	return dssdev->dss;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ciint omapdss_device_connect(struct dss_device *dss,
14262306a36Sopenharmony_ci			   struct omap_dss_device *src,
14362306a36Sopenharmony_ci			   struct omap_dss_device *dst)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	dev_dbg(&dss->pdev->dev, "connect(%s, %s)\n",
14662306a36Sopenharmony_ci		src ? dev_name(src->dev) : "NULL",
14762306a36Sopenharmony_ci		dst ? dev_name(dst->dev) : "NULL");
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	if (!dst) {
15062306a36Sopenharmony_ci		/*
15162306a36Sopenharmony_ci		 * The destination is NULL when the source is connected to a
15262306a36Sopenharmony_ci		 * bridge instead of a DSS device. Stop here, we will attach
15362306a36Sopenharmony_ci		 * the bridge later when we will have a DRM encoder.
15462306a36Sopenharmony_ci		 */
15562306a36Sopenharmony_ci		return src && src->bridge ? 0 : -EINVAL;
15662306a36Sopenharmony_ci	}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	if (omapdss_device_is_connected(dst))
15962306a36Sopenharmony_ci		return -EBUSY;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	dst->dss = dss;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	return 0;
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_civoid omapdss_device_disconnect(struct omap_dss_device *src,
16762306a36Sopenharmony_ci			       struct omap_dss_device *dst)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	struct dss_device *dss = src ? src->dss : dst->dss;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	dev_dbg(&dss->pdev->dev, "disconnect(%s, %s)\n",
17262306a36Sopenharmony_ci		src ? dev_name(src->dev) : "NULL",
17362306a36Sopenharmony_ci		dst ? dev_name(dst->dev) : "NULL");
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	if (!dst) {
17662306a36Sopenharmony_ci		WARN_ON(!src->bridge);
17762306a36Sopenharmony_ci		return;
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	if (!dst->id && !omapdss_device_is_connected(dst)) {
18162306a36Sopenharmony_ci		WARN_ON(1);
18262306a36Sopenharmony_ci		return;
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	dst->dss = NULL;
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci/* -----------------------------------------------------------------------------
18962306a36Sopenharmony_ci * Components Handling
19062306a36Sopenharmony_ci */
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic struct list_head omapdss_comp_list;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistruct omapdss_comp_node {
19562306a36Sopenharmony_ci	struct list_head list;
19662306a36Sopenharmony_ci	struct device_node *node;
19762306a36Sopenharmony_ci	bool dss_core_component;
19862306a36Sopenharmony_ci	const char *compat;
19962306a36Sopenharmony_ci};
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic bool omapdss_list_contains(const struct device_node *node)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	struct omapdss_comp_node *comp;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	list_for_each_entry(comp, &omapdss_comp_list, list) {
20662306a36Sopenharmony_ci		if (comp->node == node)
20762306a36Sopenharmony_ci			return true;
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	return false;
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic void omapdss_walk_device(struct device *dev, struct device_node *node,
21462306a36Sopenharmony_ci				bool dss_core)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	struct omapdss_comp_node *comp;
21762306a36Sopenharmony_ci	struct device_node *n;
21862306a36Sopenharmony_ci	const char *compat;
21962306a36Sopenharmony_ci	int ret;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	ret = of_property_read_string(node, "compatible", &compat);
22262306a36Sopenharmony_ci	if (ret < 0)
22362306a36Sopenharmony_ci		return;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL);
22662306a36Sopenharmony_ci	if (comp) {
22762306a36Sopenharmony_ci		comp->node = node;
22862306a36Sopenharmony_ci		comp->dss_core_component = dss_core;
22962306a36Sopenharmony_ci		comp->compat = compat;
23062306a36Sopenharmony_ci		list_add(&comp->list, &omapdss_comp_list);
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	/*
23462306a36Sopenharmony_ci	 * of_graph_get_remote_port_parent() prints an error if there is no
23562306a36Sopenharmony_ci	 * port/ports node. To avoid that, check first that there's the node.
23662306a36Sopenharmony_ci	 */
23762306a36Sopenharmony_ci	n = of_get_child_by_name(node, "ports");
23862306a36Sopenharmony_ci	if (!n)
23962306a36Sopenharmony_ci		n = of_get_child_by_name(node, "port");
24062306a36Sopenharmony_ci	if (!n)
24162306a36Sopenharmony_ci		return;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	of_node_put(n);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	n = NULL;
24662306a36Sopenharmony_ci	while ((n = of_graph_get_next_endpoint(node, n)) != NULL) {
24762306a36Sopenharmony_ci		struct device_node *pn = of_graph_get_remote_port_parent(n);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci		if (!pn)
25062306a36Sopenharmony_ci			continue;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci		if (!of_device_is_available(pn) || omapdss_list_contains(pn)) {
25362306a36Sopenharmony_ci			of_node_put(pn);
25462306a36Sopenharmony_ci			continue;
25562306a36Sopenharmony_ci		}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci		omapdss_walk_device(dev, pn, false);
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_civoid omapdss_gather_components(struct device *dev)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	struct device_node *child;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	INIT_LIST_HEAD(&omapdss_comp_list);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	omapdss_walk_device(dev, dev->of_node, true);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	for_each_available_child_of_node(dev->of_node, child)
27062306a36Sopenharmony_ci		omapdss_walk_device(dev, child, true);
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic bool omapdss_component_is_loaded(struct omapdss_comp_node *comp)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	if (comp->dss_core_component)
27662306a36Sopenharmony_ci		return true;
27762306a36Sopenharmony_ci	if (!strstarts(comp->compat, "omapdss,"))
27862306a36Sopenharmony_ci		return true;
27962306a36Sopenharmony_ci	if (omapdss_device_is_registered(comp->node))
28062306a36Sopenharmony_ci		return true;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	return false;
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cibool omapdss_stack_is_ready(void)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	struct omapdss_comp_node *comp;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	list_for_each_entry(comp, &omapdss_comp_list, list) {
29062306a36Sopenharmony_ci		if (!omapdss_component_is_loaded(comp))
29162306a36Sopenharmony_ci			return false;
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	return true;
29562306a36Sopenharmony_ci}
296