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 * 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 * 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 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 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 84cde4c44dSDaniel Vetter drm_connector_attach_encoder(&panel_bridge->connector, 8513dfc054SEric Anholt bridge->encoder); 8613dfc054SEric Anholt 8734263c1bSMarek Szyprowski if (bridge->dev->registered) { 88934aef88SJagan Teki if (connector->funcs->reset) 89934aef88SJagan Teki connector->funcs->reset(connector); 9034263c1bSMarek Szyprowski drm_connector_register(connector); 9134263c1bSMarek Szyprowski } 92934aef88SJagan Teki 9313dfc054SEric Anholt return 0; 9413dfc054SEric Anholt } 9513dfc054SEric Anholt 9613dfc054SEric Anholt static void panel_bridge_detach(struct drm_bridge *bridge) 9713dfc054SEric Anholt { 984d906839SPaul Cercueil struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); 994d906839SPaul Cercueil struct drm_connector *connector = &panel_bridge->connector; 1004d906839SPaul Cercueil 1014d906839SPaul Cercueil /* 1024d906839SPaul Cercueil * Cleanup the connector if we know it was initialized. 1034d906839SPaul Cercueil * 1044d906839SPaul Cercueil * FIXME: This wouldn't be needed if the panel_bridge structure was 1054d906839SPaul Cercueil * allocated with drmm_kzalloc(). This might be tricky since the 1064d906839SPaul Cercueil * drm_device pointer can only be retrieved when the bridge is attached. 1074d906839SPaul Cercueil */ 1084d906839SPaul Cercueil if (connector->dev) 1094d906839SPaul Cercueil drm_connector_cleanup(connector); 11013dfc054SEric Anholt } 11113dfc054SEric Anholt 112*b67e0f53SVinod Polimera static void panel_bridge_atomic_pre_enable(struct drm_bridge *bridge, 113*b67e0f53SVinod Polimera struct drm_bridge_state *old_bridge_state) 11413dfc054SEric Anholt { 11513dfc054SEric Anholt struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); 11613dfc054SEric Anholt 11713dfc054SEric Anholt drm_panel_prepare(panel_bridge->panel); 11813dfc054SEric Anholt } 11913dfc054SEric Anholt 120*b67e0f53SVinod Polimera static void panel_bridge_atomic_enable(struct drm_bridge *bridge, 121*b67e0f53SVinod Polimera struct drm_bridge_state *old_bridge_state) 12213dfc054SEric Anholt { 12313dfc054SEric Anholt struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); 12413dfc054SEric Anholt 12513dfc054SEric Anholt drm_panel_enable(panel_bridge->panel); 12613dfc054SEric Anholt } 12713dfc054SEric Anholt 128*b67e0f53SVinod Polimera static void panel_bridge_atomic_disable(struct drm_bridge *bridge, 129*b67e0f53SVinod Polimera struct drm_bridge_state *old_bridge_state) 13013dfc054SEric Anholt { 13113dfc054SEric Anholt struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); 13213dfc054SEric Anholt 13313dfc054SEric Anholt drm_panel_disable(panel_bridge->panel); 13413dfc054SEric Anholt } 13513dfc054SEric Anholt 136*b67e0f53SVinod Polimera static void panel_bridge_atomic_post_disable(struct drm_bridge *bridge, 137*b67e0f53SVinod Polimera struct drm_bridge_state *old_bridge_state) 13813dfc054SEric Anholt { 13913dfc054SEric Anholt struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); 14013dfc054SEric Anholt 14113dfc054SEric Anholt drm_panel_unprepare(panel_bridge->panel); 14213dfc054SEric Anholt } 14313dfc054SEric Anholt 1442be68b59SLaurent Pinchart static int panel_bridge_get_modes(struct drm_bridge *bridge, 1452be68b59SLaurent Pinchart struct drm_connector *connector) 1462be68b59SLaurent Pinchart { 1472be68b59SLaurent Pinchart struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); 1482be68b59SLaurent Pinchart 1492be68b59SLaurent Pinchart return drm_panel_get_modes(panel_bridge->panel, connector); 1502be68b59SLaurent Pinchart } 1512be68b59SLaurent Pinchart 1522509969aSDouglas Anderson static void panel_bridge_debugfs_init(struct drm_bridge *bridge, 1532509969aSDouglas Anderson struct dentry *root) 1542509969aSDouglas Anderson { 1552509969aSDouglas Anderson struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); 1562509969aSDouglas Anderson struct drm_panel *panel = panel_bridge->panel; 1572509969aSDouglas Anderson 1582509969aSDouglas Anderson root = debugfs_create_dir("panel", root); 1592509969aSDouglas Anderson if (panel->funcs->debugfs_init) 1602509969aSDouglas Anderson panel->funcs->debugfs_init(panel, root); 1612509969aSDouglas Anderson } 1622509969aSDouglas Anderson 16313dfc054SEric Anholt static const struct drm_bridge_funcs panel_bridge_bridge_funcs = { 16413dfc054SEric Anholt .attach = panel_bridge_attach, 16513dfc054SEric Anholt .detach = panel_bridge_detach, 166*b67e0f53SVinod Polimera .atomic_pre_enable = panel_bridge_atomic_pre_enable, 167*b67e0f53SVinod Polimera .atomic_enable = panel_bridge_atomic_enable, 168*b67e0f53SVinod Polimera .atomic_disable = panel_bridge_atomic_disable, 169*b67e0f53SVinod Polimera .atomic_post_disable = panel_bridge_atomic_post_disable, 1702be68b59SLaurent Pinchart .get_modes = panel_bridge_get_modes, 171cf52925aSBoris Brezillon .atomic_reset = drm_atomic_helper_bridge_reset, 172cf52925aSBoris Brezillon .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, 173cf52925aSBoris Brezillon .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, 174cf52925aSBoris Brezillon .atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt, 1752509969aSDouglas Anderson .debugfs_init = panel_bridge_debugfs_init, 17613dfc054SEric Anholt }; 17713dfc054SEric Anholt 17813dfc054SEric Anholt /** 17915b9ca16SHsin-Yi Wang * drm_bridge_is_panel - Checks if a drm_bridge is a panel_bridge. 18015b9ca16SHsin-Yi Wang * 18115b9ca16SHsin-Yi Wang * @bridge: The drm_bridge to be checked. 18215b9ca16SHsin-Yi Wang * 18315b9ca16SHsin-Yi Wang * Returns true if the bridge is a panel bridge, or false otherwise. 18415b9ca16SHsin-Yi Wang */ 18515b9ca16SHsin-Yi Wang bool drm_bridge_is_panel(const struct drm_bridge *bridge) 18615b9ca16SHsin-Yi Wang { 18715b9ca16SHsin-Yi Wang return bridge->funcs == &panel_bridge_bridge_funcs; 18815b9ca16SHsin-Yi Wang } 18915b9ca16SHsin-Yi Wang EXPORT_SYMBOL(drm_bridge_is_panel); 19015b9ca16SHsin-Yi Wang 19115b9ca16SHsin-Yi Wang /** 1920aa5eb3aSDaniel Vetter * drm_panel_bridge_add - Creates a &drm_bridge and &drm_connector that 1930aa5eb3aSDaniel Vetter * just calls the appropriate functions from &drm_panel. 19413dfc054SEric Anholt * 19513dfc054SEric Anholt * @panel: The drm_panel being wrapped. Must be non-NULL. 19613dfc054SEric Anholt * 19713dfc054SEric Anholt * For drivers converting from directly using drm_panel: The expected 19813dfc054SEric Anholt * usage pattern is that during either encoder module probe or DSI 19913dfc054SEric Anholt * host attach, a drm_panel will be looked up through 20013dfc054SEric Anholt * drm_of_find_panel_or_bridge(). drm_panel_bridge_add() is used to 20113dfc054SEric Anholt * wrap that panel in the new bridge, and the result can then be 20213dfc054SEric Anholt * passed to drm_bridge_attach(). The drm_panel_prepare() and related 20313dfc054SEric Anholt * functions can be dropped from the encoder driver (they're now 20413dfc054SEric Anholt * called by the KMS helpers before calling into the encoder), along 2050aa5eb3aSDaniel Vetter * with connector creation. When done with the bridge (after 2060aa5eb3aSDaniel Vetter * drm_mode_config_cleanup() if the bridge has already been attached), then 20713dfc054SEric Anholt * drm_panel_bridge_remove() to free it. 2080aa5eb3aSDaniel Vetter * 20989958b7cSLaurent Pinchart * The connector type is set to @panel->connector_type, which must be set to a 21089958b7cSLaurent Pinchart * known type. Calling this function with a panel whose connector type is 21130be3031SEnric Balletbo i Serra * DRM_MODE_CONNECTOR_Unknown will return ERR_PTR(-EINVAL). 21289958b7cSLaurent Pinchart * 213cb05ec58SEnric Balletbo i Serra * See devm_drm_panel_bridge_add() for an automatically managed version of this 2140aa5eb3aSDaniel Vetter * function. 21513dfc054SEric Anholt */ 21689958b7cSLaurent Pinchart struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel) 21789958b7cSLaurent Pinchart { 21889958b7cSLaurent Pinchart if (WARN_ON(panel->connector_type == DRM_MODE_CONNECTOR_Unknown)) 21930be3031SEnric Balletbo i Serra return ERR_PTR(-EINVAL); 22089958b7cSLaurent Pinchart 22189958b7cSLaurent Pinchart return drm_panel_bridge_add_typed(panel, panel->connector_type); 22289958b7cSLaurent Pinchart } 22389958b7cSLaurent Pinchart EXPORT_SYMBOL(drm_panel_bridge_add); 22489958b7cSLaurent Pinchart 22589958b7cSLaurent Pinchart /** 22689958b7cSLaurent Pinchart * drm_panel_bridge_add_typed - Creates a &drm_bridge and &drm_connector with 22789958b7cSLaurent Pinchart * an explicit connector type. 22889958b7cSLaurent Pinchart * @panel: The drm_panel being wrapped. Must be non-NULL. 22989958b7cSLaurent Pinchart * @connector_type: The connector type (DRM_MODE_CONNECTOR_*) 23089958b7cSLaurent Pinchart * 23189958b7cSLaurent Pinchart * This is just like drm_panel_bridge_add(), but forces the connector type to 23289958b7cSLaurent Pinchart * @connector_type instead of infering it from the panel. 23389958b7cSLaurent Pinchart * 23489958b7cSLaurent Pinchart * This function is deprecated and should not be used in new drivers. Use 23589958b7cSLaurent Pinchart * drm_panel_bridge_add() instead, and fix panel drivers as necessary if they 23689958b7cSLaurent Pinchart * don't report a connector type. 23789958b7cSLaurent Pinchart */ 23889958b7cSLaurent Pinchart struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel, 23913dfc054SEric Anholt u32 connector_type) 24013dfc054SEric Anholt { 24113dfc054SEric Anholt struct panel_bridge *panel_bridge; 24213dfc054SEric Anholt 24313dfc054SEric Anholt if (!panel) 244e6f0acb2SEric Anholt return ERR_PTR(-EINVAL); 24513dfc054SEric Anholt 24613dfc054SEric Anholt panel_bridge = devm_kzalloc(panel->dev, sizeof(*panel_bridge), 24713dfc054SEric Anholt GFP_KERNEL); 24813dfc054SEric Anholt if (!panel_bridge) 24913dfc054SEric Anholt return ERR_PTR(-ENOMEM); 25013dfc054SEric Anholt 25113dfc054SEric Anholt panel_bridge->connector_type = connector_type; 25213dfc054SEric Anholt panel_bridge->panel = panel; 25313dfc054SEric Anholt 25413dfc054SEric Anholt panel_bridge->bridge.funcs = &panel_bridge_bridge_funcs; 25513dfc054SEric Anholt #ifdef CONFIG_OF 25613dfc054SEric Anholt panel_bridge->bridge.of_node = panel->dev->of_node; 25713dfc054SEric Anholt #endif 2582be68b59SLaurent Pinchart panel_bridge->bridge.ops = DRM_BRIDGE_OP_MODES; 2592be68b59SLaurent Pinchart panel_bridge->bridge.type = connector_type; 26013dfc054SEric Anholt 2613a45d25dSInki Dae drm_bridge_add(&panel_bridge->bridge); 26213dfc054SEric Anholt 26313dfc054SEric Anholt return &panel_bridge->bridge; 26413dfc054SEric Anholt } 26589958b7cSLaurent Pinchart EXPORT_SYMBOL(drm_panel_bridge_add_typed); 26613dfc054SEric Anholt 26713dfc054SEric Anholt /** 26813dfc054SEric Anholt * drm_panel_bridge_remove - Unregisters and frees a drm_bridge 26913dfc054SEric Anholt * created by drm_panel_bridge_add(). 27013dfc054SEric Anholt * 27113dfc054SEric Anholt * @bridge: The drm_bridge being freed. 27213dfc054SEric Anholt */ 27313dfc054SEric Anholt void drm_panel_bridge_remove(struct drm_bridge *bridge) 27413dfc054SEric Anholt { 2756b0e284cSbenjamin.gaignard@linaro.org struct panel_bridge *panel_bridge; 2766b0e284cSbenjamin.gaignard@linaro.org 2776b0e284cSbenjamin.gaignard@linaro.org if (!bridge) 2786b0e284cSbenjamin.gaignard@linaro.org return; 2796b0e284cSbenjamin.gaignard@linaro.org 2806b0e284cSbenjamin.gaignard@linaro.org if (bridge->funcs != &panel_bridge_bridge_funcs) 2816b0e284cSbenjamin.gaignard@linaro.org return; 2826b0e284cSbenjamin.gaignard@linaro.org 2836b0e284cSbenjamin.gaignard@linaro.org panel_bridge = drm_bridge_to_panel_bridge(bridge); 28413dfc054SEric Anholt 28513dfc054SEric Anholt drm_bridge_remove(bridge); 28613dfc054SEric Anholt devm_kfree(panel_bridge->panel->dev, bridge); 28713dfc054SEric Anholt } 28813dfc054SEric Anholt EXPORT_SYMBOL(drm_panel_bridge_remove); 28967022227SEric Anholt 29015b9ca16SHsin-Yi Wang /** 29115b9ca16SHsin-Yi Wang * drm_panel_bridge_set_orientation - Set the connector's panel orientation 29215b9ca16SHsin-Yi Wang * from the bridge that can be transformed to panel bridge. 29315b9ca16SHsin-Yi Wang * 29415b9ca16SHsin-Yi Wang * @connector: The connector to be set panel orientation. 29515b9ca16SHsin-Yi Wang * @bridge: The drm_bridge to be transformed to panel bridge. 29615b9ca16SHsin-Yi Wang * 29715b9ca16SHsin-Yi Wang * Returns 0 on success, negative errno on failure. 29815b9ca16SHsin-Yi Wang */ 29915b9ca16SHsin-Yi Wang int drm_panel_bridge_set_orientation(struct drm_connector *connector, 30015b9ca16SHsin-Yi Wang struct drm_bridge *bridge) 30115b9ca16SHsin-Yi Wang { 30215b9ca16SHsin-Yi Wang struct panel_bridge *panel_bridge; 30315b9ca16SHsin-Yi Wang 30415b9ca16SHsin-Yi Wang panel_bridge = drm_bridge_to_panel_bridge(bridge); 30515b9ca16SHsin-Yi Wang 30615b9ca16SHsin-Yi Wang return drm_connector_set_orientation_from_panel(connector, 30715b9ca16SHsin-Yi Wang panel_bridge->panel); 30815b9ca16SHsin-Yi Wang } 30915b9ca16SHsin-Yi Wang EXPORT_SYMBOL(drm_panel_bridge_set_orientation); 31015b9ca16SHsin-Yi Wang 31167022227SEric Anholt static void devm_drm_panel_bridge_release(struct device *dev, void *res) 31267022227SEric Anholt { 31367022227SEric Anholt struct drm_bridge **bridge = res; 31467022227SEric Anholt 31567022227SEric Anholt drm_panel_bridge_remove(*bridge); 31667022227SEric Anholt } 31767022227SEric Anholt 3180aa5eb3aSDaniel Vetter /** 3190aa5eb3aSDaniel Vetter * devm_drm_panel_bridge_add - Creates a managed &drm_bridge and &drm_connector 3200aa5eb3aSDaniel Vetter * that just calls the appropriate functions from &drm_panel. 3210aa5eb3aSDaniel Vetter * @dev: device to tie the bridge lifetime to 3220aa5eb3aSDaniel Vetter * @panel: The drm_panel being wrapped. Must be non-NULL. 3230aa5eb3aSDaniel Vetter * 3240aa5eb3aSDaniel Vetter * This is the managed version of drm_panel_bridge_add() which automatically 3250aa5eb3aSDaniel Vetter * calls drm_panel_bridge_remove() when @dev is unbound. 3260aa5eb3aSDaniel Vetter */ 32767022227SEric Anholt struct drm_bridge *devm_drm_panel_bridge_add(struct device *dev, 32889958b7cSLaurent Pinchart struct drm_panel *panel) 32989958b7cSLaurent Pinchart { 33089958b7cSLaurent Pinchart if (WARN_ON(panel->connector_type == DRM_MODE_CONNECTOR_Unknown)) 33130be3031SEnric Balletbo i Serra return ERR_PTR(-EINVAL); 33289958b7cSLaurent Pinchart 33389958b7cSLaurent Pinchart return devm_drm_panel_bridge_add_typed(dev, panel, 33489958b7cSLaurent Pinchart panel->connector_type); 33589958b7cSLaurent Pinchart } 33689958b7cSLaurent Pinchart EXPORT_SYMBOL(devm_drm_panel_bridge_add); 33789958b7cSLaurent Pinchart 33889958b7cSLaurent Pinchart /** 33989958b7cSLaurent Pinchart * devm_drm_panel_bridge_add_typed - Creates a managed &drm_bridge and 34089958b7cSLaurent Pinchart * &drm_connector with an explicit connector type. 34189958b7cSLaurent Pinchart * @dev: device to tie the bridge lifetime to 34289958b7cSLaurent Pinchart * @panel: The drm_panel being wrapped. Must be non-NULL. 34389958b7cSLaurent Pinchart * @connector_type: The connector type (DRM_MODE_CONNECTOR_*) 34489958b7cSLaurent Pinchart * 34589958b7cSLaurent Pinchart * This is just like devm_drm_panel_bridge_add(), but forces the connector type 34689958b7cSLaurent Pinchart * to @connector_type instead of infering it from the panel. 34789958b7cSLaurent Pinchart * 34889958b7cSLaurent Pinchart * This function is deprecated and should not be used in new drivers. Use 34989958b7cSLaurent Pinchart * devm_drm_panel_bridge_add() instead, and fix panel drivers as necessary if 35089958b7cSLaurent Pinchart * they don't report a connector type. 35189958b7cSLaurent Pinchart */ 35289958b7cSLaurent Pinchart struct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev, 35367022227SEric Anholt struct drm_panel *panel, 35467022227SEric Anholt u32 connector_type) 35567022227SEric Anholt { 35667022227SEric Anholt struct drm_bridge **ptr, *bridge; 35767022227SEric Anholt 35867022227SEric Anholt ptr = devres_alloc(devm_drm_panel_bridge_release, sizeof(*ptr), 35967022227SEric Anholt GFP_KERNEL); 36067022227SEric Anholt if (!ptr) 36167022227SEric Anholt return ERR_PTR(-ENOMEM); 36267022227SEric Anholt 36389958b7cSLaurent Pinchart bridge = drm_panel_bridge_add_typed(panel, connector_type); 36467022227SEric Anholt if (!IS_ERR(bridge)) { 36567022227SEric Anholt *ptr = bridge; 36667022227SEric Anholt devres_add(dev, ptr); 36767022227SEric Anholt } else { 36867022227SEric Anholt devres_free(ptr); 36967022227SEric Anholt } 37067022227SEric Anholt 3715ea6b170SDave Stevenson bridge->pre_enable_prev_first = panel->prepare_prev_first; 3725ea6b170SDave Stevenson 37367022227SEric Anholt return bridge; 37467022227SEric Anholt } 37589958b7cSLaurent Pinchart EXPORT_SYMBOL(devm_drm_panel_bridge_add_typed); 376d383fb5fSSam Ravnborg 377abea75e9SMaxime Ripard static void drmm_drm_panel_bridge_release(struct drm_device *drm, void *ptr) 378abea75e9SMaxime Ripard { 379abea75e9SMaxime Ripard struct drm_bridge *bridge = ptr; 380abea75e9SMaxime Ripard 381abea75e9SMaxime Ripard drm_panel_bridge_remove(bridge); 382abea75e9SMaxime Ripard } 383abea75e9SMaxime Ripard 384abea75e9SMaxime Ripard /** 385abea75e9SMaxime Ripard * drmm_panel_bridge_add - Creates a DRM-managed &drm_bridge and 386abea75e9SMaxime Ripard * &drm_connector that just calls the 387abea75e9SMaxime Ripard * appropriate functions from &drm_panel. 388abea75e9SMaxime Ripard * 389abea75e9SMaxime Ripard * @drm: DRM device to tie the bridge lifetime to 390abea75e9SMaxime Ripard * @panel: The drm_panel being wrapped. Must be non-NULL. 391abea75e9SMaxime Ripard * 392abea75e9SMaxime Ripard * This is the DRM-managed version of drm_panel_bridge_add() which 393abea75e9SMaxime Ripard * automatically calls drm_panel_bridge_remove() when @dev is cleaned 394abea75e9SMaxime Ripard * up. 395abea75e9SMaxime Ripard */ 396abea75e9SMaxime Ripard struct drm_bridge *drmm_panel_bridge_add(struct drm_device *drm, 397abea75e9SMaxime Ripard struct drm_panel *panel) 398abea75e9SMaxime Ripard { 399abea75e9SMaxime Ripard struct drm_bridge *bridge; 400abea75e9SMaxime Ripard int ret; 401abea75e9SMaxime Ripard 402abea75e9SMaxime Ripard bridge = drm_panel_bridge_add_typed(panel, panel->connector_type); 403abea75e9SMaxime Ripard if (IS_ERR(bridge)) 404abea75e9SMaxime Ripard return bridge; 405abea75e9SMaxime Ripard 406abea75e9SMaxime Ripard ret = drmm_add_action_or_reset(drm, drmm_drm_panel_bridge_release, 407abea75e9SMaxime Ripard bridge); 408abea75e9SMaxime Ripard if (ret) 409abea75e9SMaxime Ripard return ERR_PTR(ret); 410abea75e9SMaxime Ripard 4110974687aSDave Stevenson bridge->pre_enable_prev_first = panel->prepare_prev_first; 4120974687aSDave Stevenson 413abea75e9SMaxime Ripard return bridge; 414abea75e9SMaxime Ripard } 415abea75e9SMaxime Ripard EXPORT_SYMBOL(drmm_panel_bridge_add); 416abea75e9SMaxime Ripard 417d383fb5fSSam Ravnborg /** 418d383fb5fSSam Ravnborg * drm_panel_bridge_connector - return the connector for the panel bridge 41991fcf8e6SSam Ravnborg * @bridge: The drm_bridge. 420d383fb5fSSam Ravnborg * 421d383fb5fSSam Ravnborg * drm_panel_bridge creates the connector. 422d383fb5fSSam Ravnborg * This function gives external access to the connector. 423d383fb5fSSam Ravnborg * 424d383fb5fSSam Ravnborg * Returns: Pointer to drm_connector 425d383fb5fSSam Ravnborg */ 426d383fb5fSSam Ravnborg struct drm_connector *drm_panel_bridge_connector(struct drm_bridge *bridge) 427d383fb5fSSam Ravnborg { 428d383fb5fSSam Ravnborg struct panel_bridge *panel_bridge; 429d383fb5fSSam Ravnborg 430d383fb5fSSam Ravnborg panel_bridge = drm_bridge_to_panel_bridge(bridge); 431d383fb5fSSam Ravnborg 432d383fb5fSSam Ravnborg return &panel_bridge->connector; 433d383fb5fSSam Ravnborg } 4343cc1430cSMihail Atanassov EXPORT_SYMBOL(drm_panel_bridge_connector); 435d4ae66f1SMaxime Ripard 436d4ae66f1SMaxime Ripard #ifdef CONFIG_OF 437d4ae66f1SMaxime Ripard /** 438d4ae66f1SMaxime Ripard * devm_drm_of_get_bridge - Return next bridge in the chain 439d4ae66f1SMaxime Ripard * @dev: device to tie the bridge lifetime to 440d4ae66f1SMaxime Ripard * @np: device tree node containing encoder output ports 441d4ae66f1SMaxime Ripard * @port: port in the device tree node 442d4ae66f1SMaxime Ripard * @endpoint: endpoint in the device tree node 443d4ae66f1SMaxime Ripard * 444d4ae66f1SMaxime Ripard * Given a DT node's port and endpoint number, finds the connected node 445d4ae66f1SMaxime Ripard * and returns the associated bridge if any, or creates and returns a 446d4ae66f1SMaxime Ripard * drm panel bridge instance if a panel is connected. 447d4ae66f1SMaxime Ripard * 448d4ae66f1SMaxime Ripard * Returns a pointer to the bridge if successful, or an error pointer 449d4ae66f1SMaxime Ripard * otherwise. 450d4ae66f1SMaxime Ripard */ 451d4ae66f1SMaxime Ripard struct drm_bridge *devm_drm_of_get_bridge(struct device *dev, 452d4ae66f1SMaxime Ripard struct device_node *np, 453d4ae66f1SMaxime Ripard u32 port, u32 endpoint) 454d4ae66f1SMaxime Ripard { 455d4ae66f1SMaxime Ripard struct drm_bridge *bridge; 456d4ae66f1SMaxime Ripard struct drm_panel *panel; 457d4ae66f1SMaxime Ripard int ret; 458d4ae66f1SMaxime Ripard 459d4ae66f1SMaxime Ripard ret = drm_of_find_panel_or_bridge(np, port, endpoint, 460d4ae66f1SMaxime Ripard &panel, &bridge); 461d4ae66f1SMaxime Ripard if (ret) 462d4ae66f1SMaxime Ripard return ERR_PTR(ret); 463d4ae66f1SMaxime Ripard 464d4ae66f1SMaxime Ripard if (panel) 465d4ae66f1SMaxime Ripard bridge = devm_drm_panel_bridge_add(dev, panel); 466d4ae66f1SMaxime Ripard 467d4ae66f1SMaxime Ripard return bridge; 468d4ae66f1SMaxime Ripard } 469d4ae66f1SMaxime Ripard EXPORT_SYMBOL(devm_drm_of_get_bridge); 470ae9f1f2cSMaxime Ripard 471ae9f1f2cSMaxime Ripard /** 472ae9f1f2cSMaxime Ripard * drmm_of_get_bridge - Return next bridge in the chain 473ae9f1f2cSMaxime Ripard * @drm: device to tie the bridge lifetime to 474ae9f1f2cSMaxime Ripard * @np: device tree node containing encoder output ports 475ae9f1f2cSMaxime Ripard * @port: port in the device tree node 476ae9f1f2cSMaxime Ripard * @endpoint: endpoint in the device tree node 477ae9f1f2cSMaxime Ripard * 478ae9f1f2cSMaxime Ripard * Given a DT node's port and endpoint number, finds the connected node 479ae9f1f2cSMaxime Ripard * and returns the associated bridge if any, or creates and returns a 480ae9f1f2cSMaxime Ripard * drm panel bridge instance if a panel is connected. 481ae9f1f2cSMaxime Ripard * 482ae9f1f2cSMaxime Ripard * Returns a drmm managed pointer to the bridge if successful, or an error 483ae9f1f2cSMaxime Ripard * pointer otherwise. 484ae9f1f2cSMaxime Ripard */ 485ae9f1f2cSMaxime Ripard struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm, 486ae9f1f2cSMaxime Ripard struct device_node *np, 487ae9f1f2cSMaxime Ripard u32 port, u32 endpoint) 488ae9f1f2cSMaxime Ripard { 489ae9f1f2cSMaxime Ripard struct drm_bridge *bridge; 490ae9f1f2cSMaxime Ripard struct drm_panel *panel; 491ae9f1f2cSMaxime Ripard int ret; 492ae9f1f2cSMaxime Ripard 493ae9f1f2cSMaxime Ripard ret = drm_of_find_panel_or_bridge(np, port, endpoint, 494ae9f1f2cSMaxime Ripard &panel, &bridge); 495ae9f1f2cSMaxime Ripard if (ret) 496ae9f1f2cSMaxime Ripard return ERR_PTR(ret); 497ae9f1f2cSMaxime Ripard 498ae9f1f2cSMaxime Ripard if (panel) 499ae9f1f2cSMaxime Ripard bridge = drmm_panel_bridge_add(drm, panel); 500ae9f1f2cSMaxime Ripard 501ae9f1f2cSMaxime Ripard return bridge; 502ae9f1f2cSMaxime Ripard } 503ae9f1f2cSMaxime Ripard EXPORT_SYMBOL(drmm_of_get_bridge); 504ae9f1f2cSMaxime Ripard 505d4ae66f1SMaxime Ripard #endif 506