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> 1113dfc054SEric Anholt #include <drm/drm_modeset_helper_vtables.h> 1213dfc054SEric Anholt #include <drm/drm_panel.h> 1395b60804SSam Ravnborg #include <drm/drm_print.h> 1478666baaSSabyasachi Gupta #include <drm/drm_probe_helper.h> 1513dfc054SEric Anholt 1613dfc054SEric Anholt struct panel_bridge { 1713dfc054SEric Anholt struct drm_bridge bridge; 1813dfc054SEric Anholt struct drm_connector connector; 1913dfc054SEric Anholt struct drm_panel *panel; 2013dfc054SEric Anholt u32 connector_type; 2113dfc054SEric Anholt }; 2213dfc054SEric Anholt 2313dfc054SEric Anholt static inline struct panel_bridge * 2413dfc054SEric Anholt drm_bridge_to_panel_bridge(struct drm_bridge *bridge) 2513dfc054SEric Anholt { 2613dfc054SEric Anholt return container_of(bridge, struct panel_bridge, bridge); 2713dfc054SEric Anholt } 2813dfc054SEric Anholt 2913dfc054SEric Anholt static inline struct panel_bridge * 3013dfc054SEric Anholt drm_connector_to_panel_bridge(struct drm_connector *connector) 3113dfc054SEric Anholt { 3213dfc054SEric Anholt return container_of(connector, struct panel_bridge, connector); 3313dfc054SEric Anholt } 3413dfc054SEric Anholt 3513dfc054SEric Anholt static int panel_bridge_connector_get_modes(struct drm_connector *connector) 3613dfc054SEric Anholt { 3713dfc054SEric Anholt struct panel_bridge *panel_bridge = 3813dfc054SEric Anholt drm_connector_to_panel_bridge(connector); 3913dfc054SEric Anholt 4006c4a9c2SSam Ravnborg return drm_panel_get_modes(panel_bridge->panel, connector); 4113dfc054SEric Anholt } 4213dfc054SEric Anholt 4313dfc054SEric Anholt static const struct drm_connector_helper_funcs 4413dfc054SEric Anholt panel_bridge_connector_helper_funcs = { 4513dfc054SEric Anholt .get_modes = panel_bridge_connector_get_modes, 4613dfc054SEric Anholt }; 4713dfc054SEric Anholt 4813dfc054SEric Anholt static const struct drm_connector_funcs panel_bridge_connector_funcs = { 4913dfc054SEric Anholt .reset = drm_atomic_helper_connector_reset, 5013dfc054SEric Anholt .fill_modes = drm_helper_probe_single_connector_modes, 5113dfc054SEric Anholt .destroy = drm_connector_cleanup, 5213dfc054SEric Anholt .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 5313dfc054SEric Anholt .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 5413dfc054SEric Anholt }; 5513dfc054SEric Anholt 56a25b988fSLaurent Pinchart static int panel_bridge_attach(struct drm_bridge *bridge, 57a25b988fSLaurent Pinchart enum drm_bridge_attach_flags flags) 5813dfc054SEric Anholt { 5913dfc054SEric Anholt struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); 6013dfc054SEric Anholt struct drm_connector *connector = &panel_bridge->connector; 6113dfc054SEric Anholt int ret; 6213dfc054SEric Anholt 632be68b59SLaurent Pinchart if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) 642be68b59SLaurent Pinchart return 0; 65a25b988fSLaurent Pinchart 6613dfc054SEric Anholt if (!bridge->encoder) { 6713dfc054SEric Anholt DRM_ERROR("Missing encoder\n"); 6813dfc054SEric Anholt return -ENODEV; 6913dfc054SEric Anholt } 7013dfc054SEric Anholt 7113dfc054SEric Anholt drm_connector_helper_add(connector, 7213dfc054SEric Anholt &panel_bridge_connector_helper_funcs); 7313dfc054SEric Anholt 7413dfc054SEric Anholt ret = drm_connector_init(bridge->dev, connector, 7513dfc054SEric Anholt &panel_bridge_connector_funcs, 7613dfc054SEric Anholt panel_bridge->connector_type); 7713dfc054SEric Anholt if (ret) { 7813dfc054SEric Anholt DRM_ERROR("Failed to initialize connector\n"); 7913dfc054SEric Anholt return ret; 8013dfc054SEric Anholt } 8113dfc054SEric Anholt 82cde4c44dSDaniel Vetter drm_connector_attach_encoder(&panel_bridge->connector, 8313dfc054SEric Anholt bridge->encoder); 8413dfc054SEric Anholt 8513dfc054SEric Anholt return 0; 8613dfc054SEric Anholt } 8713dfc054SEric Anholt 8813dfc054SEric Anholt static void panel_bridge_detach(struct drm_bridge *bridge) 8913dfc054SEric Anholt { 90*4d906839SPaul Cercueil struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); 91*4d906839SPaul Cercueil struct drm_connector *connector = &panel_bridge->connector; 92*4d906839SPaul Cercueil 93*4d906839SPaul Cercueil /* 94*4d906839SPaul Cercueil * Cleanup the connector if we know it was initialized. 95*4d906839SPaul Cercueil * 96*4d906839SPaul Cercueil * FIXME: This wouldn't be needed if the panel_bridge structure was 97*4d906839SPaul Cercueil * allocated with drmm_kzalloc(). This might be tricky since the 98*4d906839SPaul Cercueil * drm_device pointer can only be retrieved when the bridge is attached. 99*4d906839SPaul Cercueil */ 100*4d906839SPaul Cercueil if (connector->dev) 101*4d906839SPaul Cercueil drm_connector_cleanup(connector); 10213dfc054SEric Anholt } 10313dfc054SEric Anholt 10413dfc054SEric Anholt static void panel_bridge_pre_enable(struct drm_bridge *bridge) 10513dfc054SEric Anholt { 10613dfc054SEric Anholt struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); 10713dfc054SEric Anholt 10813dfc054SEric Anholt drm_panel_prepare(panel_bridge->panel); 10913dfc054SEric Anholt } 11013dfc054SEric Anholt 11113dfc054SEric Anholt static void panel_bridge_enable(struct drm_bridge *bridge) 11213dfc054SEric Anholt { 11313dfc054SEric Anholt struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); 11413dfc054SEric Anholt 11513dfc054SEric Anholt drm_panel_enable(panel_bridge->panel); 11613dfc054SEric Anholt } 11713dfc054SEric Anholt 11813dfc054SEric Anholt static void panel_bridge_disable(struct drm_bridge *bridge) 11913dfc054SEric Anholt { 12013dfc054SEric Anholt struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); 12113dfc054SEric Anholt 12213dfc054SEric Anholt drm_panel_disable(panel_bridge->panel); 12313dfc054SEric Anholt } 12413dfc054SEric Anholt 12513dfc054SEric Anholt static void panel_bridge_post_disable(struct drm_bridge *bridge) 12613dfc054SEric Anholt { 12713dfc054SEric Anholt struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); 12813dfc054SEric Anholt 12913dfc054SEric Anholt drm_panel_unprepare(panel_bridge->panel); 13013dfc054SEric Anholt } 13113dfc054SEric Anholt 1322be68b59SLaurent Pinchart static int panel_bridge_get_modes(struct drm_bridge *bridge, 1332be68b59SLaurent Pinchart struct drm_connector *connector) 1342be68b59SLaurent Pinchart { 1352be68b59SLaurent Pinchart struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); 1362be68b59SLaurent Pinchart 1372be68b59SLaurent Pinchart return drm_panel_get_modes(panel_bridge->panel, connector); 1382be68b59SLaurent Pinchart } 1392be68b59SLaurent Pinchart 14013dfc054SEric Anholt static const struct drm_bridge_funcs panel_bridge_bridge_funcs = { 14113dfc054SEric Anholt .attach = panel_bridge_attach, 14213dfc054SEric Anholt .detach = panel_bridge_detach, 14313dfc054SEric Anholt .pre_enable = panel_bridge_pre_enable, 14413dfc054SEric Anholt .enable = panel_bridge_enable, 14513dfc054SEric Anholt .disable = panel_bridge_disable, 14613dfc054SEric Anholt .post_disable = panel_bridge_post_disable, 1472be68b59SLaurent Pinchart .get_modes = panel_bridge_get_modes, 148cf52925aSBoris Brezillon .atomic_reset = drm_atomic_helper_bridge_reset, 149cf52925aSBoris Brezillon .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, 150cf52925aSBoris Brezillon .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, 151cf52925aSBoris Brezillon .atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt, 15213dfc054SEric Anholt }; 15313dfc054SEric Anholt 15413dfc054SEric Anholt /** 1550aa5eb3aSDaniel Vetter * drm_panel_bridge_add - Creates a &drm_bridge and &drm_connector that 1560aa5eb3aSDaniel Vetter * just calls the appropriate functions from &drm_panel. 15713dfc054SEric Anholt * 15813dfc054SEric Anholt * @panel: The drm_panel being wrapped. Must be non-NULL. 15913dfc054SEric Anholt * 16013dfc054SEric Anholt * For drivers converting from directly using drm_panel: The expected 16113dfc054SEric Anholt * usage pattern is that during either encoder module probe or DSI 16213dfc054SEric Anholt * host attach, a drm_panel will be looked up through 16313dfc054SEric Anholt * drm_of_find_panel_or_bridge(). drm_panel_bridge_add() is used to 16413dfc054SEric Anholt * wrap that panel in the new bridge, and the result can then be 16513dfc054SEric Anholt * passed to drm_bridge_attach(). The drm_panel_prepare() and related 16613dfc054SEric Anholt * functions can be dropped from the encoder driver (they're now 16713dfc054SEric Anholt * called by the KMS helpers before calling into the encoder), along 1680aa5eb3aSDaniel Vetter * with connector creation. When done with the bridge (after 1690aa5eb3aSDaniel Vetter * drm_mode_config_cleanup() if the bridge has already been attached), then 17013dfc054SEric Anholt * drm_panel_bridge_remove() to free it. 1710aa5eb3aSDaniel Vetter * 17289958b7cSLaurent Pinchart * The connector type is set to @panel->connector_type, which must be set to a 17389958b7cSLaurent Pinchart * known type. Calling this function with a panel whose connector type is 17430be3031SEnric Balletbo i Serra * DRM_MODE_CONNECTOR_Unknown will return ERR_PTR(-EINVAL). 17589958b7cSLaurent Pinchart * 176cb05ec58SEnric Balletbo i Serra * See devm_drm_panel_bridge_add() for an automatically managed version of this 1770aa5eb3aSDaniel Vetter * function. 17813dfc054SEric Anholt */ 17989958b7cSLaurent Pinchart struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel) 18089958b7cSLaurent Pinchart { 18189958b7cSLaurent Pinchart if (WARN_ON(panel->connector_type == DRM_MODE_CONNECTOR_Unknown)) 18230be3031SEnric Balletbo i Serra return ERR_PTR(-EINVAL); 18389958b7cSLaurent Pinchart 18489958b7cSLaurent Pinchart return drm_panel_bridge_add_typed(panel, panel->connector_type); 18589958b7cSLaurent Pinchart } 18689958b7cSLaurent Pinchart EXPORT_SYMBOL(drm_panel_bridge_add); 18789958b7cSLaurent Pinchart 18889958b7cSLaurent Pinchart /** 18989958b7cSLaurent Pinchart * drm_panel_bridge_add_typed - Creates a &drm_bridge and &drm_connector with 19089958b7cSLaurent Pinchart * an explicit connector type. 19189958b7cSLaurent Pinchart * @panel: The drm_panel being wrapped. Must be non-NULL. 19289958b7cSLaurent Pinchart * @connector_type: The connector type (DRM_MODE_CONNECTOR_*) 19389958b7cSLaurent Pinchart * 19489958b7cSLaurent Pinchart * This is just like drm_panel_bridge_add(), but forces the connector type to 19589958b7cSLaurent Pinchart * @connector_type instead of infering it from the panel. 19689958b7cSLaurent Pinchart * 19789958b7cSLaurent Pinchart * This function is deprecated and should not be used in new drivers. Use 19889958b7cSLaurent Pinchart * drm_panel_bridge_add() instead, and fix panel drivers as necessary if they 19989958b7cSLaurent Pinchart * don't report a connector type. 20089958b7cSLaurent Pinchart */ 20189958b7cSLaurent Pinchart struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel, 20213dfc054SEric Anholt u32 connector_type) 20313dfc054SEric Anholt { 20413dfc054SEric Anholt struct panel_bridge *panel_bridge; 20513dfc054SEric Anholt 20613dfc054SEric Anholt if (!panel) 207e6f0acb2SEric Anholt return ERR_PTR(-EINVAL); 20813dfc054SEric Anholt 20913dfc054SEric Anholt panel_bridge = devm_kzalloc(panel->dev, sizeof(*panel_bridge), 21013dfc054SEric Anholt GFP_KERNEL); 21113dfc054SEric Anholt if (!panel_bridge) 21213dfc054SEric Anholt return ERR_PTR(-ENOMEM); 21313dfc054SEric Anholt 21413dfc054SEric Anholt panel_bridge->connector_type = connector_type; 21513dfc054SEric Anholt panel_bridge->panel = panel; 21613dfc054SEric Anholt 21713dfc054SEric Anholt panel_bridge->bridge.funcs = &panel_bridge_bridge_funcs; 21813dfc054SEric Anholt #ifdef CONFIG_OF 21913dfc054SEric Anholt panel_bridge->bridge.of_node = panel->dev->of_node; 22013dfc054SEric Anholt #endif 2212be68b59SLaurent Pinchart panel_bridge->bridge.ops = DRM_BRIDGE_OP_MODES; 2222be68b59SLaurent Pinchart panel_bridge->bridge.type = connector_type; 22313dfc054SEric Anholt 2243a45d25dSInki Dae drm_bridge_add(&panel_bridge->bridge); 22513dfc054SEric Anholt 22613dfc054SEric Anholt return &panel_bridge->bridge; 22713dfc054SEric Anholt } 22889958b7cSLaurent Pinchart EXPORT_SYMBOL(drm_panel_bridge_add_typed); 22913dfc054SEric Anholt 23013dfc054SEric Anholt /** 23113dfc054SEric Anholt * drm_panel_bridge_remove - Unregisters and frees a drm_bridge 23213dfc054SEric Anholt * created by drm_panel_bridge_add(). 23313dfc054SEric Anholt * 23413dfc054SEric Anholt * @bridge: The drm_bridge being freed. 23513dfc054SEric Anholt */ 23613dfc054SEric Anholt void drm_panel_bridge_remove(struct drm_bridge *bridge) 23713dfc054SEric Anholt { 2386b0e284cSbenjamin.gaignard@linaro.org struct panel_bridge *panel_bridge; 2396b0e284cSbenjamin.gaignard@linaro.org 2406b0e284cSbenjamin.gaignard@linaro.org if (!bridge) 2416b0e284cSbenjamin.gaignard@linaro.org return; 2426b0e284cSbenjamin.gaignard@linaro.org 2436b0e284cSbenjamin.gaignard@linaro.org if (bridge->funcs != &panel_bridge_bridge_funcs) 2446b0e284cSbenjamin.gaignard@linaro.org return; 2456b0e284cSbenjamin.gaignard@linaro.org 2466b0e284cSbenjamin.gaignard@linaro.org panel_bridge = drm_bridge_to_panel_bridge(bridge); 24713dfc054SEric Anholt 24813dfc054SEric Anholt drm_bridge_remove(bridge); 24913dfc054SEric Anholt devm_kfree(panel_bridge->panel->dev, bridge); 25013dfc054SEric Anholt } 25113dfc054SEric Anholt EXPORT_SYMBOL(drm_panel_bridge_remove); 25267022227SEric Anholt 25367022227SEric Anholt static void devm_drm_panel_bridge_release(struct device *dev, void *res) 25467022227SEric Anholt { 25567022227SEric Anholt struct drm_bridge **bridge = res; 25667022227SEric Anholt 25767022227SEric Anholt drm_panel_bridge_remove(*bridge); 25867022227SEric Anholt } 25967022227SEric Anholt 2600aa5eb3aSDaniel Vetter /** 2610aa5eb3aSDaniel Vetter * devm_drm_panel_bridge_add - Creates a managed &drm_bridge and &drm_connector 2620aa5eb3aSDaniel Vetter * that just calls the appropriate functions from &drm_panel. 2630aa5eb3aSDaniel Vetter * @dev: device to tie the bridge lifetime to 2640aa5eb3aSDaniel Vetter * @panel: The drm_panel being wrapped. Must be non-NULL. 2650aa5eb3aSDaniel Vetter * 2660aa5eb3aSDaniel Vetter * This is the managed version of drm_panel_bridge_add() which automatically 2670aa5eb3aSDaniel Vetter * calls drm_panel_bridge_remove() when @dev is unbound. 2680aa5eb3aSDaniel Vetter */ 26967022227SEric Anholt struct drm_bridge *devm_drm_panel_bridge_add(struct device *dev, 27089958b7cSLaurent Pinchart struct drm_panel *panel) 27189958b7cSLaurent Pinchart { 27289958b7cSLaurent Pinchart if (WARN_ON(panel->connector_type == DRM_MODE_CONNECTOR_Unknown)) 27330be3031SEnric Balletbo i Serra return ERR_PTR(-EINVAL); 27489958b7cSLaurent Pinchart 27589958b7cSLaurent Pinchart return devm_drm_panel_bridge_add_typed(dev, panel, 27689958b7cSLaurent Pinchart panel->connector_type); 27789958b7cSLaurent Pinchart } 27889958b7cSLaurent Pinchart EXPORT_SYMBOL(devm_drm_panel_bridge_add); 27989958b7cSLaurent Pinchart 28089958b7cSLaurent Pinchart /** 28189958b7cSLaurent Pinchart * devm_drm_panel_bridge_add_typed - Creates a managed &drm_bridge and 28289958b7cSLaurent Pinchart * &drm_connector with an explicit connector type. 28389958b7cSLaurent Pinchart * @dev: device to tie the bridge lifetime to 28489958b7cSLaurent Pinchart * @panel: The drm_panel being wrapped. Must be non-NULL. 28589958b7cSLaurent Pinchart * @connector_type: The connector type (DRM_MODE_CONNECTOR_*) 28689958b7cSLaurent Pinchart * 28789958b7cSLaurent Pinchart * This is just like devm_drm_panel_bridge_add(), but forces the connector type 28889958b7cSLaurent Pinchart * to @connector_type instead of infering it from the panel. 28989958b7cSLaurent Pinchart * 29089958b7cSLaurent Pinchart * This function is deprecated and should not be used in new drivers. Use 29189958b7cSLaurent Pinchart * devm_drm_panel_bridge_add() instead, and fix panel drivers as necessary if 29289958b7cSLaurent Pinchart * they don't report a connector type. 29389958b7cSLaurent Pinchart */ 29489958b7cSLaurent Pinchart struct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev, 29567022227SEric Anholt struct drm_panel *panel, 29667022227SEric Anholt u32 connector_type) 29767022227SEric Anholt { 29867022227SEric Anholt struct drm_bridge **ptr, *bridge; 29967022227SEric Anholt 30067022227SEric Anholt ptr = devres_alloc(devm_drm_panel_bridge_release, sizeof(*ptr), 30167022227SEric Anholt GFP_KERNEL); 30267022227SEric Anholt if (!ptr) 30367022227SEric Anholt return ERR_PTR(-ENOMEM); 30467022227SEric Anholt 30589958b7cSLaurent Pinchart bridge = drm_panel_bridge_add_typed(panel, connector_type); 30667022227SEric Anholt if (!IS_ERR(bridge)) { 30767022227SEric Anholt *ptr = bridge; 30867022227SEric Anholt devres_add(dev, ptr); 30967022227SEric Anholt } else { 31067022227SEric Anholt devres_free(ptr); 31167022227SEric Anholt } 31267022227SEric Anholt 31367022227SEric Anholt return bridge; 31467022227SEric Anholt } 31589958b7cSLaurent Pinchart EXPORT_SYMBOL(devm_drm_panel_bridge_add_typed); 316d383fb5fSSam Ravnborg 317d383fb5fSSam Ravnborg /** 318d383fb5fSSam Ravnborg * drm_panel_bridge_connector - return the connector for the panel bridge 31991fcf8e6SSam Ravnborg * @bridge: The drm_bridge. 320d383fb5fSSam Ravnborg * 321d383fb5fSSam Ravnborg * drm_panel_bridge creates the connector. 322d383fb5fSSam Ravnborg * This function gives external access to the connector. 323d383fb5fSSam Ravnborg * 324d383fb5fSSam Ravnborg * Returns: Pointer to drm_connector 325d383fb5fSSam Ravnborg */ 326d383fb5fSSam Ravnborg struct drm_connector *drm_panel_bridge_connector(struct drm_bridge *bridge) 327d383fb5fSSam Ravnborg { 328d383fb5fSSam Ravnborg struct panel_bridge *panel_bridge; 329d383fb5fSSam Ravnborg 330d383fb5fSSam Ravnborg panel_bridge = drm_bridge_to_panel_bridge(bridge); 331d383fb5fSSam Ravnborg 332d383fb5fSSam Ravnborg return &panel_bridge->connector; 333d383fb5fSSam Ravnborg } 3343cc1430cSMihail Atanassov EXPORT_SYMBOL(drm_panel_bridge_connector); 335