xref: /openbmc/linux/drivers/gpu/drm/bridge/panel.c (revision b5c8ffc8)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
213dfc054SEric Anholt /*
313dfc054SEric Anholt  * Copyright (C) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com>
413dfc054SEric Anholt  * Copyright (C) 2017 Broadcom
513dfc054SEric Anholt  */
613dfc054SEric Anholt 
713dfc054SEric Anholt #include <drm/drm_atomic_helper.h>
8ee68c743SBoris Brezillon #include <drm/drm_bridge.h>
913dfc054SEric Anholt #include <drm/drm_connector.h>
1013dfc054SEric Anholt #include <drm/drm_encoder.h>
11abea75e9SMaxime Ripard #include <drm/drm_managed.h>
1213dfc054SEric Anholt #include <drm/drm_modeset_helper_vtables.h>
13d4ae66f1SMaxime Ripard #include <drm/drm_of.h>
1413dfc054SEric Anholt #include <drm/drm_panel.h>
1595b60804SSam Ravnborg #include <drm/drm_print.h>
1678666baaSSabyasachi Gupta #include <drm/drm_probe_helper.h>
1713dfc054SEric Anholt 
1813dfc054SEric Anholt struct panel_bridge {
1913dfc054SEric Anholt 	struct drm_bridge bridge;
2013dfc054SEric Anholt 	struct drm_connector connector;
2113dfc054SEric Anholt 	struct drm_panel *panel;
2213dfc054SEric Anholt 	u32 connector_type;
2313dfc054SEric Anholt };
2413dfc054SEric Anholt 
2513dfc054SEric Anholt static inline struct panel_bridge *
drm_bridge_to_panel_bridge(struct drm_bridge * bridge)2613dfc054SEric Anholt drm_bridge_to_panel_bridge(struct drm_bridge *bridge)
2713dfc054SEric Anholt {
2813dfc054SEric Anholt 	return container_of(bridge, struct panel_bridge, bridge);
2913dfc054SEric Anholt }
3013dfc054SEric Anholt 
3113dfc054SEric Anholt static inline struct panel_bridge *
drm_connector_to_panel_bridge(struct drm_connector * connector)3213dfc054SEric Anholt drm_connector_to_panel_bridge(struct drm_connector *connector)
3313dfc054SEric Anholt {
3413dfc054SEric Anholt 	return container_of(connector, struct panel_bridge, connector);
3513dfc054SEric Anholt }
3613dfc054SEric Anholt 
panel_bridge_connector_get_modes(struct drm_connector * connector)3713dfc054SEric Anholt static int panel_bridge_connector_get_modes(struct drm_connector *connector)
3813dfc054SEric Anholt {
3913dfc054SEric Anholt 	struct panel_bridge *panel_bridge =
4013dfc054SEric Anholt 		drm_connector_to_panel_bridge(connector);
4113dfc054SEric Anholt 
4206c4a9c2SSam Ravnborg 	return drm_panel_get_modes(panel_bridge->panel, connector);
4313dfc054SEric Anholt }
4413dfc054SEric Anholt 
4513dfc054SEric Anholt static const struct drm_connector_helper_funcs
4613dfc054SEric Anholt panel_bridge_connector_helper_funcs = {
4713dfc054SEric Anholt 	.get_modes = panel_bridge_connector_get_modes,
4813dfc054SEric Anholt };
4913dfc054SEric Anholt 
5013dfc054SEric Anholt static const struct drm_connector_funcs panel_bridge_connector_funcs = {
5113dfc054SEric Anholt 	.reset = drm_atomic_helper_connector_reset,
5213dfc054SEric Anholt 	.fill_modes = drm_helper_probe_single_connector_modes,
5313dfc054SEric Anholt 	.destroy = drm_connector_cleanup,
5413dfc054SEric Anholt 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
5513dfc054SEric Anholt 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
5613dfc054SEric Anholt };
5713dfc054SEric Anholt 
panel_bridge_attach(struct drm_bridge * bridge,enum drm_bridge_attach_flags flags)58a25b988fSLaurent Pinchart static int panel_bridge_attach(struct drm_bridge *bridge,
59a25b988fSLaurent Pinchart 			       enum drm_bridge_attach_flags flags)
6013dfc054SEric Anholt {
6113dfc054SEric Anholt 	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
6213dfc054SEric Anholt 	struct drm_connector *connector = &panel_bridge->connector;
6313dfc054SEric Anholt 	int ret;
6413dfc054SEric Anholt 
652be68b59SLaurent Pinchart 	if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
662be68b59SLaurent Pinchart 		return 0;
67a25b988fSLaurent Pinchart 
6813dfc054SEric Anholt 	if (!bridge->encoder) {
6913dfc054SEric Anholt 		DRM_ERROR("Missing encoder\n");
7013dfc054SEric Anholt 		return -ENODEV;
7113dfc054SEric Anholt 	}
7213dfc054SEric Anholt 
7313dfc054SEric Anholt 	drm_connector_helper_add(connector,
7413dfc054SEric Anholt 				 &panel_bridge_connector_helper_funcs);
7513dfc054SEric Anholt 
7613dfc054SEric Anholt 	ret = drm_connector_init(bridge->dev, connector,
7713dfc054SEric Anholt 				 &panel_bridge_connector_funcs,
7813dfc054SEric Anholt 				 panel_bridge->connector_type);
7913dfc054SEric Anholt 	if (ret) {
8013dfc054SEric Anholt 		DRM_ERROR("Failed to initialize connector\n");
8113dfc054SEric Anholt 		return ret;
8213dfc054SEric Anholt 	}
8313dfc054SEric Anholt 
84e3ea1806SJohn Keeping 	drm_panel_bridge_set_orientation(connector, bridge);
85e3ea1806SJohn Keeping 
86cde4c44dSDaniel Vetter 	drm_connector_attach_encoder(&panel_bridge->connector,
8713dfc054SEric Anholt 					  bridge->encoder);
8813dfc054SEric Anholt 
8934263c1bSMarek Szyprowski 	if (bridge->dev->registered) {
90934aef88SJagan Teki 		if (connector->funcs->reset)
91934aef88SJagan Teki 			connector->funcs->reset(connector);
9234263c1bSMarek Szyprowski 		drm_connector_register(connector);
9334263c1bSMarek Szyprowski 	}
94934aef88SJagan Teki 
9513dfc054SEric Anholt 	return 0;
9613dfc054SEric Anholt }
9713dfc054SEric Anholt 
panel_bridge_detach(struct drm_bridge * bridge)9813dfc054SEric Anholt static void panel_bridge_detach(struct drm_bridge *bridge)
9913dfc054SEric Anholt {
1004d906839SPaul Cercueil 	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
1014d906839SPaul Cercueil 	struct drm_connector *connector = &panel_bridge->connector;
1024d906839SPaul Cercueil 
1034d906839SPaul Cercueil 	/*
1044d906839SPaul Cercueil 	 * Cleanup the connector if we know it was initialized.
1054d906839SPaul Cercueil 	 *
1064d906839SPaul Cercueil 	 * FIXME: This wouldn't be needed if the panel_bridge structure was
1074d906839SPaul Cercueil 	 * allocated with drmm_kzalloc(). This might be tricky since the
1084d906839SPaul Cercueil 	 * drm_device pointer can only be retrieved when the bridge is attached.
1094d906839SPaul Cercueil 	 */
1104d906839SPaul Cercueil 	if (connector->dev)
1114d906839SPaul Cercueil 		drm_connector_cleanup(connector);
11213dfc054SEric Anholt }
11313dfc054SEric Anholt 
panel_bridge_atomic_pre_enable(struct drm_bridge * bridge,struct drm_bridge_state * old_bridge_state)114b67e0f53SVinod Polimera static void panel_bridge_atomic_pre_enable(struct drm_bridge *bridge,
115b67e0f53SVinod Polimera 				struct drm_bridge_state *old_bridge_state)
11613dfc054SEric Anholt {
11713dfc054SEric Anholt 	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
118d011db30SVinod Polimera 	struct drm_atomic_state *atomic_state = old_bridge_state->base.state;
119d011db30SVinod Polimera 	struct drm_encoder *encoder = bridge->encoder;
120d011db30SVinod Polimera 	struct drm_crtc *crtc;
121d011db30SVinod Polimera 	struct drm_crtc_state *old_crtc_state;
122d011db30SVinod Polimera 
123d011db30SVinod Polimera 	crtc = drm_atomic_get_new_crtc_for_encoder(atomic_state, encoder);
124d011db30SVinod Polimera 	if (!crtc)
125d011db30SVinod Polimera 		return;
126d011db30SVinod Polimera 
127d011db30SVinod Polimera 	old_crtc_state = drm_atomic_get_old_crtc_state(atomic_state, crtc);
128d011db30SVinod Polimera 	if (old_crtc_state && old_crtc_state->self_refresh_active)
129d011db30SVinod Polimera 		return;
13013dfc054SEric Anholt 
13113dfc054SEric Anholt 	drm_panel_prepare(panel_bridge->panel);
13213dfc054SEric Anholt }
13313dfc054SEric Anholt 
panel_bridge_atomic_enable(struct drm_bridge * bridge,struct drm_bridge_state * old_bridge_state)134b67e0f53SVinod Polimera static void panel_bridge_atomic_enable(struct drm_bridge *bridge,
135b67e0f53SVinod Polimera 				struct drm_bridge_state *old_bridge_state)
13613dfc054SEric Anholt {
13713dfc054SEric Anholt 	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
138d011db30SVinod Polimera 	struct drm_atomic_state *atomic_state = old_bridge_state->base.state;
139d011db30SVinod Polimera 	struct drm_encoder *encoder = bridge->encoder;
140d011db30SVinod Polimera 	struct drm_crtc *crtc;
141d011db30SVinod Polimera 	struct drm_crtc_state *old_crtc_state;
142d011db30SVinod Polimera 
143d011db30SVinod Polimera 	crtc = drm_atomic_get_new_crtc_for_encoder(atomic_state, encoder);
144d011db30SVinod Polimera 	if (!crtc)
145d011db30SVinod Polimera 		return;
146d011db30SVinod Polimera 
147d011db30SVinod Polimera 	old_crtc_state = drm_atomic_get_old_crtc_state(atomic_state, crtc);
148d011db30SVinod Polimera 	if (old_crtc_state && old_crtc_state->self_refresh_active)
149d011db30SVinod Polimera 		return;
15013dfc054SEric Anholt 
15113dfc054SEric Anholt 	drm_panel_enable(panel_bridge->panel);
15213dfc054SEric Anholt }
15313dfc054SEric Anholt 
panel_bridge_atomic_disable(struct drm_bridge * bridge,struct drm_bridge_state * old_bridge_state)154b67e0f53SVinod Polimera static void panel_bridge_atomic_disable(struct drm_bridge *bridge,
155b67e0f53SVinod Polimera 				struct drm_bridge_state *old_bridge_state)
15613dfc054SEric Anholt {
15713dfc054SEric Anholt 	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
158d011db30SVinod Polimera 	struct drm_atomic_state *atomic_state = old_bridge_state->base.state;
159d011db30SVinod Polimera 	struct drm_encoder *encoder = bridge->encoder;
160d011db30SVinod Polimera 	struct drm_crtc *crtc;
161d011db30SVinod Polimera 	struct drm_crtc_state *new_crtc_state;
162d011db30SVinod Polimera 
163d011db30SVinod Polimera 	crtc = drm_atomic_get_old_crtc_for_encoder(atomic_state, encoder);
164d011db30SVinod Polimera 	if (!crtc)
165d011db30SVinod Polimera 		return;
166d011db30SVinod Polimera 
167d011db30SVinod Polimera 	new_crtc_state = drm_atomic_get_new_crtc_state(atomic_state, crtc);
168d011db30SVinod Polimera 	if (new_crtc_state && new_crtc_state->self_refresh_active)
169d011db30SVinod Polimera 		return;
17013dfc054SEric Anholt 
17113dfc054SEric Anholt 	drm_panel_disable(panel_bridge->panel);
17213dfc054SEric Anholt }
17313dfc054SEric Anholt 
panel_bridge_atomic_post_disable(struct drm_bridge * bridge,struct drm_bridge_state * old_bridge_state)174b67e0f53SVinod Polimera static void panel_bridge_atomic_post_disable(struct drm_bridge *bridge,
175b67e0f53SVinod Polimera 				struct drm_bridge_state *old_bridge_state)
17613dfc054SEric Anholt {
17713dfc054SEric Anholt 	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
178d011db30SVinod Polimera 	struct drm_atomic_state *atomic_state = old_bridge_state->base.state;
179d011db30SVinod Polimera 	struct drm_encoder *encoder = bridge->encoder;
180d011db30SVinod Polimera 	struct drm_crtc *crtc;
181d011db30SVinod Polimera 	struct drm_crtc_state *new_crtc_state;
182d011db30SVinod Polimera 
183d011db30SVinod Polimera 	crtc = drm_atomic_get_old_crtc_for_encoder(atomic_state, encoder);
184d011db30SVinod Polimera 	if (!crtc)
185d011db30SVinod Polimera 		return;
186d011db30SVinod Polimera 
187d011db30SVinod Polimera 	new_crtc_state = drm_atomic_get_new_crtc_state(atomic_state, crtc);
188d011db30SVinod Polimera 	if (new_crtc_state && new_crtc_state->self_refresh_active)
189d011db30SVinod Polimera 		return;
19013dfc054SEric Anholt 
19113dfc054SEric Anholt 	drm_panel_unprepare(panel_bridge->panel);
19213dfc054SEric Anholt }
19313dfc054SEric Anholt 
panel_bridge_get_modes(struct drm_bridge * bridge,struct drm_connector * connector)1942be68b59SLaurent Pinchart static int panel_bridge_get_modes(struct drm_bridge *bridge,
1952be68b59SLaurent Pinchart 				  struct drm_connector *connector)
1962be68b59SLaurent Pinchart {
1972be68b59SLaurent Pinchart 	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
1982be68b59SLaurent Pinchart 
1992be68b59SLaurent Pinchart 	return drm_panel_get_modes(panel_bridge->panel, connector);
2002be68b59SLaurent Pinchart }
2012be68b59SLaurent Pinchart 
panel_bridge_debugfs_init(struct drm_bridge * bridge,struct dentry * root)2022509969aSDouglas Anderson static void panel_bridge_debugfs_init(struct drm_bridge *bridge,
2032509969aSDouglas Anderson 				      struct dentry *root)
2042509969aSDouglas Anderson {
2052509969aSDouglas Anderson 	struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
2062509969aSDouglas Anderson 	struct drm_panel *panel = panel_bridge->panel;
2072509969aSDouglas Anderson 
2082509969aSDouglas Anderson 	root = debugfs_create_dir("panel", root);
2092509969aSDouglas Anderson 	if (panel->funcs->debugfs_init)
2102509969aSDouglas Anderson 		panel->funcs->debugfs_init(panel, root);
2112509969aSDouglas Anderson }
2122509969aSDouglas Anderson 
21313dfc054SEric Anholt static const struct drm_bridge_funcs panel_bridge_bridge_funcs = {
21413dfc054SEric Anholt 	.attach = panel_bridge_attach,
21513dfc054SEric Anholt 	.detach = panel_bridge_detach,
216b67e0f53SVinod Polimera 	.atomic_pre_enable = panel_bridge_atomic_pre_enable,
217b67e0f53SVinod Polimera 	.atomic_enable = panel_bridge_atomic_enable,
218b67e0f53SVinod Polimera 	.atomic_disable = panel_bridge_atomic_disable,
219b67e0f53SVinod Polimera 	.atomic_post_disable = panel_bridge_atomic_post_disable,
2202be68b59SLaurent Pinchart 	.get_modes = panel_bridge_get_modes,
221cf52925aSBoris Brezillon 	.atomic_reset = drm_atomic_helper_bridge_reset,
222cf52925aSBoris Brezillon 	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
223cf52925aSBoris Brezillon 	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
224cf52925aSBoris Brezillon 	.atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt,
2252509969aSDouglas Anderson 	.debugfs_init = panel_bridge_debugfs_init,
22613dfc054SEric Anholt };
22713dfc054SEric Anholt 
22813dfc054SEric Anholt /**
22915b9ca16SHsin-Yi Wang  * drm_bridge_is_panel - Checks if a drm_bridge is a panel_bridge.
23015b9ca16SHsin-Yi Wang  *
23115b9ca16SHsin-Yi Wang  * @bridge: The drm_bridge to be checked.
23215b9ca16SHsin-Yi Wang  *
23315b9ca16SHsin-Yi Wang  * Returns true if the bridge is a panel bridge, or false otherwise.
23415b9ca16SHsin-Yi Wang  */
drm_bridge_is_panel(const struct drm_bridge * bridge)23515b9ca16SHsin-Yi Wang bool drm_bridge_is_panel(const struct drm_bridge *bridge)
23615b9ca16SHsin-Yi Wang {
23715b9ca16SHsin-Yi Wang 	return bridge->funcs == &panel_bridge_bridge_funcs;
23815b9ca16SHsin-Yi Wang }
23915b9ca16SHsin-Yi Wang EXPORT_SYMBOL(drm_bridge_is_panel);
24015b9ca16SHsin-Yi Wang 
24115b9ca16SHsin-Yi Wang /**
2420aa5eb3aSDaniel Vetter  * drm_panel_bridge_add - Creates a &drm_bridge and &drm_connector that
2430aa5eb3aSDaniel Vetter  * just calls the appropriate functions from &drm_panel.
24413dfc054SEric Anholt  *
24513dfc054SEric Anholt  * @panel: The drm_panel being wrapped.  Must be non-NULL.
24613dfc054SEric Anholt  *
24713dfc054SEric Anholt  * For drivers converting from directly using drm_panel: The expected
24813dfc054SEric Anholt  * usage pattern is that during either encoder module probe or DSI
24913dfc054SEric Anholt  * host attach, a drm_panel will be looked up through
25013dfc054SEric Anholt  * drm_of_find_panel_or_bridge().  drm_panel_bridge_add() is used to
25113dfc054SEric Anholt  * wrap that panel in the new bridge, and the result can then be
25213dfc054SEric Anholt  * passed to drm_bridge_attach().  The drm_panel_prepare() and related
25313dfc054SEric Anholt  * functions can be dropped from the encoder driver (they're now
25413dfc054SEric Anholt  * called by the KMS helpers before calling into the encoder), along
2550aa5eb3aSDaniel Vetter  * with connector creation.  When done with the bridge (after
2560aa5eb3aSDaniel Vetter  * drm_mode_config_cleanup() if the bridge has already been attached), then
25713dfc054SEric Anholt  * drm_panel_bridge_remove() to free it.
2580aa5eb3aSDaniel Vetter  *
25989958b7cSLaurent Pinchart  * The connector type is set to @panel->connector_type, which must be set to a
26089958b7cSLaurent Pinchart  * known type. Calling this function with a panel whose connector type is
26130be3031SEnric Balletbo i Serra  * DRM_MODE_CONNECTOR_Unknown will return ERR_PTR(-EINVAL).
26289958b7cSLaurent Pinchart  *
263cb05ec58SEnric Balletbo i Serra  * See devm_drm_panel_bridge_add() for an automatically managed version of this
2640aa5eb3aSDaniel Vetter  * function.
26513dfc054SEric Anholt  */
drm_panel_bridge_add(struct drm_panel * panel)26689958b7cSLaurent Pinchart struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel)
26789958b7cSLaurent Pinchart {
26889958b7cSLaurent Pinchart 	if (WARN_ON(panel->connector_type == DRM_MODE_CONNECTOR_Unknown))
26930be3031SEnric Balletbo i Serra 		return ERR_PTR(-EINVAL);
27089958b7cSLaurent Pinchart 
27189958b7cSLaurent Pinchart 	return drm_panel_bridge_add_typed(panel, panel->connector_type);
27289958b7cSLaurent Pinchart }
27389958b7cSLaurent Pinchart EXPORT_SYMBOL(drm_panel_bridge_add);
27489958b7cSLaurent Pinchart 
27589958b7cSLaurent Pinchart /**
27689958b7cSLaurent Pinchart  * drm_panel_bridge_add_typed - Creates a &drm_bridge and &drm_connector with
27789958b7cSLaurent Pinchart  * an explicit connector type.
27889958b7cSLaurent Pinchart  * @panel: The drm_panel being wrapped.  Must be non-NULL.
27989958b7cSLaurent Pinchart  * @connector_type: The connector type (DRM_MODE_CONNECTOR_*)
28089958b7cSLaurent Pinchart  *
28189958b7cSLaurent Pinchart  * This is just like drm_panel_bridge_add(), but forces the connector type to
28289958b7cSLaurent Pinchart  * @connector_type instead of infering it from the panel.
28389958b7cSLaurent Pinchart  *
28489958b7cSLaurent Pinchart  * This function is deprecated and should not be used in new drivers. Use
28589958b7cSLaurent Pinchart  * drm_panel_bridge_add() instead, and fix panel drivers as necessary if they
28689958b7cSLaurent Pinchart  * don't report a connector type.
28789958b7cSLaurent Pinchart  */
drm_panel_bridge_add_typed(struct drm_panel * panel,u32 connector_type)28889958b7cSLaurent Pinchart struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel,
28913dfc054SEric Anholt 					      u32 connector_type)
29013dfc054SEric Anholt {
29113dfc054SEric Anholt 	struct panel_bridge *panel_bridge;
29213dfc054SEric Anholt 
29313dfc054SEric Anholt 	if (!panel)
294e6f0acb2SEric Anholt 		return ERR_PTR(-EINVAL);
29513dfc054SEric Anholt 
29613dfc054SEric Anholt 	panel_bridge = devm_kzalloc(panel->dev, sizeof(*panel_bridge),
29713dfc054SEric Anholt 				    GFP_KERNEL);
29813dfc054SEric Anholt 	if (!panel_bridge)
29913dfc054SEric Anholt 		return ERR_PTR(-ENOMEM);
30013dfc054SEric Anholt 
30113dfc054SEric Anholt 	panel_bridge->connector_type = connector_type;
30213dfc054SEric Anholt 	panel_bridge->panel = panel;
30313dfc054SEric Anholt 
30413dfc054SEric Anholt 	panel_bridge->bridge.funcs = &panel_bridge_bridge_funcs;
30513dfc054SEric Anholt #ifdef CONFIG_OF
30613dfc054SEric Anholt 	panel_bridge->bridge.of_node = panel->dev->of_node;
30713dfc054SEric Anholt #endif
3082be68b59SLaurent Pinchart 	panel_bridge->bridge.ops = DRM_BRIDGE_OP_MODES;
3092be68b59SLaurent Pinchart 	panel_bridge->bridge.type = connector_type;
31013dfc054SEric Anholt 
3113a45d25dSInki Dae 	drm_bridge_add(&panel_bridge->bridge);
31213dfc054SEric Anholt 
31313dfc054SEric Anholt 	return &panel_bridge->bridge;
31413dfc054SEric Anholt }
31589958b7cSLaurent Pinchart EXPORT_SYMBOL(drm_panel_bridge_add_typed);
31613dfc054SEric Anholt 
31713dfc054SEric Anholt /**
31813dfc054SEric Anholt  * drm_panel_bridge_remove - Unregisters and frees a drm_bridge
31913dfc054SEric Anholt  * created by drm_panel_bridge_add().
32013dfc054SEric Anholt  *
32113dfc054SEric Anholt  * @bridge: The drm_bridge being freed.
32213dfc054SEric Anholt  */
drm_panel_bridge_remove(struct drm_bridge * bridge)32313dfc054SEric Anholt void drm_panel_bridge_remove(struct drm_bridge *bridge)
32413dfc054SEric Anholt {
3256b0e284cSbenjamin.gaignard@linaro.org 	struct panel_bridge *panel_bridge;
3266b0e284cSbenjamin.gaignard@linaro.org 
3276b0e284cSbenjamin.gaignard@linaro.org 	if (!bridge)
3286b0e284cSbenjamin.gaignard@linaro.org 		return;
3296b0e284cSbenjamin.gaignard@linaro.org 
3306b0e284cSbenjamin.gaignard@linaro.org 	if (bridge->funcs != &panel_bridge_bridge_funcs)
3316b0e284cSbenjamin.gaignard@linaro.org 		return;
3326b0e284cSbenjamin.gaignard@linaro.org 
3336b0e284cSbenjamin.gaignard@linaro.org 	panel_bridge = drm_bridge_to_panel_bridge(bridge);
33413dfc054SEric Anholt 
33513dfc054SEric Anholt 	drm_bridge_remove(bridge);
33613dfc054SEric Anholt 	devm_kfree(panel_bridge->panel->dev, bridge);
33713dfc054SEric Anholt }
33813dfc054SEric Anholt EXPORT_SYMBOL(drm_panel_bridge_remove);
33967022227SEric Anholt 
34015b9ca16SHsin-Yi Wang /**
34115b9ca16SHsin-Yi Wang  * drm_panel_bridge_set_orientation - Set the connector's panel orientation
34215b9ca16SHsin-Yi Wang  * from the bridge that can be transformed to panel bridge.
34315b9ca16SHsin-Yi Wang  *
34415b9ca16SHsin-Yi Wang  * @connector: The connector to be set panel orientation.
34515b9ca16SHsin-Yi Wang  * @bridge: The drm_bridge to be transformed to panel bridge.
34615b9ca16SHsin-Yi Wang  *
34715b9ca16SHsin-Yi Wang  * Returns 0 on success, negative errno on failure.
34815b9ca16SHsin-Yi Wang  */
drm_panel_bridge_set_orientation(struct drm_connector * connector,struct drm_bridge * bridge)34915b9ca16SHsin-Yi Wang int drm_panel_bridge_set_orientation(struct drm_connector *connector,
35015b9ca16SHsin-Yi Wang 				     struct drm_bridge *bridge)
35115b9ca16SHsin-Yi Wang {
35215b9ca16SHsin-Yi Wang 	struct panel_bridge *panel_bridge;
35315b9ca16SHsin-Yi Wang 
35415b9ca16SHsin-Yi Wang 	panel_bridge = drm_bridge_to_panel_bridge(bridge);
35515b9ca16SHsin-Yi Wang 
35615b9ca16SHsin-Yi Wang 	return drm_connector_set_orientation_from_panel(connector,
35715b9ca16SHsin-Yi Wang 							panel_bridge->panel);
35815b9ca16SHsin-Yi Wang }
35915b9ca16SHsin-Yi Wang EXPORT_SYMBOL(drm_panel_bridge_set_orientation);
36015b9ca16SHsin-Yi Wang 
devm_drm_panel_bridge_release(struct device * dev,void * res)36167022227SEric Anholt static void devm_drm_panel_bridge_release(struct device *dev, void *res)
36267022227SEric Anholt {
363b5c8ffc8SAdam Miotk 	struct drm_bridge *bridge = *(struct drm_bridge **)res;
36467022227SEric Anholt 
365b5c8ffc8SAdam Miotk 	if (!bridge)
366b5c8ffc8SAdam Miotk 		return;
367b5c8ffc8SAdam Miotk 
368b5c8ffc8SAdam Miotk 	drm_bridge_remove(bridge);
36967022227SEric Anholt }
37067022227SEric Anholt 
3710aa5eb3aSDaniel Vetter /**
3720aa5eb3aSDaniel Vetter  * devm_drm_panel_bridge_add - Creates a managed &drm_bridge and &drm_connector
3730aa5eb3aSDaniel Vetter  * that just calls the appropriate functions from &drm_panel.
3740aa5eb3aSDaniel Vetter  * @dev: device to tie the bridge lifetime to
3750aa5eb3aSDaniel Vetter  * @panel: The drm_panel being wrapped.  Must be non-NULL.
3760aa5eb3aSDaniel Vetter  *
3770aa5eb3aSDaniel Vetter  * This is the managed version of drm_panel_bridge_add() which automatically
3780aa5eb3aSDaniel Vetter  * calls drm_panel_bridge_remove() when @dev is unbound.
3790aa5eb3aSDaniel Vetter  */
devm_drm_panel_bridge_add(struct device * dev,struct drm_panel * panel)38067022227SEric Anholt struct drm_bridge *devm_drm_panel_bridge_add(struct device *dev,
38189958b7cSLaurent Pinchart 					     struct drm_panel *panel)
38289958b7cSLaurent Pinchart {
38389958b7cSLaurent Pinchart 	if (WARN_ON(panel->connector_type == DRM_MODE_CONNECTOR_Unknown))
38430be3031SEnric Balletbo i Serra 		return ERR_PTR(-EINVAL);
38589958b7cSLaurent Pinchart 
38689958b7cSLaurent Pinchart 	return devm_drm_panel_bridge_add_typed(dev, panel,
38789958b7cSLaurent Pinchart 					       panel->connector_type);
38889958b7cSLaurent Pinchart }
38989958b7cSLaurent Pinchart EXPORT_SYMBOL(devm_drm_panel_bridge_add);
39089958b7cSLaurent Pinchart 
39189958b7cSLaurent Pinchart /**
39289958b7cSLaurent Pinchart  * devm_drm_panel_bridge_add_typed - Creates a managed &drm_bridge and
39389958b7cSLaurent Pinchart  * &drm_connector with an explicit connector type.
39489958b7cSLaurent Pinchart  * @dev: device to tie the bridge lifetime to
39589958b7cSLaurent Pinchart  * @panel: The drm_panel being wrapped.  Must be non-NULL.
39689958b7cSLaurent Pinchart  * @connector_type: The connector type (DRM_MODE_CONNECTOR_*)
39789958b7cSLaurent Pinchart  *
39889958b7cSLaurent Pinchart  * This is just like devm_drm_panel_bridge_add(), but forces the connector type
39989958b7cSLaurent Pinchart  * to @connector_type instead of infering it from the panel.
40089958b7cSLaurent Pinchart  *
40189958b7cSLaurent Pinchart  * This function is deprecated and should not be used in new drivers. Use
40289958b7cSLaurent Pinchart  * devm_drm_panel_bridge_add() instead, and fix panel drivers as necessary if
40389958b7cSLaurent Pinchart  * they don't report a connector type.
40489958b7cSLaurent Pinchart  */
devm_drm_panel_bridge_add_typed(struct device * dev,struct drm_panel * panel,u32 connector_type)40589958b7cSLaurent Pinchart struct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev,
40667022227SEric Anholt 						   struct drm_panel *panel,
40767022227SEric Anholt 						   u32 connector_type)
40867022227SEric Anholt {
40967022227SEric Anholt 	struct drm_bridge **ptr, *bridge;
41067022227SEric Anholt 
41167022227SEric Anholt 	ptr = devres_alloc(devm_drm_panel_bridge_release, sizeof(*ptr),
41267022227SEric Anholt 			   GFP_KERNEL);
41367022227SEric Anholt 	if (!ptr)
41467022227SEric Anholt 		return ERR_PTR(-ENOMEM);
41567022227SEric Anholt 
41689958b7cSLaurent Pinchart 	bridge = drm_panel_bridge_add_typed(panel, connector_type);
4178dd4e8c4SMaxime Ripard 	if (IS_ERR(bridge)) {
41867022227SEric Anholt 		devres_free(ptr);
4198dd4e8c4SMaxime Ripard 		return bridge;
42067022227SEric Anholt 	}
42167022227SEric Anholt 
4225ea6b170SDave Stevenson 	bridge->pre_enable_prev_first = panel->prepare_prev_first;
4235ea6b170SDave Stevenson 
4248dd4e8c4SMaxime Ripard 	*ptr = bridge;
4258dd4e8c4SMaxime Ripard 	devres_add(dev, ptr);
4268dd4e8c4SMaxime Ripard 
42767022227SEric Anholt 	return bridge;
42867022227SEric Anholt }
42989958b7cSLaurent Pinchart EXPORT_SYMBOL(devm_drm_panel_bridge_add_typed);
430d383fb5fSSam Ravnborg 
drmm_drm_panel_bridge_release(struct drm_device * drm,void * ptr)431abea75e9SMaxime Ripard static void drmm_drm_panel_bridge_release(struct drm_device *drm, void *ptr)
432abea75e9SMaxime Ripard {
433abea75e9SMaxime Ripard 	struct drm_bridge *bridge = ptr;
434abea75e9SMaxime Ripard 
435abea75e9SMaxime Ripard 	drm_panel_bridge_remove(bridge);
436abea75e9SMaxime Ripard }
437abea75e9SMaxime Ripard 
438abea75e9SMaxime Ripard /**
439abea75e9SMaxime Ripard  * drmm_panel_bridge_add - Creates a DRM-managed &drm_bridge and
440abea75e9SMaxime Ripard  *                         &drm_connector that just calls the
441abea75e9SMaxime Ripard  *                         appropriate functions from &drm_panel.
442abea75e9SMaxime Ripard  *
443abea75e9SMaxime Ripard  * @drm: DRM device to tie the bridge lifetime to
444abea75e9SMaxime Ripard  * @panel: The drm_panel being wrapped.  Must be non-NULL.
445abea75e9SMaxime Ripard  *
446abea75e9SMaxime Ripard  * This is the DRM-managed version of drm_panel_bridge_add() which
447abea75e9SMaxime Ripard  * automatically calls drm_panel_bridge_remove() when @dev is cleaned
448abea75e9SMaxime Ripard  * up.
449abea75e9SMaxime Ripard  */
drmm_panel_bridge_add(struct drm_device * drm,struct drm_panel * panel)450abea75e9SMaxime Ripard struct drm_bridge *drmm_panel_bridge_add(struct drm_device *drm,
451abea75e9SMaxime Ripard 					 struct drm_panel *panel)
452abea75e9SMaxime Ripard {
453abea75e9SMaxime Ripard 	struct drm_bridge *bridge;
454abea75e9SMaxime Ripard 	int ret;
455abea75e9SMaxime Ripard 
456abea75e9SMaxime Ripard 	bridge = drm_panel_bridge_add_typed(panel, panel->connector_type);
457abea75e9SMaxime Ripard 	if (IS_ERR(bridge))
458abea75e9SMaxime Ripard 		return bridge;
459abea75e9SMaxime Ripard 
460abea75e9SMaxime Ripard 	ret = drmm_add_action_or_reset(drm, drmm_drm_panel_bridge_release,
461abea75e9SMaxime Ripard 				       bridge);
462abea75e9SMaxime Ripard 	if (ret)
463abea75e9SMaxime Ripard 		return ERR_PTR(ret);
464abea75e9SMaxime Ripard 
4650974687aSDave Stevenson 	bridge->pre_enable_prev_first = panel->prepare_prev_first;
4660974687aSDave Stevenson 
467abea75e9SMaxime Ripard 	return bridge;
468abea75e9SMaxime Ripard }
469abea75e9SMaxime Ripard EXPORT_SYMBOL(drmm_panel_bridge_add);
470abea75e9SMaxime Ripard 
471d383fb5fSSam Ravnborg /**
472d383fb5fSSam Ravnborg  * drm_panel_bridge_connector - return the connector for the panel bridge
47391fcf8e6SSam Ravnborg  * @bridge: The drm_bridge.
474d383fb5fSSam Ravnborg  *
475d383fb5fSSam Ravnborg  * drm_panel_bridge creates the connector.
476d383fb5fSSam Ravnborg  * This function gives external access to the connector.
477d383fb5fSSam Ravnborg  *
478d383fb5fSSam Ravnborg  * Returns: Pointer to drm_connector
479d383fb5fSSam Ravnborg  */
drm_panel_bridge_connector(struct drm_bridge * bridge)480d383fb5fSSam Ravnborg struct drm_connector *drm_panel_bridge_connector(struct drm_bridge *bridge)
481d383fb5fSSam Ravnborg {
482d383fb5fSSam Ravnborg 	struct panel_bridge *panel_bridge;
483d383fb5fSSam Ravnborg 
484d383fb5fSSam Ravnborg 	panel_bridge = drm_bridge_to_panel_bridge(bridge);
485d383fb5fSSam Ravnborg 
486d383fb5fSSam Ravnborg 	return &panel_bridge->connector;
487d383fb5fSSam Ravnborg }
4883cc1430cSMihail Atanassov EXPORT_SYMBOL(drm_panel_bridge_connector);
489d4ae66f1SMaxime Ripard 
490d4ae66f1SMaxime Ripard #ifdef CONFIG_OF
491d4ae66f1SMaxime Ripard /**
492d4ae66f1SMaxime Ripard  * devm_drm_of_get_bridge - Return next bridge in the chain
493d4ae66f1SMaxime Ripard  * @dev: device to tie the bridge lifetime to
494d4ae66f1SMaxime Ripard  * @np: device tree node containing encoder output ports
495d4ae66f1SMaxime Ripard  * @port: port in the device tree node
496d4ae66f1SMaxime Ripard  * @endpoint: endpoint in the device tree node
497d4ae66f1SMaxime Ripard  *
498d4ae66f1SMaxime Ripard  * Given a DT node's port and endpoint number, finds the connected node
499d4ae66f1SMaxime Ripard  * and returns the associated bridge if any, or creates and returns a
500d4ae66f1SMaxime Ripard  * drm panel bridge instance if a panel is connected.
501d4ae66f1SMaxime Ripard  *
502d4ae66f1SMaxime Ripard  * Returns a pointer to the bridge if successful, or an error pointer
503d4ae66f1SMaxime Ripard  * otherwise.
504d4ae66f1SMaxime Ripard  */
devm_drm_of_get_bridge(struct device * dev,struct device_node * np,u32 port,u32 endpoint)505d4ae66f1SMaxime Ripard struct drm_bridge *devm_drm_of_get_bridge(struct device *dev,
506d4ae66f1SMaxime Ripard 					  struct device_node *np,
507d4ae66f1SMaxime Ripard 					  u32 port, u32 endpoint)
508d4ae66f1SMaxime Ripard {
509d4ae66f1SMaxime Ripard 	struct drm_bridge *bridge;
510d4ae66f1SMaxime Ripard 	struct drm_panel *panel;
511d4ae66f1SMaxime Ripard 	int ret;
512d4ae66f1SMaxime Ripard 
513d4ae66f1SMaxime Ripard 	ret = drm_of_find_panel_or_bridge(np, port, endpoint,
514d4ae66f1SMaxime Ripard 					  &panel, &bridge);
515d4ae66f1SMaxime Ripard 	if (ret)
516d4ae66f1SMaxime Ripard 		return ERR_PTR(ret);
517d4ae66f1SMaxime Ripard 
518d4ae66f1SMaxime Ripard 	if (panel)
519d4ae66f1SMaxime Ripard 		bridge = devm_drm_panel_bridge_add(dev, panel);
520d4ae66f1SMaxime Ripard 
521d4ae66f1SMaxime Ripard 	return bridge;
522d4ae66f1SMaxime Ripard }
523d4ae66f1SMaxime Ripard EXPORT_SYMBOL(devm_drm_of_get_bridge);
524ae9f1f2cSMaxime Ripard 
525ae9f1f2cSMaxime Ripard /**
526ae9f1f2cSMaxime Ripard  * drmm_of_get_bridge - Return next bridge in the chain
527ae9f1f2cSMaxime Ripard  * @drm: device to tie the bridge lifetime to
528ae9f1f2cSMaxime Ripard  * @np: device tree node containing encoder output ports
529ae9f1f2cSMaxime Ripard  * @port: port in the device tree node
530ae9f1f2cSMaxime Ripard  * @endpoint: endpoint in the device tree node
531ae9f1f2cSMaxime Ripard  *
532ae9f1f2cSMaxime Ripard  * Given a DT node's port and endpoint number, finds the connected node
533ae9f1f2cSMaxime Ripard  * and returns the associated bridge if any, or creates and returns a
534ae9f1f2cSMaxime Ripard  * drm panel bridge instance if a panel is connected.
535ae9f1f2cSMaxime Ripard  *
536ae9f1f2cSMaxime Ripard  * Returns a drmm managed pointer to the bridge if successful, or an error
537ae9f1f2cSMaxime Ripard  * pointer otherwise.
538ae9f1f2cSMaxime Ripard  */
drmm_of_get_bridge(struct drm_device * drm,struct device_node * np,u32 port,u32 endpoint)539ae9f1f2cSMaxime Ripard struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm,
540ae9f1f2cSMaxime Ripard 				      struct device_node *np,
541ae9f1f2cSMaxime Ripard 				      u32 port, u32 endpoint)
542ae9f1f2cSMaxime Ripard {
543ae9f1f2cSMaxime Ripard 	struct drm_bridge *bridge;
544ae9f1f2cSMaxime Ripard 	struct drm_panel *panel;
545ae9f1f2cSMaxime Ripard 	int ret;
546ae9f1f2cSMaxime Ripard 
547ae9f1f2cSMaxime Ripard 	ret = drm_of_find_panel_or_bridge(np, port, endpoint,
548ae9f1f2cSMaxime Ripard 					  &panel, &bridge);
549ae9f1f2cSMaxime Ripard 	if (ret)
550ae9f1f2cSMaxime Ripard 		return ERR_PTR(ret);
551ae9f1f2cSMaxime Ripard 
552ae9f1f2cSMaxime Ripard 	if (panel)
553ae9f1f2cSMaxime Ripard 		bridge = drmm_panel_bridge_add(drm, panel);
554ae9f1f2cSMaxime Ripard 
555ae9f1f2cSMaxime Ripard 	return bridge;
556ae9f1f2cSMaxime Ripard }
557ae9f1f2cSMaxime Ripard EXPORT_SYMBOL(drmm_of_get_bridge);
558ae9f1f2cSMaxime Ripard 
559d4ae66f1SMaxime Ripard #endif
560