162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com> 462306a36Sopenharmony_ci * Copyright (C) 2017 Broadcom 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 862306a36Sopenharmony_ci#include <drm/drm_bridge.h> 962306a36Sopenharmony_ci#include <drm/drm_connector.h> 1062306a36Sopenharmony_ci#include <drm/drm_encoder.h> 1162306a36Sopenharmony_ci#include <drm/drm_managed.h> 1262306a36Sopenharmony_ci#include <drm/drm_modeset_helper_vtables.h> 1362306a36Sopenharmony_ci#include <drm/drm_of.h> 1462306a36Sopenharmony_ci#include <drm/drm_panel.h> 1562306a36Sopenharmony_ci#include <drm/drm_print.h> 1662306a36Sopenharmony_ci#include <drm/drm_probe_helper.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistruct panel_bridge { 1962306a36Sopenharmony_ci struct drm_bridge bridge; 2062306a36Sopenharmony_ci struct drm_connector connector; 2162306a36Sopenharmony_ci struct drm_panel *panel; 2262306a36Sopenharmony_ci u32 connector_type; 2362306a36Sopenharmony_ci}; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic inline struct panel_bridge * 2662306a36Sopenharmony_cidrm_bridge_to_panel_bridge(struct drm_bridge *bridge) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci return container_of(bridge, struct panel_bridge, bridge); 2962306a36Sopenharmony_ci} 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic inline struct panel_bridge * 3262306a36Sopenharmony_cidrm_connector_to_panel_bridge(struct drm_connector *connector) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci return container_of(connector, struct panel_bridge, connector); 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic int panel_bridge_connector_get_modes(struct drm_connector *connector) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci struct panel_bridge *panel_bridge = 4062306a36Sopenharmony_ci drm_connector_to_panel_bridge(connector); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci return drm_panel_get_modes(panel_bridge->panel, connector); 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic const struct drm_connector_helper_funcs 4662306a36Sopenharmony_cipanel_bridge_connector_helper_funcs = { 4762306a36Sopenharmony_ci .get_modes = panel_bridge_connector_get_modes, 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic const struct drm_connector_funcs panel_bridge_connector_funcs = { 5162306a36Sopenharmony_ci .reset = drm_atomic_helper_connector_reset, 5262306a36Sopenharmony_ci .fill_modes = drm_helper_probe_single_connector_modes, 5362306a36Sopenharmony_ci .destroy = drm_connector_cleanup, 5462306a36Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 5562306a36Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic int panel_bridge_attach(struct drm_bridge *bridge, 5962306a36Sopenharmony_ci enum drm_bridge_attach_flags flags) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); 6262306a36Sopenharmony_ci struct drm_connector *connector = &panel_bridge->connector; 6362306a36Sopenharmony_ci int ret; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) 6662306a36Sopenharmony_ci return 0; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if (!bridge->encoder) { 6962306a36Sopenharmony_ci DRM_ERROR("Missing encoder\n"); 7062306a36Sopenharmony_ci return -ENODEV; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci drm_connector_helper_add(connector, 7462306a36Sopenharmony_ci &panel_bridge_connector_helper_funcs); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci ret = drm_connector_init(bridge->dev, connector, 7762306a36Sopenharmony_ci &panel_bridge_connector_funcs, 7862306a36Sopenharmony_ci panel_bridge->connector_type); 7962306a36Sopenharmony_ci if (ret) { 8062306a36Sopenharmony_ci DRM_ERROR("Failed to initialize connector\n"); 8162306a36Sopenharmony_ci return ret; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci drm_panel_bridge_set_orientation(connector, bridge); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci drm_connector_attach_encoder(&panel_bridge->connector, 8762306a36Sopenharmony_ci bridge->encoder); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (bridge->dev->registered) { 9062306a36Sopenharmony_ci if (connector->funcs->reset) 9162306a36Sopenharmony_ci connector->funcs->reset(connector); 9262306a36Sopenharmony_ci drm_connector_register(connector); 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return 0; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic void panel_bridge_detach(struct drm_bridge *bridge) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); 10162306a36Sopenharmony_ci struct drm_connector *connector = &panel_bridge->connector; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* 10462306a36Sopenharmony_ci * Cleanup the connector if we know it was initialized. 10562306a36Sopenharmony_ci * 10662306a36Sopenharmony_ci * FIXME: This wouldn't be needed if the panel_bridge structure was 10762306a36Sopenharmony_ci * allocated with drmm_kzalloc(). This might be tricky since the 10862306a36Sopenharmony_ci * drm_device pointer can only be retrieved when the bridge is attached. 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ci if (connector->dev) 11162306a36Sopenharmony_ci drm_connector_cleanup(connector); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic void panel_bridge_atomic_pre_enable(struct drm_bridge *bridge, 11562306a36Sopenharmony_ci struct drm_bridge_state *old_bridge_state) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); 11862306a36Sopenharmony_ci struct drm_atomic_state *atomic_state = old_bridge_state->base.state; 11962306a36Sopenharmony_ci struct drm_encoder *encoder = bridge->encoder; 12062306a36Sopenharmony_ci struct drm_crtc *crtc; 12162306a36Sopenharmony_ci struct drm_crtc_state *old_crtc_state; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci crtc = drm_atomic_get_new_crtc_for_encoder(atomic_state, encoder); 12462306a36Sopenharmony_ci if (!crtc) 12562306a36Sopenharmony_ci return; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci old_crtc_state = drm_atomic_get_old_crtc_state(atomic_state, crtc); 12862306a36Sopenharmony_ci if (old_crtc_state && old_crtc_state->self_refresh_active) 12962306a36Sopenharmony_ci return; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci drm_panel_prepare(panel_bridge->panel); 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic void panel_bridge_atomic_enable(struct drm_bridge *bridge, 13562306a36Sopenharmony_ci struct drm_bridge_state *old_bridge_state) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); 13862306a36Sopenharmony_ci struct drm_atomic_state *atomic_state = old_bridge_state->base.state; 13962306a36Sopenharmony_ci struct drm_encoder *encoder = bridge->encoder; 14062306a36Sopenharmony_ci struct drm_crtc *crtc; 14162306a36Sopenharmony_ci struct drm_crtc_state *old_crtc_state; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci crtc = drm_atomic_get_new_crtc_for_encoder(atomic_state, encoder); 14462306a36Sopenharmony_ci if (!crtc) 14562306a36Sopenharmony_ci return; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci old_crtc_state = drm_atomic_get_old_crtc_state(atomic_state, crtc); 14862306a36Sopenharmony_ci if (old_crtc_state && old_crtc_state->self_refresh_active) 14962306a36Sopenharmony_ci return; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci drm_panel_enable(panel_bridge->panel); 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic void panel_bridge_atomic_disable(struct drm_bridge *bridge, 15562306a36Sopenharmony_ci struct drm_bridge_state *old_bridge_state) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); 15862306a36Sopenharmony_ci struct drm_atomic_state *atomic_state = old_bridge_state->base.state; 15962306a36Sopenharmony_ci struct drm_encoder *encoder = bridge->encoder; 16062306a36Sopenharmony_ci struct drm_crtc *crtc; 16162306a36Sopenharmony_ci struct drm_crtc_state *new_crtc_state; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci crtc = drm_atomic_get_old_crtc_for_encoder(atomic_state, encoder); 16462306a36Sopenharmony_ci if (!crtc) 16562306a36Sopenharmony_ci return; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci new_crtc_state = drm_atomic_get_new_crtc_state(atomic_state, crtc); 16862306a36Sopenharmony_ci if (new_crtc_state && new_crtc_state->self_refresh_active) 16962306a36Sopenharmony_ci return; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci drm_panel_disable(panel_bridge->panel); 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic void panel_bridge_atomic_post_disable(struct drm_bridge *bridge, 17562306a36Sopenharmony_ci struct drm_bridge_state *old_bridge_state) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); 17862306a36Sopenharmony_ci struct drm_atomic_state *atomic_state = old_bridge_state->base.state; 17962306a36Sopenharmony_ci struct drm_encoder *encoder = bridge->encoder; 18062306a36Sopenharmony_ci struct drm_crtc *crtc; 18162306a36Sopenharmony_ci struct drm_crtc_state *new_crtc_state; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci crtc = drm_atomic_get_old_crtc_for_encoder(atomic_state, encoder); 18462306a36Sopenharmony_ci if (!crtc) 18562306a36Sopenharmony_ci return; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci new_crtc_state = drm_atomic_get_new_crtc_state(atomic_state, crtc); 18862306a36Sopenharmony_ci if (new_crtc_state && new_crtc_state->self_refresh_active) 18962306a36Sopenharmony_ci return; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci drm_panel_unprepare(panel_bridge->panel); 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic int panel_bridge_get_modes(struct drm_bridge *bridge, 19562306a36Sopenharmony_ci struct drm_connector *connector) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci return drm_panel_get_modes(panel_bridge->panel, connector); 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic void panel_bridge_debugfs_init(struct drm_bridge *bridge, 20362306a36Sopenharmony_ci struct dentry *root) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); 20662306a36Sopenharmony_ci struct drm_panel *panel = panel_bridge->panel; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci root = debugfs_create_dir("panel", root); 20962306a36Sopenharmony_ci if (panel->funcs->debugfs_init) 21062306a36Sopenharmony_ci panel->funcs->debugfs_init(panel, root); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic const struct drm_bridge_funcs panel_bridge_bridge_funcs = { 21462306a36Sopenharmony_ci .attach = panel_bridge_attach, 21562306a36Sopenharmony_ci .detach = panel_bridge_detach, 21662306a36Sopenharmony_ci .atomic_pre_enable = panel_bridge_atomic_pre_enable, 21762306a36Sopenharmony_ci .atomic_enable = panel_bridge_atomic_enable, 21862306a36Sopenharmony_ci .atomic_disable = panel_bridge_atomic_disable, 21962306a36Sopenharmony_ci .atomic_post_disable = panel_bridge_atomic_post_disable, 22062306a36Sopenharmony_ci .get_modes = panel_bridge_get_modes, 22162306a36Sopenharmony_ci .atomic_reset = drm_atomic_helper_bridge_reset, 22262306a36Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, 22362306a36Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, 22462306a36Sopenharmony_ci .atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt, 22562306a36Sopenharmony_ci .debugfs_init = panel_bridge_debugfs_init, 22662306a36Sopenharmony_ci}; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci/** 22962306a36Sopenharmony_ci * drm_bridge_is_panel - Checks if a drm_bridge is a panel_bridge. 23062306a36Sopenharmony_ci * 23162306a36Sopenharmony_ci * @bridge: The drm_bridge to be checked. 23262306a36Sopenharmony_ci * 23362306a36Sopenharmony_ci * Returns true if the bridge is a panel bridge, or false otherwise. 23462306a36Sopenharmony_ci */ 23562306a36Sopenharmony_cibool drm_bridge_is_panel(const struct drm_bridge *bridge) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci return bridge->funcs == &panel_bridge_bridge_funcs; 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ciEXPORT_SYMBOL(drm_bridge_is_panel); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci/** 24262306a36Sopenharmony_ci * drm_panel_bridge_add - Creates a &drm_bridge and &drm_connector that 24362306a36Sopenharmony_ci * just calls the appropriate functions from &drm_panel. 24462306a36Sopenharmony_ci * 24562306a36Sopenharmony_ci * @panel: The drm_panel being wrapped. Must be non-NULL. 24662306a36Sopenharmony_ci * 24762306a36Sopenharmony_ci * For drivers converting from directly using drm_panel: The expected 24862306a36Sopenharmony_ci * usage pattern is that during either encoder module probe or DSI 24962306a36Sopenharmony_ci * host attach, a drm_panel will be looked up through 25062306a36Sopenharmony_ci * drm_of_find_panel_or_bridge(). drm_panel_bridge_add() is used to 25162306a36Sopenharmony_ci * wrap that panel in the new bridge, and the result can then be 25262306a36Sopenharmony_ci * passed to drm_bridge_attach(). The drm_panel_prepare() and related 25362306a36Sopenharmony_ci * functions can be dropped from the encoder driver (they're now 25462306a36Sopenharmony_ci * called by the KMS helpers before calling into the encoder), along 25562306a36Sopenharmony_ci * with connector creation. When done with the bridge (after 25662306a36Sopenharmony_ci * drm_mode_config_cleanup() if the bridge has already been attached), then 25762306a36Sopenharmony_ci * drm_panel_bridge_remove() to free it. 25862306a36Sopenharmony_ci * 25962306a36Sopenharmony_ci * The connector type is set to @panel->connector_type, which must be set to a 26062306a36Sopenharmony_ci * known type. Calling this function with a panel whose connector type is 26162306a36Sopenharmony_ci * DRM_MODE_CONNECTOR_Unknown will return ERR_PTR(-EINVAL). 26262306a36Sopenharmony_ci * 26362306a36Sopenharmony_ci * See devm_drm_panel_bridge_add() for an automatically managed version of this 26462306a36Sopenharmony_ci * function. 26562306a36Sopenharmony_ci */ 26662306a36Sopenharmony_cistruct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci if (WARN_ON(panel->connector_type == DRM_MODE_CONNECTOR_Unknown)) 26962306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci return drm_panel_bridge_add_typed(panel, panel->connector_type); 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ciEXPORT_SYMBOL(drm_panel_bridge_add); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci/** 27662306a36Sopenharmony_ci * drm_panel_bridge_add_typed - Creates a &drm_bridge and &drm_connector with 27762306a36Sopenharmony_ci * an explicit connector type. 27862306a36Sopenharmony_ci * @panel: The drm_panel being wrapped. Must be non-NULL. 27962306a36Sopenharmony_ci * @connector_type: The connector type (DRM_MODE_CONNECTOR_*) 28062306a36Sopenharmony_ci * 28162306a36Sopenharmony_ci * This is just like drm_panel_bridge_add(), but forces the connector type to 28262306a36Sopenharmony_ci * @connector_type instead of infering it from the panel. 28362306a36Sopenharmony_ci * 28462306a36Sopenharmony_ci * This function is deprecated and should not be used in new drivers. Use 28562306a36Sopenharmony_ci * drm_panel_bridge_add() instead, and fix panel drivers as necessary if they 28662306a36Sopenharmony_ci * don't report a connector type. 28762306a36Sopenharmony_ci */ 28862306a36Sopenharmony_cistruct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel, 28962306a36Sopenharmony_ci u32 connector_type) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci struct panel_bridge *panel_bridge; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (!panel) 29462306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci panel_bridge = devm_kzalloc(panel->dev, sizeof(*panel_bridge), 29762306a36Sopenharmony_ci GFP_KERNEL); 29862306a36Sopenharmony_ci if (!panel_bridge) 29962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci panel_bridge->connector_type = connector_type; 30262306a36Sopenharmony_ci panel_bridge->panel = panel; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci panel_bridge->bridge.funcs = &panel_bridge_bridge_funcs; 30562306a36Sopenharmony_ci#ifdef CONFIG_OF 30662306a36Sopenharmony_ci panel_bridge->bridge.of_node = panel->dev->of_node; 30762306a36Sopenharmony_ci#endif 30862306a36Sopenharmony_ci panel_bridge->bridge.ops = DRM_BRIDGE_OP_MODES; 30962306a36Sopenharmony_ci panel_bridge->bridge.type = connector_type; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci drm_bridge_add(&panel_bridge->bridge); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci return &panel_bridge->bridge; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ciEXPORT_SYMBOL(drm_panel_bridge_add_typed); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci/** 31862306a36Sopenharmony_ci * drm_panel_bridge_remove - Unregisters and frees a drm_bridge 31962306a36Sopenharmony_ci * created by drm_panel_bridge_add(). 32062306a36Sopenharmony_ci * 32162306a36Sopenharmony_ci * @bridge: The drm_bridge being freed. 32262306a36Sopenharmony_ci */ 32362306a36Sopenharmony_civoid drm_panel_bridge_remove(struct drm_bridge *bridge) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci struct panel_bridge *panel_bridge; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (!bridge) 32862306a36Sopenharmony_ci return; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (bridge->funcs != &panel_bridge_bridge_funcs) 33162306a36Sopenharmony_ci return; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci panel_bridge = drm_bridge_to_panel_bridge(bridge); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci drm_bridge_remove(bridge); 33662306a36Sopenharmony_ci devm_kfree(panel_bridge->panel->dev, bridge); 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ciEXPORT_SYMBOL(drm_panel_bridge_remove); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci/** 34162306a36Sopenharmony_ci * drm_panel_bridge_set_orientation - Set the connector's panel orientation 34262306a36Sopenharmony_ci * from the bridge that can be transformed to panel bridge. 34362306a36Sopenharmony_ci * 34462306a36Sopenharmony_ci * @connector: The connector to be set panel orientation. 34562306a36Sopenharmony_ci * @bridge: The drm_bridge to be transformed to panel bridge. 34662306a36Sopenharmony_ci * 34762306a36Sopenharmony_ci * Returns 0 on success, negative errno on failure. 34862306a36Sopenharmony_ci */ 34962306a36Sopenharmony_ciint drm_panel_bridge_set_orientation(struct drm_connector *connector, 35062306a36Sopenharmony_ci struct drm_bridge *bridge) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci struct panel_bridge *panel_bridge; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci panel_bridge = drm_bridge_to_panel_bridge(bridge); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci return drm_connector_set_orientation_from_panel(connector, 35762306a36Sopenharmony_ci panel_bridge->panel); 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ciEXPORT_SYMBOL(drm_panel_bridge_set_orientation); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic void devm_drm_panel_bridge_release(struct device *dev, void *res) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci struct drm_bridge **bridge = res; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci drm_panel_bridge_remove(*bridge); 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci/** 36962306a36Sopenharmony_ci * devm_drm_panel_bridge_add - Creates a managed &drm_bridge and &drm_connector 37062306a36Sopenharmony_ci * that just calls the appropriate functions from &drm_panel. 37162306a36Sopenharmony_ci * @dev: device to tie the bridge lifetime to 37262306a36Sopenharmony_ci * @panel: The drm_panel being wrapped. Must be non-NULL. 37362306a36Sopenharmony_ci * 37462306a36Sopenharmony_ci * This is the managed version of drm_panel_bridge_add() which automatically 37562306a36Sopenharmony_ci * calls drm_panel_bridge_remove() when @dev is unbound. 37662306a36Sopenharmony_ci */ 37762306a36Sopenharmony_cistruct drm_bridge *devm_drm_panel_bridge_add(struct device *dev, 37862306a36Sopenharmony_ci struct drm_panel *panel) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci if (WARN_ON(panel->connector_type == DRM_MODE_CONNECTOR_Unknown)) 38162306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci return devm_drm_panel_bridge_add_typed(dev, panel, 38462306a36Sopenharmony_ci panel->connector_type); 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ciEXPORT_SYMBOL(devm_drm_panel_bridge_add); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci/** 38962306a36Sopenharmony_ci * devm_drm_panel_bridge_add_typed - Creates a managed &drm_bridge and 39062306a36Sopenharmony_ci * &drm_connector with an explicit connector type. 39162306a36Sopenharmony_ci * @dev: device to tie the bridge lifetime to 39262306a36Sopenharmony_ci * @panel: The drm_panel being wrapped. Must be non-NULL. 39362306a36Sopenharmony_ci * @connector_type: The connector type (DRM_MODE_CONNECTOR_*) 39462306a36Sopenharmony_ci * 39562306a36Sopenharmony_ci * This is just like devm_drm_panel_bridge_add(), but forces the connector type 39662306a36Sopenharmony_ci * to @connector_type instead of infering it from the panel. 39762306a36Sopenharmony_ci * 39862306a36Sopenharmony_ci * This function is deprecated and should not be used in new drivers. Use 39962306a36Sopenharmony_ci * devm_drm_panel_bridge_add() instead, and fix panel drivers as necessary if 40062306a36Sopenharmony_ci * they don't report a connector type. 40162306a36Sopenharmony_ci */ 40262306a36Sopenharmony_cistruct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev, 40362306a36Sopenharmony_ci struct drm_panel *panel, 40462306a36Sopenharmony_ci u32 connector_type) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci struct drm_bridge **ptr, *bridge; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci ptr = devres_alloc(devm_drm_panel_bridge_release, sizeof(*ptr), 40962306a36Sopenharmony_ci GFP_KERNEL); 41062306a36Sopenharmony_ci if (!ptr) 41162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci bridge = drm_panel_bridge_add_typed(panel, connector_type); 41462306a36Sopenharmony_ci if (IS_ERR(bridge)) { 41562306a36Sopenharmony_ci devres_free(ptr); 41662306a36Sopenharmony_ci return bridge; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci bridge->pre_enable_prev_first = panel->prepare_prev_first; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci *ptr = bridge; 42262306a36Sopenharmony_ci devres_add(dev, ptr); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci return bridge; 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ciEXPORT_SYMBOL(devm_drm_panel_bridge_add_typed); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_cistatic void drmm_drm_panel_bridge_release(struct drm_device *drm, void *ptr) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci struct drm_bridge *bridge = ptr; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci drm_panel_bridge_remove(bridge); 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci/** 43662306a36Sopenharmony_ci * drmm_panel_bridge_add - Creates a DRM-managed &drm_bridge and 43762306a36Sopenharmony_ci * &drm_connector that just calls the 43862306a36Sopenharmony_ci * appropriate functions from &drm_panel. 43962306a36Sopenharmony_ci * 44062306a36Sopenharmony_ci * @drm: DRM device to tie the bridge lifetime to 44162306a36Sopenharmony_ci * @panel: The drm_panel being wrapped. Must be non-NULL. 44262306a36Sopenharmony_ci * 44362306a36Sopenharmony_ci * This is the DRM-managed version of drm_panel_bridge_add() which 44462306a36Sopenharmony_ci * automatically calls drm_panel_bridge_remove() when @dev is cleaned 44562306a36Sopenharmony_ci * up. 44662306a36Sopenharmony_ci */ 44762306a36Sopenharmony_cistruct drm_bridge *drmm_panel_bridge_add(struct drm_device *drm, 44862306a36Sopenharmony_ci struct drm_panel *panel) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci struct drm_bridge *bridge; 45162306a36Sopenharmony_ci int ret; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci bridge = drm_panel_bridge_add_typed(panel, panel->connector_type); 45462306a36Sopenharmony_ci if (IS_ERR(bridge)) 45562306a36Sopenharmony_ci return bridge; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci ret = drmm_add_action_or_reset(drm, drmm_drm_panel_bridge_release, 45862306a36Sopenharmony_ci bridge); 45962306a36Sopenharmony_ci if (ret) 46062306a36Sopenharmony_ci return ERR_PTR(ret); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci bridge->pre_enable_prev_first = panel->prepare_prev_first; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci return bridge; 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ciEXPORT_SYMBOL(drmm_panel_bridge_add); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci/** 46962306a36Sopenharmony_ci * drm_panel_bridge_connector - return the connector for the panel bridge 47062306a36Sopenharmony_ci * @bridge: The drm_bridge. 47162306a36Sopenharmony_ci * 47262306a36Sopenharmony_ci * drm_panel_bridge creates the connector. 47362306a36Sopenharmony_ci * This function gives external access to the connector. 47462306a36Sopenharmony_ci * 47562306a36Sopenharmony_ci * Returns: Pointer to drm_connector 47662306a36Sopenharmony_ci */ 47762306a36Sopenharmony_cistruct drm_connector *drm_panel_bridge_connector(struct drm_bridge *bridge) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci struct panel_bridge *panel_bridge; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci panel_bridge = drm_bridge_to_panel_bridge(bridge); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci return &panel_bridge->connector; 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ciEXPORT_SYMBOL(drm_panel_bridge_connector); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci#ifdef CONFIG_OF 48862306a36Sopenharmony_ci/** 48962306a36Sopenharmony_ci * devm_drm_of_get_bridge - Return next bridge in the chain 49062306a36Sopenharmony_ci * @dev: device to tie the bridge lifetime to 49162306a36Sopenharmony_ci * @np: device tree node containing encoder output ports 49262306a36Sopenharmony_ci * @port: port in the device tree node 49362306a36Sopenharmony_ci * @endpoint: endpoint in the device tree node 49462306a36Sopenharmony_ci * 49562306a36Sopenharmony_ci * Given a DT node's port and endpoint number, finds the connected node 49662306a36Sopenharmony_ci * and returns the associated bridge if any, or creates and returns a 49762306a36Sopenharmony_ci * drm panel bridge instance if a panel is connected. 49862306a36Sopenharmony_ci * 49962306a36Sopenharmony_ci * Returns a pointer to the bridge if successful, or an error pointer 50062306a36Sopenharmony_ci * otherwise. 50162306a36Sopenharmony_ci */ 50262306a36Sopenharmony_cistruct drm_bridge *devm_drm_of_get_bridge(struct device *dev, 50362306a36Sopenharmony_ci struct device_node *np, 50462306a36Sopenharmony_ci u32 port, u32 endpoint) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci struct drm_bridge *bridge; 50762306a36Sopenharmony_ci struct drm_panel *panel; 50862306a36Sopenharmony_ci int ret; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci ret = drm_of_find_panel_or_bridge(np, port, endpoint, 51162306a36Sopenharmony_ci &panel, &bridge); 51262306a36Sopenharmony_ci if (ret) 51362306a36Sopenharmony_ci return ERR_PTR(ret); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci if (panel) 51662306a36Sopenharmony_ci bridge = devm_drm_panel_bridge_add(dev, panel); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci return bridge; 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ciEXPORT_SYMBOL(devm_drm_of_get_bridge); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci/** 52362306a36Sopenharmony_ci * drmm_of_get_bridge - Return next bridge in the chain 52462306a36Sopenharmony_ci * @drm: device to tie the bridge lifetime to 52562306a36Sopenharmony_ci * @np: device tree node containing encoder output ports 52662306a36Sopenharmony_ci * @port: port in the device tree node 52762306a36Sopenharmony_ci * @endpoint: endpoint in the device tree node 52862306a36Sopenharmony_ci * 52962306a36Sopenharmony_ci * Given a DT node's port and endpoint number, finds the connected node 53062306a36Sopenharmony_ci * and returns the associated bridge if any, or creates and returns a 53162306a36Sopenharmony_ci * drm panel bridge instance if a panel is connected. 53262306a36Sopenharmony_ci * 53362306a36Sopenharmony_ci * Returns a drmm managed pointer to the bridge if successful, or an error 53462306a36Sopenharmony_ci * pointer otherwise. 53562306a36Sopenharmony_ci */ 53662306a36Sopenharmony_cistruct drm_bridge *drmm_of_get_bridge(struct drm_device *drm, 53762306a36Sopenharmony_ci struct device_node *np, 53862306a36Sopenharmony_ci u32 port, u32 endpoint) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci struct drm_bridge *bridge; 54162306a36Sopenharmony_ci struct drm_panel *panel; 54262306a36Sopenharmony_ci int ret; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci ret = drm_of_find_panel_or_bridge(np, port, endpoint, 54562306a36Sopenharmony_ci &panel, &bridge); 54662306a36Sopenharmony_ci if (ret) 54762306a36Sopenharmony_ci return ERR_PTR(ret); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci if (panel) 55062306a36Sopenharmony_ci bridge = drmm_panel_bridge_add(drm, panel); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci return bridge; 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ciEXPORT_SYMBOL(drmm_of_get_bridge); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci#endif 557