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