18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
48c2ecf20Sopenharmony_ci * Author: Archit Taneja <archit@ti.com>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/kernel.h>
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
108c2ecf20Sopenharmony_ci#include <linux/slab.h>
118c2ecf20Sopenharmony_ci#include <linux/of.h>
128c2ecf20Sopenharmony_ci#include <linux/of_graph.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <drm/drm_bridge.h>
158c2ecf20Sopenharmony_ci#include <drm/drm_panel.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "dss.h"
188c2ecf20Sopenharmony_ci#include "omapdss.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ciint omapdss_device_init_output(struct omap_dss_device *out,
218c2ecf20Sopenharmony_ci			       struct drm_bridge *local_bridge)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	struct device_node *remote_node;
248c2ecf20Sopenharmony_ci	int ret;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	remote_node = of_graph_get_remote_node(out->dev->of_node,
278c2ecf20Sopenharmony_ci					       out->of_port, 0);
288c2ecf20Sopenharmony_ci	if (!remote_node) {
298c2ecf20Sopenharmony_ci		dev_dbg(out->dev, "failed to find video sink\n");
308c2ecf20Sopenharmony_ci		return 0;
318c2ecf20Sopenharmony_ci	}
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	out->next = omapdss_find_device_by_node(remote_node);
348c2ecf20Sopenharmony_ci	out->bridge = of_drm_find_bridge(remote_node);
358c2ecf20Sopenharmony_ci	out->panel = of_drm_find_panel(remote_node);
368c2ecf20Sopenharmony_ci	if (IS_ERR(out->panel))
378c2ecf20Sopenharmony_ci		out->panel = NULL;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	of_node_put(remote_node);
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	if (out->next && out->type != out->next->type) {
428c2ecf20Sopenharmony_ci		dev_err(out->dev, "output type and display type don't match\n");
438c2ecf20Sopenharmony_ci		ret = -EINVAL;
448c2ecf20Sopenharmony_ci		goto error;
458c2ecf20Sopenharmony_ci	}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	if (out->panel) {
488c2ecf20Sopenharmony_ci		struct drm_bridge *bridge;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci		bridge = drm_panel_bridge_add(out->panel);
518c2ecf20Sopenharmony_ci		if (IS_ERR(bridge)) {
528c2ecf20Sopenharmony_ci			dev_err(out->dev,
538c2ecf20Sopenharmony_ci				"unable to create panel bridge (%ld)\n",
548c2ecf20Sopenharmony_ci				PTR_ERR(bridge));
558c2ecf20Sopenharmony_ci			ret = PTR_ERR(bridge);
568c2ecf20Sopenharmony_ci			goto error;
578c2ecf20Sopenharmony_ci		}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci		out->bridge = bridge;
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	if (local_bridge) {
638c2ecf20Sopenharmony_ci		if (!out->bridge) {
648c2ecf20Sopenharmony_ci			ret = -EPROBE_DEFER;
658c2ecf20Sopenharmony_ci			goto error;
668c2ecf20Sopenharmony_ci		}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci		out->next_bridge = out->bridge;
698c2ecf20Sopenharmony_ci		out->bridge = local_bridge;
708c2ecf20Sopenharmony_ci	}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	if (!out->next && !out->bridge) {
738c2ecf20Sopenharmony_ci		ret = -EPROBE_DEFER;
748c2ecf20Sopenharmony_ci		goto error;
758c2ecf20Sopenharmony_ci	}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	return 0;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cierror:
808c2ecf20Sopenharmony_ci	omapdss_device_cleanup_output(out);
818c2ecf20Sopenharmony_ci	out->next = NULL;
828c2ecf20Sopenharmony_ci	return ret;
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ciEXPORT_SYMBOL(omapdss_device_init_output);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_civoid omapdss_device_cleanup_output(struct omap_dss_device *out)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	if (out->bridge && out->panel)
898c2ecf20Sopenharmony_ci		drm_panel_bridge_remove(out->next_bridge ?
908c2ecf20Sopenharmony_ci					out->next_bridge : out->bridge);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	if (out->next)
938c2ecf20Sopenharmony_ci		omapdss_device_put(out->next);
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ciEXPORT_SYMBOL(omapdss_device_cleanup_output);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ciint dss_install_mgr_ops(struct dss_device *dss,
988c2ecf20Sopenharmony_ci			const struct dss_mgr_ops *mgr_ops,
998c2ecf20Sopenharmony_ci			struct omap_drm_private *priv)
1008c2ecf20Sopenharmony_ci{
1018c2ecf20Sopenharmony_ci	if (dss->mgr_ops)
1028c2ecf20Sopenharmony_ci		return -EBUSY;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	dss->mgr_ops = mgr_ops;
1058c2ecf20Sopenharmony_ci	dss->mgr_ops_priv = priv;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	return 0;
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ciEXPORT_SYMBOL(dss_install_mgr_ops);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_civoid dss_uninstall_mgr_ops(struct dss_device *dss)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	dss->mgr_ops = NULL;
1148c2ecf20Sopenharmony_ci	dss->mgr_ops_priv = NULL;
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ciEXPORT_SYMBOL(dss_uninstall_mgr_ops);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_civoid dss_mgr_set_timings(struct omap_dss_device *dssdev,
1198c2ecf20Sopenharmony_ci			 const struct videomode *vm)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	dssdev->dss->mgr_ops->set_timings(dssdev->dss->mgr_ops_priv,
1228c2ecf20Sopenharmony_ci					  dssdev->dispc_channel, vm);
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ciEXPORT_SYMBOL(dss_mgr_set_timings);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_civoid dss_mgr_set_lcd_config(struct omap_dss_device *dssdev,
1278c2ecf20Sopenharmony_ci		const struct dss_lcd_mgr_config *config)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	dssdev->dss->mgr_ops->set_lcd_config(dssdev->dss->mgr_ops_priv,
1308c2ecf20Sopenharmony_ci					     dssdev->dispc_channel, config);
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ciEXPORT_SYMBOL(dss_mgr_set_lcd_config);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ciint dss_mgr_enable(struct omap_dss_device *dssdev)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	return dssdev->dss->mgr_ops->enable(dssdev->dss->mgr_ops_priv,
1378c2ecf20Sopenharmony_ci					    dssdev->dispc_channel);
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ciEXPORT_SYMBOL(dss_mgr_enable);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_civoid dss_mgr_disable(struct omap_dss_device *dssdev)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	dssdev->dss->mgr_ops->disable(dssdev->dss->mgr_ops_priv,
1448c2ecf20Sopenharmony_ci				      dssdev->dispc_channel);
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ciEXPORT_SYMBOL(dss_mgr_disable);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_civoid dss_mgr_start_update(struct omap_dss_device *dssdev)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	dssdev->dss->mgr_ops->start_update(dssdev->dss->mgr_ops_priv,
1518c2ecf20Sopenharmony_ci					   dssdev->dispc_channel);
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ciEXPORT_SYMBOL(dss_mgr_start_update);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ciint dss_mgr_register_framedone_handler(struct omap_dss_device *dssdev,
1568c2ecf20Sopenharmony_ci		void (*handler)(void *), void *data)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	struct dss_device *dss = dssdev->dss;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	return dss->mgr_ops->register_framedone_handler(dss->mgr_ops_priv,
1618c2ecf20Sopenharmony_ci							dssdev->dispc_channel,
1628c2ecf20Sopenharmony_ci							handler, data);
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ciEXPORT_SYMBOL(dss_mgr_register_framedone_handler);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_civoid dss_mgr_unregister_framedone_handler(struct omap_dss_device *dssdev,
1678c2ecf20Sopenharmony_ci		void (*handler)(void *), void *data)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	struct dss_device *dss = dssdev->dss;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	dss->mgr_ops->unregister_framedone_handler(dss->mgr_ops_priv,
1728c2ecf20Sopenharmony_ci						   dssdev->dispc_channel,
1738c2ecf20Sopenharmony_ci						   handler, data);
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ciEXPORT_SYMBOL(dss_mgr_unregister_framedone_handler);
176