113dfc054SEric Anholt /* 213dfc054SEric Anholt * Copyright (C) 2016 Laurent Pinchart <laurent.pinchart@ideasonboard.com> 313dfc054SEric Anholt * Copyright (C) 2017 Broadcom 413dfc054SEric Anholt * 513dfc054SEric Anholt * This program is free software; you can redistribute it and/or 613dfc054SEric Anholt * modify it under the terms of the GNU General Public License as 713dfc054SEric Anholt * published by the Free Software Foundation; either version 2 of 813dfc054SEric Anholt * the License, or (at your option) any later version. 913dfc054SEric Anholt */ 1013dfc054SEric Anholt 1113dfc054SEric Anholt #include <drm/drmP.h> 1213dfc054SEric Anholt #include <drm/drm_panel.h> 1313dfc054SEric Anholt #include <drm/drm_atomic_helper.h> 1413dfc054SEric Anholt #include <drm/drm_connector.h> 1513dfc054SEric Anholt #include <drm/drm_crtc_helper.h> 1613dfc054SEric Anholt #include <drm/drm_encoder.h> 1713dfc054SEric Anholt #include <drm/drm_modeset_helper_vtables.h> 1813dfc054SEric Anholt #include <drm/drm_panel.h> 1913dfc054SEric Anholt 2013dfc054SEric Anholt struct panel_bridge { 2113dfc054SEric Anholt struct drm_bridge bridge; 2213dfc054SEric Anholt struct drm_connector connector; 2313dfc054SEric Anholt struct drm_panel *panel; 2413dfc054SEric Anholt u32 connector_type; 2513dfc054SEric Anholt }; 2613dfc054SEric Anholt 2713dfc054SEric Anholt static inline struct panel_bridge * 2813dfc054SEric Anholt drm_bridge_to_panel_bridge(struct drm_bridge *bridge) 2913dfc054SEric Anholt { 3013dfc054SEric Anholt return container_of(bridge, struct panel_bridge, bridge); 3113dfc054SEric Anholt } 3213dfc054SEric Anholt 3313dfc054SEric Anholt static inline struct panel_bridge * 3413dfc054SEric Anholt drm_connector_to_panel_bridge(struct drm_connector *connector) 3513dfc054SEric Anholt { 3613dfc054SEric Anholt return container_of(connector, struct panel_bridge, connector); 3713dfc054SEric Anholt } 3813dfc054SEric Anholt 3913dfc054SEric Anholt static int panel_bridge_connector_get_modes(struct drm_connector *connector) 4013dfc054SEric Anholt { 4113dfc054SEric Anholt struct panel_bridge *panel_bridge = 4213dfc054SEric Anholt drm_connector_to_panel_bridge(connector); 4313dfc054SEric Anholt 4413dfc054SEric Anholt return drm_panel_get_modes(panel_bridge->panel); 4513dfc054SEric Anholt } 4613dfc054SEric Anholt 4713dfc054SEric Anholt static const struct drm_connector_helper_funcs 4813dfc054SEric Anholt panel_bridge_connector_helper_funcs = { 4913dfc054SEric Anholt .get_modes = panel_bridge_connector_get_modes, 5013dfc054SEric Anholt }; 5113dfc054SEric Anholt 5213dfc054SEric Anholt static const struct drm_connector_funcs panel_bridge_connector_funcs = { 5313dfc054SEric Anholt .reset = drm_atomic_helper_connector_reset, 5413dfc054SEric Anholt .fill_modes = drm_helper_probe_single_connector_modes, 5513dfc054SEric Anholt .destroy = drm_connector_cleanup, 5613dfc054SEric Anholt .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 5713dfc054SEric Anholt .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 5813dfc054SEric Anholt }; 5913dfc054SEric Anholt 6013dfc054SEric Anholt static int panel_bridge_attach(struct drm_bridge *bridge) 6113dfc054SEric Anholt { 6213dfc054SEric Anholt struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); 6313dfc054SEric Anholt struct drm_connector *connector = &panel_bridge->connector; 6413dfc054SEric Anholt int ret; 6513dfc054SEric Anholt 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 ret = drm_panel_attach(panel_bridge->panel, &panel_bridge->connector); 8613dfc054SEric Anholt if (ret < 0) 8713dfc054SEric Anholt return ret; 8813dfc054SEric Anholt 8913dfc054SEric Anholt return 0; 9013dfc054SEric Anholt } 9113dfc054SEric Anholt 9213dfc054SEric Anholt static void panel_bridge_detach(struct drm_bridge *bridge) 9313dfc054SEric Anholt { 9413dfc054SEric Anholt struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); 9513dfc054SEric Anholt 9613dfc054SEric Anholt drm_panel_detach(panel_bridge->panel); 9713dfc054SEric Anholt } 9813dfc054SEric Anholt 9913dfc054SEric Anholt static void panel_bridge_pre_enable(struct drm_bridge *bridge) 10013dfc054SEric Anholt { 10113dfc054SEric Anholt struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); 10213dfc054SEric Anholt 10313dfc054SEric Anholt drm_panel_prepare(panel_bridge->panel); 10413dfc054SEric Anholt } 10513dfc054SEric Anholt 10613dfc054SEric Anholt static void panel_bridge_enable(struct drm_bridge *bridge) 10713dfc054SEric Anholt { 10813dfc054SEric Anholt struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); 10913dfc054SEric Anholt 11013dfc054SEric Anholt drm_panel_enable(panel_bridge->panel); 11113dfc054SEric Anholt } 11213dfc054SEric Anholt 11313dfc054SEric Anholt static void panel_bridge_disable(struct drm_bridge *bridge) 11413dfc054SEric Anholt { 11513dfc054SEric Anholt struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); 11613dfc054SEric Anholt 11713dfc054SEric Anholt drm_panel_disable(panel_bridge->panel); 11813dfc054SEric Anholt } 11913dfc054SEric Anholt 12013dfc054SEric Anholt static void panel_bridge_post_disable(struct drm_bridge *bridge) 12113dfc054SEric Anholt { 12213dfc054SEric Anholt struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); 12313dfc054SEric Anholt 12413dfc054SEric Anholt drm_panel_unprepare(panel_bridge->panel); 12513dfc054SEric Anholt } 12613dfc054SEric Anholt 12713dfc054SEric Anholt static const struct drm_bridge_funcs panel_bridge_bridge_funcs = { 12813dfc054SEric Anholt .attach = panel_bridge_attach, 12913dfc054SEric Anholt .detach = panel_bridge_detach, 13013dfc054SEric Anholt .pre_enable = panel_bridge_pre_enable, 13113dfc054SEric Anholt .enable = panel_bridge_enable, 13213dfc054SEric Anholt .disable = panel_bridge_disable, 13313dfc054SEric Anholt .post_disable = panel_bridge_post_disable, 13413dfc054SEric Anholt }; 13513dfc054SEric Anholt 13613dfc054SEric Anholt /** 1370aa5eb3aSDaniel Vetter * drm_panel_bridge_add - Creates a &drm_bridge and &drm_connector that 1380aa5eb3aSDaniel Vetter * just calls the appropriate functions from &drm_panel. 13913dfc054SEric Anholt * 14013dfc054SEric Anholt * @panel: The drm_panel being wrapped. Must be non-NULL. 14113dfc054SEric Anholt * @connector_type: The DRM_MODE_CONNECTOR_* for the connector to be 14213dfc054SEric Anholt * created. 14313dfc054SEric Anholt * 14413dfc054SEric Anholt * For drivers converting from directly using drm_panel: The expected 14513dfc054SEric Anholt * usage pattern is that during either encoder module probe or DSI 14613dfc054SEric Anholt * host attach, a drm_panel will be looked up through 14713dfc054SEric Anholt * drm_of_find_panel_or_bridge(). drm_panel_bridge_add() is used to 14813dfc054SEric Anholt * wrap that panel in the new bridge, and the result can then be 14913dfc054SEric Anholt * passed to drm_bridge_attach(). The drm_panel_prepare() and related 15013dfc054SEric Anholt * functions can be dropped from the encoder driver (they're now 15113dfc054SEric Anholt * called by the KMS helpers before calling into the encoder), along 1520aa5eb3aSDaniel Vetter * with connector creation. When done with the bridge (after 1530aa5eb3aSDaniel Vetter * drm_mode_config_cleanup() if the bridge has already been attached), then 15413dfc054SEric Anholt * drm_panel_bridge_remove() to free it. 1550aa5eb3aSDaniel Vetter * 1560aa5eb3aSDaniel Vetter * See devm_drm_panel_bridge_add() for an automatically manged version of this 1570aa5eb3aSDaniel Vetter * function. 15813dfc054SEric Anholt */ 15913dfc054SEric Anholt struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel, 16013dfc054SEric Anholt u32 connector_type) 16113dfc054SEric Anholt { 16213dfc054SEric Anholt struct panel_bridge *panel_bridge; 16313dfc054SEric Anholt 16413dfc054SEric Anholt if (!panel) 165e6f0acb2SEric Anholt return ERR_PTR(-EINVAL); 16613dfc054SEric Anholt 16713dfc054SEric Anholt panel_bridge = devm_kzalloc(panel->dev, sizeof(*panel_bridge), 16813dfc054SEric Anholt GFP_KERNEL); 16913dfc054SEric Anholt if (!panel_bridge) 17013dfc054SEric Anholt return ERR_PTR(-ENOMEM); 17113dfc054SEric Anholt 17213dfc054SEric Anholt panel_bridge->connector_type = connector_type; 17313dfc054SEric Anholt panel_bridge->panel = panel; 17413dfc054SEric Anholt 17513dfc054SEric Anholt panel_bridge->bridge.funcs = &panel_bridge_bridge_funcs; 17613dfc054SEric Anholt #ifdef CONFIG_OF 17713dfc054SEric Anholt panel_bridge->bridge.of_node = panel->dev->of_node; 17813dfc054SEric Anholt #endif 17913dfc054SEric Anholt 1803a45d25dSInki Dae drm_bridge_add(&panel_bridge->bridge); 18113dfc054SEric Anholt 18213dfc054SEric Anholt return &panel_bridge->bridge; 18313dfc054SEric Anholt } 18413dfc054SEric Anholt EXPORT_SYMBOL(drm_panel_bridge_add); 18513dfc054SEric Anholt 18613dfc054SEric Anholt /** 18713dfc054SEric Anholt * drm_panel_bridge_remove - Unregisters and frees a drm_bridge 18813dfc054SEric Anholt * created by drm_panel_bridge_add(). 18913dfc054SEric Anholt * 19013dfc054SEric Anholt * @bridge: The drm_bridge being freed. 19113dfc054SEric Anholt */ 19213dfc054SEric Anholt void drm_panel_bridge_remove(struct drm_bridge *bridge) 19313dfc054SEric Anholt { 1946b0e284cSbenjamin.gaignard@linaro.org struct panel_bridge *panel_bridge; 1956b0e284cSbenjamin.gaignard@linaro.org 1966b0e284cSbenjamin.gaignard@linaro.org if (!bridge) 1976b0e284cSbenjamin.gaignard@linaro.org return; 1986b0e284cSbenjamin.gaignard@linaro.org 1996b0e284cSbenjamin.gaignard@linaro.org if (bridge->funcs != &panel_bridge_bridge_funcs) 2006b0e284cSbenjamin.gaignard@linaro.org return; 2016b0e284cSbenjamin.gaignard@linaro.org 2026b0e284cSbenjamin.gaignard@linaro.org panel_bridge = drm_bridge_to_panel_bridge(bridge); 20313dfc054SEric Anholt 20413dfc054SEric Anholt drm_bridge_remove(bridge); 20513dfc054SEric Anholt devm_kfree(panel_bridge->panel->dev, bridge); 20613dfc054SEric Anholt } 20713dfc054SEric Anholt EXPORT_SYMBOL(drm_panel_bridge_remove); 20867022227SEric Anholt 20967022227SEric Anholt static void devm_drm_panel_bridge_release(struct device *dev, void *res) 21067022227SEric Anholt { 21167022227SEric Anholt struct drm_bridge **bridge = res; 21267022227SEric Anholt 21367022227SEric Anholt drm_panel_bridge_remove(*bridge); 21467022227SEric Anholt } 21567022227SEric Anholt 2160aa5eb3aSDaniel Vetter /** 2170aa5eb3aSDaniel Vetter * devm_drm_panel_bridge_add - Creates a managed &drm_bridge and &drm_connector 2180aa5eb3aSDaniel Vetter * that just calls the appropriate functions from &drm_panel. 2190aa5eb3aSDaniel Vetter * @dev: device to tie the bridge lifetime to 2200aa5eb3aSDaniel Vetter * @panel: The drm_panel being wrapped. Must be non-NULL. 2210aa5eb3aSDaniel Vetter * @connector_type: The DRM_MODE_CONNECTOR_* for the connector to be 2220aa5eb3aSDaniel Vetter * created. 2230aa5eb3aSDaniel Vetter * 2240aa5eb3aSDaniel Vetter * This is the managed version of drm_panel_bridge_add() which automatically 2250aa5eb3aSDaniel Vetter * calls drm_panel_bridge_remove() when @dev is unbound. 2260aa5eb3aSDaniel Vetter */ 22767022227SEric Anholt struct drm_bridge *devm_drm_panel_bridge_add(struct device *dev, 22867022227SEric Anholt struct drm_panel *panel, 22967022227SEric Anholt u32 connector_type) 23067022227SEric Anholt { 23167022227SEric Anholt struct drm_bridge **ptr, *bridge; 23267022227SEric Anholt 23367022227SEric Anholt ptr = devres_alloc(devm_drm_panel_bridge_release, sizeof(*ptr), 23467022227SEric Anholt GFP_KERNEL); 23567022227SEric Anholt if (!ptr) 23667022227SEric Anholt return ERR_PTR(-ENOMEM); 23767022227SEric Anholt 23867022227SEric Anholt bridge = drm_panel_bridge_add(panel, connector_type); 23967022227SEric Anholt if (!IS_ERR(bridge)) { 24067022227SEric Anholt *ptr = bridge; 24167022227SEric Anholt devres_add(dev, ptr); 24267022227SEric Anholt } else { 24367022227SEric Anholt devres_free(ptr); 24467022227SEric Anholt } 24567022227SEric Anholt 24667022227SEric Anholt return bridge; 24767022227SEric Anholt } 24867022227SEric Anholt EXPORT_SYMBOL(devm_drm_panel_bridge_add); 249