15718399fSFrançois Tigeot /* 25718399fSFrançois Tigeot * Copyright (c) 2006-2008 Intel Corporation 35718399fSFrançois Tigeot * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> 45718399fSFrançois Tigeot * 55718399fSFrançois Tigeot * DRM core CRTC related functions 65718399fSFrançois Tigeot * 75718399fSFrançois Tigeot * Permission to use, copy, modify, distribute, and sell this software and its 85718399fSFrançois Tigeot * documentation for any purpose is hereby granted without fee, provided that 95718399fSFrançois Tigeot * the above copyright notice appear in all copies and that both that copyright 105718399fSFrançois Tigeot * notice and this permission notice appear in supporting documentation, and 115718399fSFrançois Tigeot * that the name of the copyright holders not be used in advertising or 125718399fSFrançois Tigeot * publicity pertaining to distribution of the software without specific, 135718399fSFrançois Tigeot * written prior permission. The copyright holders make no representations 145718399fSFrançois Tigeot * about the suitability of this software for any purpose. It is provided "as 155718399fSFrançois Tigeot * is" without express or implied warranty. 165718399fSFrançois Tigeot * 175718399fSFrançois Tigeot * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 185718399fSFrançois Tigeot * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 195718399fSFrançois Tigeot * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 205718399fSFrançois Tigeot * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 215718399fSFrançois Tigeot * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 225718399fSFrançois Tigeot * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 235718399fSFrançois Tigeot * OF THIS SOFTWARE. 245718399fSFrançois Tigeot * 255718399fSFrançois Tigeot * Authors: 265718399fSFrançois Tigeot * Keith Packard 275718399fSFrançois Tigeot * Eric Anholt <eric@anholt.net> 285718399fSFrançois Tigeot * Dave Airlie <airlied@linux.ie> 295718399fSFrançois Tigeot * Jesse Barnes <jesse.barnes@intel.com> 305718399fSFrançois Tigeot */ 315718399fSFrançois Tigeot 326e29dde8SFrançois Tigeot #include <linux/export.h> 334dbb207bSFrançois Tigeot #include <linux/moduleparam.h> 346e29dde8SFrançois Tigeot 3518e26a6dSFrançois Tigeot #include <drm/drmP.h> 3618e26a6dSFrançois Tigeot #include <drm/drm_crtc.h> 3718e26a6dSFrançois Tigeot #include <drm/drm_crtc_helper.h> 3818e26a6dSFrançois Tigeot #include <drm/drm_fb_helper.h> 396e29dde8SFrançois Tigeot #include <drm/drm_edid.h> 405718399fSFrançois Tigeot 419edbd4a0SFrançois Tigeot MODULE_AUTHOR("David Airlie, Jesse Barnes"); 429edbd4a0SFrançois Tigeot MODULE_DESCRIPTION("DRM KMS helper"); 43ba55f2f5SFrançois Tigeot MODULE_LICENSE("GPL and additional rights"); 449edbd4a0SFrançois Tigeot 456e29dde8SFrançois Tigeot /** 466e29dde8SFrançois Tigeot * drm_helper_move_panel_connectors_to_head() - move panels to the front in the 476e29dde8SFrançois Tigeot * connector list 486e29dde8SFrançois Tigeot * @dev: drm device to operate on 496e29dde8SFrançois Tigeot * 506e29dde8SFrançois Tigeot * Some userspace presumes that the first connected connector is the main 516e29dde8SFrançois Tigeot * display, where it's supposed to display e.g. the login screen. For 526e29dde8SFrançois Tigeot * laptops, this should be the main panel. Use this function to sort all 536e29dde8SFrançois Tigeot * (eDP/LVDS) panels to the front of the connector list, instead of 546e29dde8SFrançois Tigeot * painstakingly trying to initialize them in the right order. 556e29dde8SFrançois Tigeot */ 566e29dde8SFrançois Tigeot void drm_helper_move_panel_connectors_to_head(struct drm_device *dev) 576e29dde8SFrançois Tigeot { 586e29dde8SFrançois Tigeot struct drm_connector *connector, *tmp; 596e29dde8SFrançois Tigeot struct list_head panel_list; 606e29dde8SFrançois Tigeot 616e29dde8SFrançois Tigeot INIT_LIST_HEAD(&panel_list); 626e29dde8SFrançois Tigeot 636e29dde8SFrançois Tigeot list_for_each_entry_safe(connector, tmp, 646e29dde8SFrançois Tigeot &dev->mode_config.connector_list, head) { 656e29dde8SFrançois Tigeot if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS || 666e29dde8SFrançois Tigeot connector->connector_type == DRM_MODE_CONNECTOR_eDP) 676e29dde8SFrançois Tigeot list_move_tail(&connector->head, &panel_list); 686e29dde8SFrançois Tigeot } 696e29dde8SFrançois Tigeot 706e29dde8SFrançois Tigeot list_splice(&panel_list, &dev->mode_config.connector_list); 716e29dde8SFrançois Tigeot } 726e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_helper_move_panel_connectors_to_head); 736e29dde8SFrançois Tigeot 745718399fSFrançois Tigeot /** 755718399fSFrançois Tigeot * drm_helper_encoder_in_use - check if a given encoder is in use 765718399fSFrançois Tigeot * @encoder: encoder to check 775718399fSFrançois Tigeot * 78ba55f2f5SFrançois Tigeot * Checks whether @encoder is with the current mode setting output configuration 79ba55f2f5SFrançois Tigeot * in use by any connector. This doesn't mean that it is actually enabled since 80ba55f2f5SFrançois Tigeot * the DPMS state is tracked separately. 815718399fSFrançois Tigeot * 82ba55f2f5SFrançois Tigeot * Returns: 83ba55f2f5SFrançois Tigeot * True if @encoder is used, false otherwise. 845718399fSFrançois Tigeot */ 855718399fSFrançois Tigeot bool drm_helper_encoder_in_use(struct drm_encoder *encoder) 865718399fSFrançois Tigeot { 875718399fSFrançois Tigeot struct drm_connector *connector; 885718399fSFrançois Tigeot struct drm_device *dev = encoder->dev; 89ba55f2f5SFrançois Tigeot 90ba55f2f5SFrançois Tigeot WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); 91ba55f2f5SFrançois Tigeot WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); 925718399fSFrançois Tigeot list_for_each_entry(connector, &dev->mode_config.connector_list, head) 935718399fSFrançois Tigeot if (connector->encoder == encoder) 945718399fSFrançois Tigeot return true; 955718399fSFrançois Tigeot return false; 965718399fSFrançois Tigeot } 976e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_helper_encoder_in_use); 985718399fSFrançois Tigeot 995718399fSFrançois Tigeot /** 1005718399fSFrançois Tigeot * drm_helper_crtc_in_use - check if a given CRTC is in a mode_config 1015718399fSFrançois Tigeot * @crtc: CRTC to check 1025718399fSFrançois Tigeot * 103ba55f2f5SFrançois Tigeot * Checks whether @crtc is with the current mode setting output configuration 104ba55f2f5SFrançois Tigeot * in use by any connector. This doesn't mean that it is actually enabled since 105ba55f2f5SFrançois Tigeot * the DPMS state is tracked separately. 1065718399fSFrançois Tigeot * 107ba55f2f5SFrançois Tigeot * Returns: 108ba55f2f5SFrançois Tigeot * True if @crtc is used, false otherwise. 1095718399fSFrançois Tigeot */ 1105718399fSFrançois Tigeot bool drm_helper_crtc_in_use(struct drm_crtc *crtc) 1115718399fSFrançois Tigeot { 1125718399fSFrançois Tigeot struct drm_encoder *encoder; 1135718399fSFrançois Tigeot struct drm_device *dev = crtc->dev; 114ba55f2f5SFrançois Tigeot 115ba55f2f5SFrançois Tigeot WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); 1165718399fSFrançois Tigeot list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) 1175718399fSFrançois Tigeot if (encoder->crtc == crtc && drm_helper_encoder_in_use(encoder)) 1185718399fSFrançois Tigeot return true; 1195718399fSFrançois Tigeot return false; 1205718399fSFrançois Tigeot } 1216e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_helper_crtc_in_use); 1225718399fSFrançois Tigeot 1235718399fSFrançois Tigeot static void 1245718399fSFrançois Tigeot drm_encoder_disable(struct drm_encoder *encoder) 1255718399fSFrançois Tigeot { 1265718399fSFrançois Tigeot struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; 1275718399fSFrançois Tigeot 128ba55f2f5SFrançois Tigeot if (encoder->bridge) 129ba55f2f5SFrançois Tigeot encoder->bridge->funcs->disable(encoder->bridge); 130ba55f2f5SFrançois Tigeot 1315718399fSFrançois Tigeot if (encoder_funcs->disable) 1325718399fSFrançois Tigeot (*encoder_funcs->disable)(encoder); 1335718399fSFrançois Tigeot else 1345718399fSFrançois Tigeot (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); 135ba55f2f5SFrançois Tigeot 136ba55f2f5SFrançois Tigeot if (encoder->bridge) 137ba55f2f5SFrançois Tigeot encoder->bridge->funcs->post_disable(encoder->bridge); 1385718399fSFrançois Tigeot } 1395718399fSFrançois Tigeot 140ba55f2f5SFrançois Tigeot static void __drm_helper_disable_unused_functions(struct drm_device *dev) 1415718399fSFrançois Tigeot { 1425718399fSFrançois Tigeot struct drm_encoder *encoder; 1435718399fSFrançois Tigeot struct drm_crtc *crtc; 1445718399fSFrançois Tigeot 145ba55f2f5SFrançois Tigeot drm_warn_on_modeset_not_all_locked(dev); 1465718399fSFrançois Tigeot 1475718399fSFrançois Tigeot list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 1485718399fSFrançois Tigeot if (!drm_helper_encoder_in_use(encoder)) { 1495718399fSFrançois Tigeot drm_encoder_disable(encoder); 150ba55f2f5SFrançois Tigeot /* disconnect encoder from any connector */ 1515718399fSFrançois Tigeot encoder->crtc = NULL; 1525718399fSFrançois Tigeot } 1535718399fSFrançois Tigeot } 1545718399fSFrançois Tigeot 1555718399fSFrançois Tigeot list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 1565718399fSFrançois Tigeot struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; 1575718399fSFrançois Tigeot crtc->enabled = drm_helper_crtc_in_use(crtc); 1585718399fSFrançois Tigeot if (!crtc->enabled) { 1595718399fSFrançois Tigeot if (crtc_funcs->disable) 1605718399fSFrançois Tigeot (*crtc_funcs->disable)(crtc); 1615718399fSFrançois Tigeot else 1625718399fSFrançois Tigeot (*crtc_funcs->dpms)(crtc, DRM_MODE_DPMS_OFF); 163ba55f2f5SFrançois Tigeot crtc->primary->fb = NULL; 1645718399fSFrançois Tigeot } 1655718399fSFrançois Tigeot } 1665718399fSFrançois Tigeot } 167ba55f2f5SFrançois Tigeot 168ba55f2f5SFrançois Tigeot /** 169ba55f2f5SFrançois Tigeot * drm_helper_disable_unused_functions - disable unused objects 170ba55f2f5SFrançois Tigeot * @dev: DRM device 171ba55f2f5SFrançois Tigeot * 172ba55f2f5SFrançois Tigeot * This function walks through the entire mode setting configuration of @dev. It 173ba55f2f5SFrançois Tigeot * will remove any crtc links of unused encoders and encoder links of 174ba55f2f5SFrançois Tigeot * disconnected connectors. Then it will disable all unused encoders and crtcs 175ba55f2f5SFrançois Tigeot * either by calling their disable callback if available or by calling their 176ba55f2f5SFrançois Tigeot * dpms callback with DRM_MODE_DPMS_OFF. 177ba55f2f5SFrançois Tigeot */ 178ba55f2f5SFrançois Tigeot void drm_helper_disable_unused_functions(struct drm_device *dev) 179ba55f2f5SFrançois Tigeot { 180ba55f2f5SFrançois Tigeot drm_modeset_lock_all(dev); 181ba55f2f5SFrançois Tigeot __drm_helper_disable_unused_functions(dev); 182ba55f2f5SFrançois Tigeot drm_modeset_unlock_all(dev); 183ba55f2f5SFrançois Tigeot } 1846e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_helper_disable_unused_functions); 1855718399fSFrançois Tigeot 1865718399fSFrançois Tigeot /* 1875718399fSFrançois Tigeot * Check the CRTC we're going to map each output to vs. its current 1885718399fSFrançois Tigeot * CRTC. If they don't match, we have to disable the output and the CRTC 1895718399fSFrançois Tigeot * since the driver will have to re-route things. 1905718399fSFrançois Tigeot */ 1915718399fSFrançois Tigeot static void 1925718399fSFrançois Tigeot drm_crtc_prepare_encoders(struct drm_device *dev) 1935718399fSFrançois Tigeot { 1945718399fSFrançois Tigeot struct drm_encoder_helper_funcs *encoder_funcs; 1955718399fSFrançois Tigeot struct drm_encoder *encoder; 1965718399fSFrançois Tigeot 1975718399fSFrançois Tigeot list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 1985718399fSFrançois Tigeot encoder_funcs = encoder->helper_private; 1995718399fSFrançois Tigeot /* Disable unused encoders */ 2005718399fSFrançois Tigeot if (encoder->crtc == NULL) 2015718399fSFrançois Tigeot drm_encoder_disable(encoder); 2025718399fSFrançois Tigeot /* Disable encoders whose CRTC is about to change */ 2035718399fSFrançois Tigeot if (encoder_funcs->get_crtc && 2045718399fSFrançois Tigeot encoder->crtc != (*encoder_funcs->get_crtc)(encoder)) 2055718399fSFrançois Tigeot drm_encoder_disable(encoder); 2065718399fSFrançois Tigeot } 2075718399fSFrançois Tigeot } 2085718399fSFrançois Tigeot 2095718399fSFrançois Tigeot /** 2106e29dde8SFrançois Tigeot * drm_crtc_helper_set_mode - internal helper to set a mode 2115718399fSFrançois Tigeot * @crtc: CRTC to program 2125718399fSFrançois Tigeot * @mode: mode to use 2136e29dde8SFrançois Tigeot * @x: horizontal offset into the surface 2146e29dde8SFrançois Tigeot * @y: vertical offset into the surface 2156e29dde8SFrançois Tigeot * @old_fb: old framebuffer, for cleanup 2165718399fSFrançois Tigeot * 2175718399fSFrançois Tigeot * Try to set @mode on @crtc. Give @crtc and its associated connectors a chance 2186e29dde8SFrançois Tigeot * to fixup or reject the mode prior to trying to set it. This is an internal 2196e29dde8SFrançois Tigeot * helper that drivers could e.g. use to update properties that require the 2206e29dde8SFrançois Tigeot * entire output pipe to be disabled and re-enabled in a new configuration. For 2216e29dde8SFrançois Tigeot * example for changing whether audio is enabled on a hdmi link or for changing 2226e29dde8SFrançois Tigeot * panel fitter or dither attributes. It is also called by the 2236e29dde8SFrançois Tigeot * drm_crtc_helper_set_config() helper function to drive the mode setting 2246e29dde8SFrançois Tigeot * sequence. 2255718399fSFrançois Tigeot * 226ba55f2f5SFrançois Tigeot * Returns: 227ba55f2f5SFrançois Tigeot * True if the mode was set successfully, false otherwise. 2285718399fSFrançois Tigeot */ 2295718399fSFrançois Tigeot bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, 2305718399fSFrançois Tigeot struct drm_display_mode *mode, 2315718399fSFrançois Tigeot int x, int y, 2325718399fSFrançois Tigeot struct drm_framebuffer *old_fb) 2335718399fSFrançois Tigeot { 2345718399fSFrançois Tigeot struct drm_device *dev = crtc->dev; 2359edbd4a0SFrançois Tigeot struct drm_display_mode *adjusted_mode, saved_mode; 2365718399fSFrançois Tigeot struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; 2375718399fSFrançois Tigeot struct drm_encoder_helper_funcs *encoder_funcs; 2385718399fSFrançois Tigeot int saved_x, saved_y; 2399edbd4a0SFrançois Tigeot bool saved_enabled; 2405718399fSFrançois Tigeot struct drm_encoder *encoder; 2415718399fSFrançois Tigeot bool ret = true; 2425718399fSFrançois Tigeot 243ba55f2f5SFrançois Tigeot drm_warn_on_modeset_not_all_locked(dev); 244ba55f2f5SFrançois Tigeot 2459edbd4a0SFrançois Tigeot saved_enabled = crtc->enabled; 2465718399fSFrançois Tigeot crtc->enabled = drm_helper_crtc_in_use(crtc); 2475718399fSFrançois Tigeot if (!crtc->enabled) 2485718399fSFrançois Tigeot return true; 2495718399fSFrançois Tigeot 2505718399fSFrançois Tigeot adjusted_mode = drm_mode_duplicate(dev, mode); 2519edbd4a0SFrançois Tigeot if (!adjusted_mode) { 2529edbd4a0SFrançois Tigeot crtc->enabled = saved_enabled; 2535718399fSFrançois Tigeot return false; 2549edbd4a0SFrançois Tigeot } 2555718399fSFrançois Tigeot 2565718399fSFrançois Tigeot saved_mode = crtc->mode; 2575718399fSFrançois Tigeot saved_x = crtc->x; 2585718399fSFrançois Tigeot saved_y = crtc->y; 2595718399fSFrançois Tigeot 2605718399fSFrançois Tigeot /* Update crtc values up front so the driver can rely on them for mode 2615718399fSFrançois Tigeot * setting. 2625718399fSFrançois Tigeot */ 2635718399fSFrançois Tigeot crtc->mode = *mode; 2645718399fSFrançois Tigeot crtc->x = x; 2655718399fSFrançois Tigeot crtc->y = y; 2665718399fSFrançois Tigeot 2675718399fSFrançois Tigeot /* Pass our mode to the connectors and the CRTC to give them a chance to 2685718399fSFrançois Tigeot * adjust it according to limitations or connector properties, and also 2695718399fSFrançois Tigeot * a chance to reject the mode entirely. 2705718399fSFrançois Tigeot */ 2715718399fSFrançois Tigeot list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 2725718399fSFrançois Tigeot 2735718399fSFrançois Tigeot if (encoder->crtc != crtc) 2745718399fSFrançois Tigeot continue; 275ba55f2f5SFrançois Tigeot 276ba55f2f5SFrançois Tigeot if (encoder->bridge && encoder->bridge->funcs->mode_fixup) { 277ba55f2f5SFrançois Tigeot ret = encoder->bridge->funcs->mode_fixup( 278ba55f2f5SFrançois Tigeot encoder->bridge, mode, adjusted_mode); 279ba55f2f5SFrançois Tigeot if (!ret) { 280ba55f2f5SFrançois Tigeot DRM_DEBUG_KMS("Bridge fixup failed\n"); 281ba55f2f5SFrançois Tigeot goto done; 282ba55f2f5SFrançois Tigeot } 283ba55f2f5SFrançois Tigeot } 284ba55f2f5SFrançois Tigeot 2855718399fSFrançois Tigeot encoder_funcs = encoder->helper_private; 2865718399fSFrançois Tigeot if (!(ret = encoder_funcs->mode_fixup(encoder, mode, 2875718399fSFrançois Tigeot adjusted_mode))) { 2886e29dde8SFrançois Tigeot DRM_DEBUG_KMS("Encoder fixup failed\n"); 2895718399fSFrançois Tigeot goto done; 2905718399fSFrançois Tigeot } 2915718399fSFrançois Tigeot } 2925718399fSFrançois Tigeot 2935718399fSFrançois Tigeot if (!(ret = crtc_funcs->mode_fixup(crtc, mode, adjusted_mode))) { 2946e29dde8SFrançois Tigeot DRM_DEBUG_KMS("CRTC fixup failed\n"); 2955718399fSFrançois Tigeot goto done; 2965718399fSFrançois Tigeot } 2975718399fSFrançois Tigeot DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); 2985718399fSFrançois Tigeot 2995718399fSFrançois Tigeot /* Prepare the encoders and CRTCs before setting the mode. */ 3005718399fSFrançois Tigeot list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 3015718399fSFrançois Tigeot 3025718399fSFrançois Tigeot if (encoder->crtc != crtc) 3035718399fSFrançois Tigeot continue; 304ba55f2f5SFrançois Tigeot 305ba55f2f5SFrançois Tigeot if (encoder->bridge) 306ba55f2f5SFrançois Tigeot encoder->bridge->funcs->disable(encoder->bridge); 307ba55f2f5SFrançois Tigeot 3085718399fSFrançois Tigeot encoder_funcs = encoder->helper_private; 3095718399fSFrançois Tigeot /* Disable the encoders as the first thing we do. */ 3105718399fSFrançois Tigeot encoder_funcs->prepare(encoder); 311ba55f2f5SFrançois Tigeot 312ba55f2f5SFrançois Tigeot if (encoder->bridge) 313ba55f2f5SFrançois Tigeot encoder->bridge->funcs->post_disable(encoder->bridge); 3145718399fSFrançois Tigeot } 3155718399fSFrançois Tigeot 3165718399fSFrançois Tigeot drm_crtc_prepare_encoders(dev); 3175718399fSFrançois Tigeot 3185718399fSFrançois Tigeot crtc_funcs->prepare(crtc); 3195718399fSFrançois Tigeot 3205718399fSFrançois Tigeot /* Set up the DPLL and any encoders state that needs to adjust or depend 3215718399fSFrançois Tigeot * on the DPLL. 3225718399fSFrançois Tigeot */ 3235718399fSFrançois Tigeot ret = !crtc_funcs->mode_set(crtc, mode, adjusted_mode, x, y, old_fb); 3245718399fSFrançois Tigeot if (!ret) 3255718399fSFrançois Tigeot goto done; 3265718399fSFrançois Tigeot 3275718399fSFrançois Tigeot list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 3285718399fSFrançois Tigeot 3295718399fSFrançois Tigeot if (encoder->crtc != crtc) 3305718399fSFrançois Tigeot continue; 3315718399fSFrançois Tigeot 3325718399fSFrançois Tigeot DRM_DEBUG_KMS("[ENCODER:%d:%s] set [MODE:%d:%s]\n", 333ba55f2f5SFrançois Tigeot encoder->base.id, encoder->name, 3345718399fSFrançois Tigeot mode->base.id, mode->name); 3355718399fSFrançois Tigeot encoder_funcs = encoder->helper_private; 3365718399fSFrançois Tigeot encoder_funcs->mode_set(encoder, mode, adjusted_mode); 337ba55f2f5SFrançois Tigeot 338ba55f2f5SFrançois Tigeot if (encoder->bridge && encoder->bridge->funcs->mode_set) 339ba55f2f5SFrançois Tigeot encoder->bridge->funcs->mode_set(encoder->bridge, mode, 340ba55f2f5SFrançois Tigeot adjusted_mode); 3415718399fSFrançois Tigeot } 3425718399fSFrançois Tigeot 3435718399fSFrançois Tigeot /* Now enable the clocks, plane, pipe, and connectors that we set up. */ 3445718399fSFrançois Tigeot crtc_funcs->commit(crtc); 3455718399fSFrançois Tigeot 3465718399fSFrançois Tigeot list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 3475718399fSFrançois Tigeot 3485718399fSFrançois Tigeot if (encoder->crtc != crtc) 3495718399fSFrançois Tigeot continue; 3505718399fSFrançois Tigeot 351ba55f2f5SFrançois Tigeot if (encoder->bridge) 352ba55f2f5SFrançois Tigeot encoder->bridge->funcs->pre_enable(encoder->bridge); 353ba55f2f5SFrançois Tigeot 3545718399fSFrançois Tigeot encoder_funcs = encoder->helper_private; 3555718399fSFrançois Tigeot encoder_funcs->commit(encoder); 3565718399fSFrançois Tigeot 357ba55f2f5SFrançois Tigeot if (encoder->bridge) 358ba55f2f5SFrançois Tigeot encoder->bridge->funcs->enable(encoder->bridge); 3595718399fSFrançois Tigeot } 3605718399fSFrançois Tigeot 3615718399fSFrançois Tigeot /* Store real post-adjustment hardware mode. */ 3625718399fSFrançois Tigeot crtc->hwmode = *adjusted_mode; 3635718399fSFrançois Tigeot 3645718399fSFrançois Tigeot /* Calculate and store various constants which 3655718399fSFrançois Tigeot * are later needed by vblank and swap-completion 3665718399fSFrançois Tigeot * timestamping. They are derived from true hwmode. 3675718399fSFrançois Tigeot */ 3689edbd4a0SFrançois Tigeot drm_calc_timestamping_constants(crtc, &crtc->hwmode); 3695718399fSFrançois Tigeot 3705718399fSFrançois Tigeot /* FIXME: add subpixel order */ 3715718399fSFrançois Tigeot done: 3725718399fSFrançois Tigeot drm_mode_destroy(dev, adjusted_mode); 3735718399fSFrançois Tigeot if (!ret) { 3749edbd4a0SFrançois Tigeot crtc->enabled = saved_enabled; 3755718399fSFrançois Tigeot crtc->mode = saved_mode; 3765718399fSFrançois Tigeot crtc->x = saved_x; 3775718399fSFrançois Tigeot crtc->y = saved_y; 3785718399fSFrançois Tigeot } 3795718399fSFrançois Tigeot 3805718399fSFrançois Tigeot return ret; 3815718399fSFrançois Tigeot } 3826e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_crtc_helper_set_mode); 3836e29dde8SFrançois Tigeot 384ba55f2f5SFrançois Tigeot static void 3855718399fSFrançois Tigeot drm_crtc_helper_disable(struct drm_crtc *crtc) 3865718399fSFrançois Tigeot { 3875718399fSFrançois Tigeot struct drm_device *dev = crtc->dev; 3885718399fSFrançois Tigeot struct drm_connector *connector; 3895718399fSFrançois Tigeot struct drm_encoder *encoder; 3905718399fSFrançois Tigeot 3915718399fSFrançois Tigeot /* Decouple all encoders and their attached connectors from this crtc */ 3925718399fSFrançois Tigeot list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 3935718399fSFrançois Tigeot if (encoder->crtc != crtc) 3945718399fSFrançois Tigeot continue; 3955718399fSFrançois Tigeot 3965718399fSFrançois Tigeot list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 3975718399fSFrançois Tigeot if (connector->encoder != encoder) 3985718399fSFrançois Tigeot continue; 3995718399fSFrançois Tigeot 4005718399fSFrançois Tigeot connector->encoder = NULL; 4019edbd4a0SFrançois Tigeot 4029edbd4a0SFrançois Tigeot /* 4039edbd4a0SFrançois Tigeot * drm_helper_disable_unused_functions() ought to be 4049edbd4a0SFrançois Tigeot * doing this, but since we've decoupled the encoder 4059edbd4a0SFrançois Tigeot * from the connector above, the required connection 4069edbd4a0SFrançois Tigeot * between them is henceforth no longer available. 4079edbd4a0SFrançois Tigeot */ 4089edbd4a0SFrançois Tigeot connector->dpms = DRM_MODE_DPMS_OFF; 4095718399fSFrançois Tigeot } 4105718399fSFrançois Tigeot } 4115718399fSFrançois Tigeot 412ba55f2f5SFrançois Tigeot __drm_helper_disable_unused_functions(dev); 4135718399fSFrançois Tigeot } 4145718399fSFrançois Tigeot 4155718399fSFrançois Tigeot /** 4165718399fSFrançois Tigeot * drm_crtc_helper_set_config - set a new config from userspace 4176e29dde8SFrançois Tigeot * @set: mode set configuration 4185718399fSFrançois Tigeot * 4196e29dde8SFrançois Tigeot * Setup a new configuration, provided by the upper layers (either an ioctl call 420ba55f2f5SFrançois Tigeot * from userspace or internally e.g. from the fbdev support code) in @set, and 4216e29dde8SFrançois Tigeot * enable it. This is the main helper functions for drivers that implement 4226e29dde8SFrançois Tigeot * kernel mode setting with the crtc helper functions and the assorted 4236e29dde8SFrançois Tigeot * ->prepare(), ->modeset() and ->commit() helper callbacks. 4245718399fSFrançois Tigeot * 425ba55f2f5SFrançois Tigeot * Returns: 426ba55f2f5SFrançois Tigeot * Returns 0 on success, negative errno numbers on failure. 4275718399fSFrançois Tigeot */ 4285718399fSFrançois Tigeot int drm_crtc_helper_set_config(struct drm_mode_set *set) 4295718399fSFrançois Tigeot { 4305718399fSFrançois Tigeot struct drm_device *dev; 4319edbd4a0SFrançois Tigeot struct drm_crtc *new_crtc; 4325718399fSFrançois Tigeot struct drm_encoder *save_encoders, *new_encoder, *encoder; 4335718399fSFrançois Tigeot bool mode_changed = false; /* if true do a full mode set */ 4345718399fSFrançois Tigeot bool fb_changed = false; /* if true and !mode_changed just do a flip */ 4355718399fSFrançois Tigeot struct drm_connector *save_connectors, *connector; 4365718399fSFrançois Tigeot int count = 0, ro, fail = 0; 4375718399fSFrançois Tigeot struct drm_crtc_helper_funcs *crtc_funcs; 4385718399fSFrançois Tigeot struct drm_mode_set save_set; 4396e29dde8SFrançois Tigeot int ret; 4405718399fSFrançois Tigeot int i; 4415718399fSFrançois Tigeot 4425718399fSFrançois Tigeot DRM_DEBUG_KMS("\n"); 4435718399fSFrançois Tigeot 4449edbd4a0SFrançois Tigeot BUG_ON(!set); 4459edbd4a0SFrançois Tigeot BUG_ON(!set->crtc); 4469edbd4a0SFrançois Tigeot BUG_ON(!set->crtc->helper_private); 4475718399fSFrançois Tigeot 4489edbd4a0SFrançois Tigeot /* Enforce sane interface api - has been abused by the fb helper. */ 4499edbd4a0SFrançois Tigeot BUG_ON(!set->mode && set->fb); 4509edbd4a0SFrançois Tigeot BUG_ON(set->fb && set->num_connectors == 0); 4515718399fSFrançois Tigeot 4525718399fSFrançois Tigeot crtc_funcs = set->crtc->helper_private; 4535718399fSFrançois Tigeot 4545718399fSFrançois Tigeot if (!set->mode) 4555718399fSFrançois Tigeot set->fb = NULL; 4565718399fSFrançois Tigeot 4575718399fSFrançois Tigeot if (set->fb) { 4585718399fSFrançois Tigeot DRM_DEBUG_KMS("[CRTC:%d] [FB:%d] #connectors=%d (x y) (%i %i)\n", 4595718399fSFrançois Tigeot set->crtc->base.id, set->fb->base.id, 4605718399fSFrançois Tigeot (int)set->num_connectors, set->x, set->y); 4615718399fSFrançois Tigeot } else { 4625718399fSFrançois Tigeot DRM_DEBUG_KMS("[CRTC:%d] [NOFB]\n", set->crtc->base.id); 463ba55f2f5SFrançois Tigeot drm_crtc_helper_disable(set->crtc); 464ba55f2f5SFrançois Tigeot return 0; 4655718399fSFrançois Tigeot } 4665718399fSFrançois Tigeot 4675718399fSFrançois Tigeot dev = set->crtc->dev; 4685718399fSFrançois Tigeot 469ba55f2f5SFrançois Tigeot drm_warn_on_modeset_not_all_locked(dev); 470ba55f2f5SFrançois Tigeot 4719edbd4a0SFrançois Tigeot /* 4729edbd4a0SFrançois Tigeot * Allocate space for the backup of all (non-pointer) encoder and 4739edbd4a0SFrançois Tigeot * connector data. 4749edbd4a0SFrançois Tigeot */ 4754dbb207bSFrançois Tigeot save_encoders = kzalloc(dev->mode_config.num_encoder * 4764dbb207bSFrançois Tigeot sizeof(struct drm_encoder), GFP_KERNEL); 4779edbd4a0SFrançois Tigeot if (!save_encoders) 4784dbb207bSFrançois Tigeot return -ENOMEM; 4794dbb207bSFrançois Tigeot 4804dbb207bSFrançois Tigeot save_connectors = kzalloc(dev->mode_config.num_connector * 4814dbb207bSFrançois Tigeot sizeof(struct drm_connector), GFP_KERNEL); 4824dbb207bSFrançois Tigeot if (!save_connectors) { 4834dbb207bSFrançois Tigeot kfree(save_encoders); 4844dbb207bSFrançois Tigeot return -ENOMEM; 4854dbb207bSFrançois Tigeot } 4865718399fSFrançois Tigeot 4879edbd4a0SFrançois Tigeot /* 4889edbd4a0SFrançois Tigeot * Copy data. Note that driver private data is not affected. 4895718399fSFrançois Tigeot * Should anything bad happen only the expected state is 4905718399fSFrançois Tigeot * restored, not the drivers personal bookkeeping. 4915718399fSFrançois Tigeot */ 4925718399fSFrançois Tigeot count = 0; 4935718399fSFrançois Tigeot list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 4945718399fSFrançois Tigeot save_encoders[count++] = *encoder; 4955718399fSFrançois Tigeot } 4965718399fSFrançois Tigeot 4975718399fSFrançois Tigeot count = 0; 4985718399fSFrançois Tigeot list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 4995718399fSFrançois Tigeot save_connectors[count++] = *connector; 5005718399fSFrançois Tigeot } 5015718399fSFrançois Tigeot 5025718399fSFrançois Tigeot save_set.crtc = set->crtc; 5035718399fSFrançois Tigeot save_set.mode = &set->crtc->mode; 5045718399fSFrançois Tigeot save_set.x = set->crtc->x; 5055718399fSFrançois Tigeot save_set.y = set->crtc->y; 506ba55f2f5SFrançois Tigeot save_set.fb = set->crtc->primary->fb; 5075718399fSFrançois Tigeot 5085718399fSFrançois Tigeot /* We should be able to check here if the fb has the same properties 5095718399fSFrançois Tigeot * and then just flip_or_move it */ 510ba55f2f5SFrançois Tigeot if (set->crtc->primary->fb != set->fb) { 5115718399fSFrançois Tigeot /* If we have no fb then treat it as a full mode set */ 512ba55f2f5SFrançois Tigeot if (set->crtc->primary->fb == NULL) { 5135718399fSFrançois Tigeot DRM_DEBUG_KMS("crtc has no fb, full mode set\n"); 5145718399fSFrançois Tigeot mode_changed = true; 5155718399fSFrançois Tigeot } else if (set->fb == NULL) { 5165718399fSFrançois Tigeot mode_changed = true; 5174dbb207bSFrançois Tigeot } else if (set->fb->pixel_format != 518ba55f2f5SFrançois Tigeot set->crtc->primary->fb->pixel_format) { 5194dbb207bSFrançois Tigeot mode_changed = true; 5205718399fSFrançois Tigeot } else 5215718399fSFrançois Tigeot fb_changed = true; 5225718399fSFrançois Tigeot } 5235718399fSFrançois Tigeot 5245718399fSFrançois Tigeot if (set->x != set->crtc->x || set->y != set->crtc->y) 5255718399fSFrançois Tigeot fb_changed = true; 5265718399fSFrançois Tigeot 5275718399fSFrançois Tigeot if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) { 5285718399fSFrançois Tigeot DRM_DEBUG_KMS("modes are different, full mode set\n"); 5295718399fSFrançois Tigeot drm_mode_debug_printmodeline(&set->crtc->mode); 5305718399fSFrançois Tigeot drm_mode_debug_printmodeline(set->mode); 5315718399fSFrançois Tigeot mode_changed = true; 5325718399fSFrançois Tigeot } 5335718399fSFrançois Tigeot 5345718399fSFrançois Tigeot /* a) traverse passed in connector list and get encoders for them */ 5355718399fSFrançois Tigeot count = 0; 5365718399fSFrançois Tigeot list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 5375718399fSFrançois Tigeot struct drm_connector_helper_funcs *connector_funcs = 5385718399fSFrançois Tigeot connector->helper_private; 5395718399fSFrançois Tigeot new_encoder = connector->encoder; 5405718399fSFrançois Tigeot for (ro = 0; ro < set->num_connectors; ro++) { 5415718399fSFrançois Tigeot if (set->connectors[ro] == connector) { 5425718399fSFrançois Tigeot new_encoder = connector_funcs->best_encoder(connector); 5435718399fSFrançois Tigeot /* if we can't get an encoder for a connector 5445718399fSFrançois Tigeot we are setting now - then fail */ 5455718399fSFrançois Tigeot if (new_encoder == NULL) 5465718399fSFrançois Tigeot /* don't break so fail path works correct */ 5475718399fSFrançois Tigeot fail = 1; 5489edbd4a0SFrançois Tigeot 5499edbd4a0SFrançois Tigeot if (connector->dpms != DRM_MODE_DPMS_ON) { 5509edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("connector dpms not on, full mode switch\n"); 5519edbd4a0SFrançois Tigeot mode_changed = true; 5529edbd4a0SFrançois Tigeot } 553ba55f2f5SFrançois Tigeot 554ba55f2f5SFrançois Tigeot break; 5555718399fSFrançois Tigeot } 5565718399fSFrançois Tigeot } 5575718399fSFrançois Tigeot 5585718399fSFrançois Tigeot if (new_encoder != connector->encoder) { 5595718399fSFrançois Tigeot DRM_DEBUG_KMS("encoder changed, full mode switch\n"); 5605718399fSFrançois Tigeot mode_changed = true; 5615718399fSFrançois Tigeot /* If the encoder is reused for another connector, then 5625718399fSFrançois Tigeot * the appropriate crtc will be set later. 5635718399fSFrançois Tigeot */ 5645718399fSFrançois Tigeot if (connector->encoder) 5655718399fSFrançois Tigeot connector->encoder->crtc = NULL; 5665718399fSFrançois Tigeot connector->encoder = new_encoder; 5675718399fSFrançois Tigeot } 5685718399fSFrançois Tigeot } 5695718399fSFrançois Tigeot 5705718399fSFrançois Tigeot if (fail) { 5715718399fSFrançois Tigeot ret = -EINVAL; 5725718399fSFrançois Tigeot goto fail; 5735718399fSFrançois Tigeot } 5745718399fSFrançois Tigeot 5755718399fSFrançois Tigeot count = 0; 5765718399fSFrançois Tigeot list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 5775718399fSFrançois Tigeot if (!connector->encoder) 5785718399fSFrançois Tigeot continue; 5795718399fSFrançois Tigeot 5805718399fSFrançois Tigeot if (connector->encoder->crtc == set->crtc) 5815718399fSFrançois Tigeot new_crtc = NULL; 5825718399fSFrançois Tigeot else 5835718399fSFrançois Tigeot new_crtc = connector->encoder->crtc; 5845718399fSFrançois Tigeot 5855718399fSFrançois Tigeot for (ro = 0; ro < set->num_connectors; ro++) { 5865718399fSFrançois Tigeot if (set->connectors[ro] == connector) 5875718399fSFrançois Tigeot new_crtc = set->crtc; 5885718399fSFrançois Tigeot } 5895718399fSFrançois Tigeot 5905718399fSFrançois Tigeot /* Make sure the new CRTC will work with the encoder */ 5915718399fSFrançois Tigeot if (new_crtc && 5925718399fSFrançois Tigeot !drm_encoder_crtc_ok(connector->encoder, new_crtc)) { 5935718399fSFrançois Tigeot ret = -EINVAL; 5945718399fSFrançois Tigeot goto fail; 5955718399fSFrançois Tigeot } 5965718399fSFrançois Tigeot if (new_crtc != connector->encoder->crtc) { 5975718399fSFrançois Tigeot DRM_DEBUG_KMS("crtc changed, full mode switch\n"); 5985718399fSFrançois Tigeot mode_changed = true; 5995718399fSFrançois Tigeot connector->encoder->crtc = new_crtc; 6005718399fSFrançois Tigeot } 6015718399fSFrançois Tigeot if (new_crtc) { 6025718399fSFrançois Tigeot DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d]\n", 603ba55f2f5SFrançois Tigeot connector->base.id, connector->name, 6045718399fSFrançois Tigeot new_crtc->base.id); 6055718399fSFrançois Tigeot } else { 6065718399fSFrançois Tigeot DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [NOCRTC]\n", 607ba55f2f5SFrançois Tigeot connector->base.id, connector->name); 6085718399fSFrançois Tigeot } 6095718399fSFrançois Tigeot } 6105718399fSFrançois Tigeot 6115718399fSFrançois Tigeot /* mode_set_base is not a required function */ 6125718399fSFrançois Tigeot if (fb_changed && !crtc_funcs->mode_set_base) 6135718399fSFrançois Tigeot mode_changed = true; 6145718399fSFrançois Tigeot 6155718399fSFrançois Tigeot if (mode_changed) { 6169edbd4a0SFrançois Tigeot if (drm_helper_crtc_in_use(set->crtc)) { 6175718399fSFrançois Tigeot DRM_DEBUG_KMS("attempting to set mode from" 6185718399fSFrançois Tigeot " userspace\n"); 6195718399fSFrançois Tigeot drm_mode_debug_printmodeline(set->mode); 620ba55f2f5SFrançois Tigeot set->crtc->primary->fb = set->fb; 6215718399fSFrançois Tigeot if (!drm_crtc_helper_set_mode(set->crtc, set->mode, 6225718399fSFrançois Tigeot set->x, set->y, 6239edbd4a0SFrançois Tigeot save_set.fb)) { 6245718399fSFrançois Tigeot DRM_ERROR("failed to set mode on [CRTC:%d]\n", 6255718399fSFrançois Tigeot set->crtc->base.id); 626ba55f2f5SFrançois Tigeot set->crtc->primary->fb = save_set.fb; 6275718399fSFrançois Tigeot ret = -EINVAL; 6285718399fSFrançois Tigeot goto fail; 6295718399fSFrançois Tigeot } 6305718399fSFrançois Tigeot DRM_DEBUG_KMS("Setting connector DPMS state to on\n"); 6315718399fSFrançois Tigeot for (i = 0; i < set->num_connectors; i++) { 6325718399fSFrançois Tigeot DRM_DEBUG_KMS("\t[CONNECTOR:%d:%s] set DPMS on\n", set->connectors[i]->base.id, 633ba55f2f5SFrançois Tigeot set->connectors[i]->name); 6346e29dde8SFrançois Tigeot set->connectors[i]->funcs->dpms(set->connectors[i], DRM_MODE_DPMS_ON); 6355718399fSFrançois Tigeot } 6365718399fSFrançois Tigeot } 637ba55f2f5SFrançois Tigeot __drm_helper_disable_unused_functions(dev); 6385718399fSFrançois Tigeot } else if (fb_changed) { 6395718399fSFrançois Tigeot set->crtc->x = set->x; 6405718399fSFrançois Tigeot set->crtc->y = set->y; 641ba55f2f5SFrançois Tigeot set->crtc->primary->fb = set->fb; 6425718399fSFrançois Tigeot ret = crtc_funcs->mode_set_base(set->crtc, 6439edbd4a0SFrançois Tigeot set->x, set->y, save_set.fb); 6445718399fSFrançois Tigeot if (ret != 0) { 6459edbd4a0SFrançois Tigeot set->crtc->x = save_set.x; 6469edbd4a0SFrançois Tigeot set->crtc->y = save_set.y; 647ba55f2f5SFrançois Tigeot set->crtc->primary->fb = save_set.fb; 6485718399fSFrançois Tigeot goto fail; 6495718399fSFrançois Tigeot } 6505718399fSFrançois Tigeot } 6515718399fSFrançois Tigeot 652158486a6SFrançois Tigeot kfree(save_connectors); 653158486a6SFrançois Tigeot kfree(save_encoders); 6545718399fSFrançois Tigeot return 0; 6555718399fSFrançois Tigeot 6565718399fSFrançois Tigeot fail: 6575718399fSFrançois Tigeot /* Restore all previous data. */ 6585718399fSFrançois Tigeot count = 0; 6595718399fSFrançois Tigeot list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 6605718399fSFrançois Tigeot *encoder = save_encoders[count++]; 6615718399fSFrançois Tigeot } 6625718399fSFrançois Tigeot 6635718399fSFrançois Tigeot count = 0; 6645718399fSFrançois Tigeot list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 6655718399fSFrançois Tigeot *connector = save_connectors[count++]; 6665718399fSFrançois Tigeot } 6675718399fSFrançois Tigeot 6685718399fSFrançois Tigeot /* Try to restore the config */ 6695718399fSFrançois Tigeot if (mode_changed && 6705718399fSFrançois Tigeot !drm_crtc_helper_set_mode(save_set.crtc, save_set.mode, save_set.x, 6715718399fSFrançois Tigeot save_set.y, save_set.fb)) 6725718399fSFrançois Tigeot DRM_ERROR("failed to restore config after modeset failure\n"); 6735718399fSFrançois Tigeot 6744dbb207bSFrançois Tigeot kfree(save_connectors); 6754dbb207bSFrançois Tigeot kfree(save_encoders); 6765718399fSFrançois Tigeot return ret; 6775718399fSFrançois Tigeot } 6786e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_crtc_helper_set_config); 6795718399fSFrançois Tigeot 6805718399fSFrançois Tigeot static int drm_helper_choose_encoder_dpms(struct drm_encoder *encoder) 6815718399fSFrançois Tigeot { 6825718399fSFrançois Tigeot int dpms = DRM_MODE_DPMS_OFF; 6835718399fSFrançois Tigeot struct drm_connector *connector; 6845718399fSFrançois Tigeot struct drm_device *dev = encoder->dev; 6855718399fSFrançois Tigeot 6865718399fSFrançois Tigeot list_for_each_entry(connector, &dev->mode_config.connector_list, head) 6875718399fSFrançois Tigeot if (connector->encoder == encoder) 6885718399fSFrançois Tigeot if (connector->dpms < dpms) 6895718399fSFrançois Tigeot dpms = connector->dpms; 6905718399fSFrançois Tigeot return dpms; 6915718399fSFrançois Tigeot } 6925718399fSFrançois Tigeot 693ba55f2f5SFrançois Tigeot /* Helper which handles bridge ordering around encoder dpms */ 694ba55f2f5SFrançois Tigeot static void drm_helper_encoder_dpms(struct drm_encoder *encoder, int mode) 695ba55f2f5SFrançois Tigeot { 696ba55f2f5SFrançois Tigeot struct drm_bridge *bridge = encoder->bridge; 697ba55f2f5SFrançois Tigeot struct drm_encoder_helper_funcs *encoder_funcs; 698ba55f2f5SFrançois Tigeot 699ba55f2f5SFrançois Tigeot if (bridge) { 700ba55f2f5SFrançois Tigeot if (mode == DRM_MODE_DPMS_ON) 701ba55f2f5SFrançois Tigeot bridge->funcs->pre_enable(bridge); 702ba55f2f5SFrançois Tigeot else 703ba55f2f5SFrançois Tigeot bridge->funcs->disable(bridge); 704ba55f2f5SFrançois Tigeot } 705ba55f2f5SFrançois Tigeot 706ba55f2f5SFrançois Tigeot encoder_funcs = encoder->helper_private; 707ba55f2f5SFrançois Tigeot if (encoder_funcs->dpms) 708ba55f2f5SFrançois Tigeot encoder_funcs->dpms(encoder, mode); 709ba55f2f5SFrançois Tigeot 710ba55f2f5SFrançois Tigeot if (bridge) { 711ba55f2f5SFrançois Tigeot if (mode == DRM_MODE_DPMS_ON) 712ba55f2f5SFrançois Tigeot bridge->funcs->enable(bridge); 713ba55f2f5SFrançois Tigeot else 714ba55f2f5SFrançois Tigeot bridge->funcs->post_disable(bridge); 715ba55f2f5SFrançois Tigeot } 716ba55f2f5SFrançois Tigeot } 717ba55f2f5SFrançois Tigeot 7185718399fSFrançois Tigeot static int drm_helper_choose_crtc_dpms(struct drm_crtc *crtc) 7195718399fSFrançois Tigeot { 7205718399fSFrançois Tigeot int dpms = DRM_MODE_DPMS_OFF; 7215718399fSFrançois Tigeot struct drm_connector *connector; 7225718399fSFrançois Tigeot struct drm_device *dev = crtc->dev; 7235718399fSFrançois Tigeot 7245718399fSFrançois Tigeot list_for_each_entry(connector, &dev->mode_config.connector_list, head) 7255718399fSFrançois Tigeot if (connector->encoder && connector->encoder->crtc == crtc) 7265718399fSFrançois Tigeot if (connector->dpms < dpms) 7275718399fSFrançois Tigeot dpms = connector->dpms; 7285718399fSFrançois Tigeot return dpms; 7295718399fSFrançois Tigeot } 7305718399fSFrançois Tigeot 7315718399fSFrançois Tigeot /** 7326e29dde8SFrançois Tigeot * drm_helper_connector_dpms() - connector dpms helper implementation 7336e29dde8SFrançois Tigeot * @connector: affected connector 7346e29dde8SFrançois Tigeot * @mode: DPMS mode 7355718399fSFrançois Tigeot * 7366e29dde8SFrançois Tigeot * This is the main helper function provided by the crtc helper framework for 7376e29dde8SFrançois Tigeot * implementing the DPMS connector attribute. It computes the new desired DPMS 7386e29dde8SFrançois Tigeot * state for all encoders and crtcs in the output mesh and calls the ->dpms() 7396e29dde8SFrançois Tigeot * callback provided by the driver appropriately. 7405718399fSFrançois Tigeot */ 7415718399fSFrançois Tigeot void drm_helper_connector_dpms(struct drm_connector *connector, int mode) 7425718399fSFrançois Tigeot { 7435718399fSFrançois Tigeot struct drm_encoder *encoder = connector->encoder; 7445718399fSFrançois Tigeot struct drm_crtc *crtc = encoder ? encoder->crtc : NULL; 745ba55f2f5SFrançois Tigeot int old_dpms, encoder_dpms = DRM_MODE_DPMS_OFF; 7465718399fSFrançois Tigeot 7475718399fSFrançois Tigeot if (mode == connector->dpms) 7485718399fSFrançois Tigeot return; 7495718399fSFrançois Tigeot 7505718399fSFrançois Tigeot old_dpms = connector->dpms; 7515718399fSFrançois Tigeot connector->dpms = mode; 7525718399fSFrançois Tigeot 753ba55f2f5SFrançois Tigeot if (encoder) 754ba55f2f5SFrançois Tigeot encoder_dpms = drm_helper_choose_encoder_dpms(encoder); 755ba55f2f5SFrançois Tigeot 7565718399fSFrançois Tigeot /* from off to on, do crtc then encoder */ 7575718399fSFrançois Tigeot if (mode < old_dpms) { 7585718399fSFrançois Tigeot if (crtc) { 7595718399fSFrançois Tigeot struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; 7605718399fSFrançois Tigeot if (crtc_funcs->dpms) 7615718399fSFrançois Tigeot (*crtc_funcs->dpms) (crtc, 7625718399fSFrançois Tigeot drm_helper_choose_crtc_dpms(crtc)); 7635718399fSFrançois Tigeot } 764ba55f2f5SFrançois Tigeot if (encoder) 765ba55f2f5SFrançois Tigeot drm_helper_encoder_dpms(encoder, encoder_dpms); 7665718399fSFrançois Tigeot } 7675718399fSFrançois Tigeot 7685718399fSFrançois Tigeot /* from on to off, do encoder then crtc */ 7695718399fSFrançois Tigeot if (mode > old_dpms) { 770ba55f2f5SFrançois Tigeot if (encoder) 771ba55f2f5SFrançois Tigeot drm_helper_encoder_dpms(encoder, encoder_dpms); 7725718399fSFrançois Tigeot if (crtc) { 7735718399fSFrançois Tigeot struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; 7745718399fSFrançois Tigeot if (crtc_funcs->dpms) 7755718399fSFrançois Tigeot (*crtc_funcs->dpms) (crtc, 7765718399fSFrançois Tigeot drm_helper_choose_crtc_dpms(crtc)); 7775718399fSFrançois Tigeot } 7785718399fSFrançois Tigeot } 7795718399fSFrançois Tigeot 7805718399fSFrançois Tigeot return; 7815718399fSFrançois Tigeot } 7826e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_helper_connector_dpms); 7835718399fSFrançois Tigeot 784ba55f2f5SFrançois Tigeot /** 785ba55f2f5SFrançois Tigeot * drm_helper_mode_fill_fb_struct - fill out framebuffer metadata 786ba55f2f5SFrançois Tigeot * @fb: drm_framebuffer object to fill out 787ba55f2f5SFrançois Tigeot * @mode_cmd: metadata from the userspace fb creation request 788ba55f2f5SFrançois Tigeot * 789ba55f2f5SFrançois Tigeot * This helper can be used in a drivers fb_create callback to pre-fill the fb's 790ba55f2f5SFrançois Tigeot * metadata fields. 791ba55f2f5SFrançois Tigeot */ 792ba55f2f5SFrançois Tigeot void drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, 7935718399fSFrançois Tigeot struct drm_mode_fb_cmd2 *mode_cmd) 7945718399fSFrançois Tigeot { 7955718399fSFrançois Tigeot int i; 7965718399fSFrançois Tigeot 7975718399fSFrançois Tigeot fb->width = mode_cmd->width; 7985718399fSFrançois Tigeot fb->height = mode_cmd->height; 7995718399fSFrançois Tigeot for (i = 0; i < 4; i++) { 8005718399fSFrançois Tigeot fb->pitches[i] = mode_cmd->pitches[i]; 8015718399fSFrançois Tigeot fb->offsets[i] = mode_cmd->offsets[i]; 8025718399fSFrançois Tigeot } 8035718399fSFrançois Tigeot drm_fb_get_bpp_depth(mode_cmd->pixel_format, &fb->depth, 8045718399fSFrançois Tigeot &fb->bits_per_pixel); 8055718399fSFrançois Tigeot fb->pixel_format = mode_cmd->pixel_format; 806*24edb884SFrançois Tigeot fb->flags = mode_cmd->flags; 8075718399fSFrançois Tigeot } 8086e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct); 8095718399fSFrançois Tigeot 810ba55f2f5SFrançois Tigeot /** 811ba55f2f5SFrançois Tigeot * drm_helper_resume_force_mode - force-restore mode setting configuration 812ba55f2f5SFrançois Tigeot * @dev: drm_device which should be restored 813ba55f2f5SFrançois Tigeot * 814ba55f2f5SFrançois Tigeot * Drivers which use the mode setting helpers can use this function to 815ba55f2f5SFrançois Tigeot * force-restore the mode setting configuration e.g. on resume or when something 816ba55f2f5SFrançois Tigeot * else might have trampled over the hw state (like some overzealous old BIOSen 817ba55f2f5SFrançois Tigeot * tended to do). 818ba55f2f5SFrançois Tigeot * 819ba55f2f5SFrançois Tigeot * This helper doesn't provide a error return value since restoring the old 820ba55f2f5SFrançois Tigeot * config should never fail due to resource allocation issues since the driver 821ba55f2f5SFrançois Tigeot * has successfully set the restored configuration already. Hence this should 822ba55f2f5SFrançois Tigeot * boil down to the equivalent of a few dpms on calls, which also don't provide 823ba55f2f5SFrançois Tigeot * an error code. 824ba55f2f5SFrançois Tigeot * 825ba55f2f5SFrançois Tigeot * Drivers where simply restoring an old configuration again might fail (e.g. 826ba55f2f5SFrançois Tigeot * due to slight differences in allocating shared resources when the 827ba55f2f5SFrançois Tigeot * configuration is restored in a different order than when userspace set it up) 828ba55f2f5SFrançois Tigeot * need to use their own restore logic. 829ba55f2f5SFrançois Tigeot */ 830ba55f2f5SFrançois Tigeot void drm_helper_resume_force_mode(struct drm_device *dev) 8315718399fSFrançois Tigeot { 8325718399fSFrançois Tigeot struct drm_crtc *crtc; 8335718399fSFrançois Tigeot struct drm_encoder *encoder; 8345718399fSFrançois Tigeot struct drm_crtc_helper_funcs *crtc_funcs; 835ba55f2f5SFrançois Tigeot int encoder_dpms; 836ba55f2f5SFrançois Tigeot bool ret; 8375718399fSFrançois Tigeot 838ba55f2f5SFrançois Tigeot drm_modeset_lock_all(dev); 8395718399fSFrançois Tigeot list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 8405718399fSFrançois Tigeot 8415718399fSFrançois Tigeot if (!crtc->enabled) 8425718399fSFrançois Tigeot continue; 8435718399fSFrançois Tigeot 8445718399fSFrançois Tigeot ret = drm_crtc_helper_set_mode(crtc, &crtc->mode, 845ba55f2f5SFrançois Tigeot crtc->x, crtc->y, crtc->primary->fb); 8465718399fSFrançois Tigeot 847ba55f2f5SFrançois Tigeot /* Restoring the old config should never fail! */ 8486e29dde8SFrançois Tigeot if (ret == false) 8495718399fSFrançois Tigeot DRM_ERROR("failed to set mode on crtc %p\n", crtc); 8505718399fSFrançois Tigeot 8515718399fSFrançois Tigeot /* Turn off outputs that were already powered off */ 8525718399fSFrançois Tigeot if (drm_helper_choose_crtc_dpms(crtc)) { 8535718399fSFrançois Tigeot list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 8545718399fSFrançois Tigeot 8555718399fSFrançois Tigeot if(encoder->crtc != crtc) 8565718399fSFrançois Tigeot continue; 8575718399fSFrançois Tigeot 858ba55f2f5SFrançois Tigeot encoder_dpms = drm_helper_choose_encoder_dpms( 859ba55f2f5SFrançois Tigeot encoder); 860ba55f2f5SFrançois Tigeot 861ba55f2f5SFrançois Tigeot drm_helper_encoder_dpms(encoder, encoder_dpms); 8625718399fSFrançois Tigeot } 8635718399fSFrançois Tigeot 8645718399fSFrançois Tigeot crtc_funcs = crtc->helper_private; 8655718399fSFrançois Tigeot if (crtc_funcs->dpms) 8665718399fSFrançois Tigeot (*crtc_funcs->dpms) (crtc, 8675718399fSFrançois Tigeot drm_helper_choose_crtc_dpms(crtc)); 8685718399fSFrançois Tigeot } 8695718399fSFrançois Tigeot } 870ba55f2f5SFrançois Tigeot 8715718399fSFrançois Tigeot /* disable the unused connectors while restoring the modesetting */ 872ba55f2f5SFrançois Tigeot __drm_helper_disable_unused_functions(dev); 873ba55f2f5SFrançois Tigeot drm_modeset_unlock_all(dev); 8745718399fSFrançois Tigeot } 8756e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_helper_resume_force_mode); 876