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 32*a05eeebfSFrançois Tigeot #include <linux/kernel.h> 336e29dde8SFrançois Tigeot #include <linux/export.h> 344dbb207bSFrançois Tigeot #include <linux/moduleparam.h> 356e29dde8SFrançois Tigeot 3618e26a6dSFrançois Tigeot #include <drm/drmP.h> 372c9916cdSFrançois Tigeot #include <drm/drm_atomic.h> 3818e26a6dSFrançois Tigeot #include <drm/drm_crtc.h> 39*a05eeebfSFrançois Tigeot #include <uapi_drm/drm_fourcc.h> 4018e26a6dSFrançois Tigeot #include <drm/drm_crtc_helper.h> 4118e26a6dSFrançois Tigeot #include <drm/drm_fb_helper.h> 422c9916cdSFrançois Tigeot #include <drm/drm_plane_helper.h> 432c9916cdSFrançois Tigeot #include <drm/drm_atomic_helper.h> 446e29dde8SFrançois Tigeot #include <drm/drm_edid.h> 455718399fSFrançois Tigeot 462c9916cdSFrançois Tigeot /** 472c9916cdSFrançois Tigeot * DOC: overview 482c9916cdSFrançois Tigeot * 492c9916cdSFrançois Tigeot * The CRTC modeset helper library provides a default set_config implementation 502c9916cdSFrançois Tigeot * in drm_crtc_helper_set_config(). Plus a few other convenience functions using 512c9916cdSFrançois Tigeot * the same callbacks which drivers can use to e.g. restore the modeset 522c9916cdSFrançois Tigeot * configuration on resume with drm_helper_resume_force_mode(). 532c9916cdSFrançois Tigeot * 542c9916cdSFrançois Tigeot * The driver callbacks are mostly compatible with the atomic modeset helpers, 552c9916cdSFrançois Tigeot * except for the handling of the primary plane: Atomic helpers require that the 562c9916cdSFrançois Tigeot * primary plane is implemented as a real standalone plane and not directly tied 572c9916cdSFrançois Tigeot * to the CRTC state. For easier transition this library provides functions to 582c9916cdSFrançois Tigeot * implement the old semantics required by the CRTC helpers using the new plane 592c9916cdSFrançois Tigeot * and atomic helper callbacks. 602c9916cdSFrançois Tigeot * 612c9916cdSFrançois Tigeot * Drivers are strongly urged to convert to the atomic helpers (by way of first 622c9916cdSFrançois Tigeot * converting to the plane helpers). New drivers must not use these functions 632c9916cdSFrançois Tigeot * but need to implement the atomic interface instead, potentially using the 642c9916cdSFrançois Tigeot * atomic helpers for that. 652c9916cdSFrançois Tigeot */ 669edbd4a0SFrançois Tigeot MODULE_AUTHOR("David Airlie, Jesse Barnes"); 679edbd4a0SFrançois Tigeot MODULE_DESCRIPTION("DRM KMS helper"); 689edbd4a0SFrançois Tigeot 696e29dde8SFrançois Tigeot /** 706e29dde8SFrançois Tigeot * drm_helper_move_panel_connectors_to_head() - move panels to the front in the 716e29dde8SFrançois Tigeot * connector list 726e29dde8SFrançois Tigeot * @dev: drm device to operate on 736e29dde8SFrançois Tigeot * 746e29dde8SFrançois Tigeot * Some userspace presumes that the first connected connector is the main 756e29dde8SFrançois Tigeot * display, where it's supposed to display e.g. the login screen. For 766e29dde8SFrançois Tigeot * laptops, this should be the main panel. Use this function to sort all 776e29dde8SFrançois Tigeot * (eDP/LVDS) panels to the front of the connector list, instead of 786e29dde8SFrançois Tigeot * painstakingly trying to initialize them in the right order. 796e29dde8SFrançois Tigeot */ 806e29dde8SFrançois Tigeot void drm_helper_move_panel_connectors_to_head(struct drm_device *dev) 816e29dde8SFrançois Tigeot { 826e29dde8SFrançois Tigeot struct drm_connector *connector, *tmp; 836e29dde8SFrançois Tigeot struct list_head panel_list; 846e29dde8SFrançois Tigeot 856e29dde8SFrançois Tigeot INIT_LIST_HEAD(&panel_list); 866e29dde8SFrançois Tigeot 876e29dde8SFrançois Tigeot list_for_each_entry_safe(connector, tmp, 886e29dde8SFrançois Tigeot &dev->mode_config.connector_list, head) { 896e29dde8SFrançois Tigeot if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS || 906e29dde8SFrançois Tigeot connector->connector_type == DRM_MODE_CONNECTOR_eDP) 916e29dde8SFrançois Tigeot list_move_tail(&connector->head, &panel_list); 926e29dde8SFrançois Tigeot } 936e29dde8SFrançois Tigeot 946e29dde8SFrançois Tigeot list_splice(&panel_list, &dev->mode_config.connector_list); 956e29dde8SFrançois Tigeot } 966e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_helper_move_panel_connectors_to_head); 976e29dde8SFrançois Tigeot 985718399fSFrançois Tigeot /** 995718399fSFrançois Tigeot * drm_helper_encoder_in_use - check if a given encoder is in use 1005718399fSFrançois Tigeot * @encoder: encoder to check 1015718399fSFrançois Tigeot * 102ba55f2f5SFrançois Tigeot * Checks whether @encoder is with the current mode setting output configuration 103ba55f2f5SFrançois Tigeot * in use by any connector. This doesn't mean that it is actually enabled since 104ba55f2f5SFrançois Tigeot * the DPMS state is tracked separately. 1055718399fSFrançois Tigeot * 106ba55f2f5SFrançois Tigeot * Returns: 107ba55f2f5SFrançois Tigeot * True if @encoder is used, false otherwise. 1085718399fSFrançois Tigeot */ 1095718399fSFrançois Tigeot bool drm_helper_encoder_in_use(struct drm_encoder *encoder) 1105718399fSFrançois Tigeot { 1115718399fSFrançois Tigeot struct drm_connector *connector; 1125718399fSFrançois Tigeot struct drm_device *dev = encoder->dev; 113ba55f2f5SFrançois Tigeot 114*a05eeebfSFrançois Tigeot /* 115*a05eeebfSFrançois Tigeot * We can expect this mutex to be locked if we are not panicking. 116*a05eeebfSFrançois Tigeot * Locking is currently fubar in the panic handler. 117*a05eeebfSFrançois Tigeot */ 118*a05eeebfSFrançois Tigeot #if 0 119*a05eeebfSFrançois Tigeot if (!oops_in_progress) { 120ba55f2f5SFrançois Tigeot WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); 121ba55f2f5SFrançois Tigeot WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); 122*a05eeebfSFrançois Tigeot } 123*a05eeebfSFrançois Tigeot #endif 124*a05eeebfSFrançois Tigeot 125*a05eeebfSFrançois Tigeot drm_for_each_connector(connector, dev) 1265718399fSFrançois Tigeot if (connector->encoder == encoder) 1275718399fSFrançois Tigeot return true; 1285718399fSFrançois Tigeot return false; 1295718399fSFrançois Tigeot } 1306e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_helper_encoder_in_use); 1315718399fSFrançois Tigeot 1325718399fSFrançois Tigeot /** 1335718399fSFrançois Tigeot * drm_helper_crtc_in_use - check if a given CRTC is in a mode_config 1345718399fSFrançois Tigeot * @crtc: CRTC to check 1355718399fSFrançois Tigeot * 136ba55f2f5SFrançois Tigeot * Checks whether @crtc is with the current mode setting output configuration 137ba55f2f5SFrançois Tigeot * in use by any connector. This doesn't mean that it is actually enabled since 138ba55f2f5SFrançois Tigeot * the DPMS state is tracked separately. 1395718399fSFrançois Tigeot * 140ba55f2f5SFrançois Tigeot * Returns: 141ba55f2f5SFrançois Tigeot * True if @crtc is used, false otherwise. 1425718399fSFrançois Tigeot */ 1435718399fSFrançois Tigeot bool drm_helper_crtc_in_use(struct drm_crtc *crtc) 1445718399fSFrançois Tigeot { 1455718399fSFrançois Tigeot struct drm_encoder *encoder; 1465718399fSFrançois Tigeot struct drm_device *dev = crtc->dev; 147ba55f2f5SFrançois Tigeot 148*a05eeebfSFrançois Tigeot /* 149*a05eeebfSFrançois Tigeot * We can expect this mutex to be locked if we are not panicking. 150*a05eeebfSFrançois Tigeot * Locking is currently fubar in the panic handler. 151*a05eeebfSFrançois Tigeot */ 152*a05eeebfSFrançois Tigeot #if 0 153*a05eeebfSFrançois Tigeot if (!oops_in_progress) 154*a05eeebfSFrançois Tigeot #endif 155ba55f2f5SFrançois Tigeot WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); 156*a05eeebfSFrançois Tigeot 157*a05eeebfSFrançois Tigeot drm_for_each_encoder(encoder, dev) 1585718399fSFrançois Tigeot if (encoder->crtc == crtc && drm_helper_encoder_in_use(encoder)) 1595718399fSFrançois Tigeot return true; 1605718399fSFrançois Tigeot return false; 1615718399fSFrançois Tigeot } 1626e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_helper_crtc_in_use); 1635718399fSFrançois Tigeot 1645718399fSFrançois Tigeot static void 1655718399fSFrançois Tigeot drm_encoder_disable(struct drm_encoder *encoder) 1665718399fSFrançois Tigeot { 167477eb7f9SFrançois Tigeot const struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; 1685718399fSFrançois Tigeot 16919c468b4SFrançois Tigeot drm_bridge_disable(encoder->bridge); 170ba55f2f5SFrançois Tigeot 1715718399fSFrançois Tigeot if (encoder_funcs->disable) 1725718399fSFrançois Tigeot (*encoder_funcs->disable)(encoder); 1735718399fSFrançois Tigeot else 1745718399fSFrançois Tigeot (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); 175ba55f2f5SFrançois Tigeot 17619c468b4SFrançois Tigeot drm_bridge_post_disable(encoder->bridge); 1775718399fSFrançois Tigeot } 1785718399fSFrançois Tigeot 179ba55f2f5SFrançois Tigeot static void __drm_helper_disable_unused_functions(struct drm_device *dev) 1805718399fSFrançois Tigeot { 1815718399fSFrançois Tigeot struct drm_encoder *encoder; 1825718399fSFrançois Tigeot struct drm_crtc *crtc; 1835718399fSFrançois Tigeot 184ba55f2f5SFrançois Tigeot drm_warn_on_modeset_not_all_locked(dev); 1855718399fSFrançois Tigeot 186*a05eeebfSFrançois Tigeot drm_for_each_encoder(encoder, dev) { 1875718399fSFrançois Tigeot if (!drm_helper_encoder_in_use(encoder)) { 1885718399fSFrançois Tigeot drm_encoder_disable(encoder); 189ba55f2f5SFrançois Tigeot /* disconnect encoder from any connector */ 1905718399fSFrançois Tigeot encoder->crtc = NULL; 1915718399fSFrançois Tigeot } 1925718399fSFrançois Tigeot } 1935718399fSFrançois Tigeot 194*a05eeebfSFrançois Tigeot drm_for_each_crtc(crtc, dev) { 195477eb7f9SFrançois Tigeot const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; 1965718399fSFrançois Tigeot crtc->enabled = drm_helper_crtc_in_use(crtc); 1975718399fSFrançois Tigeot if (!crtc->enabled) { 1985718399fSFrançois Tigeot if (crtc_funcs->disable) 1995718399fSFrançois Tigeot (*crtc_funcs->disable)(crtc); 2005718399fSFrançois Tigeot else 2015718399fSFrançois Tigeot (*crtc_funcs->dpms)(crtc, DRM_MODE_DPMS_OFF); 202ba55f2f5SFrançois Tigeot crtc->primary->fb = NULL; 2035718399fSFrançois Tigeot } 2045718399fSFrançois Tigeot } 2055718399fSFrançois Tigeot } 206ba55f2f5SFrançois Tigeot 207ba55f2f5SFrançois Tigeot /** 208ba55f2f5SFrançois Tigeot * drm_helper_disable_unused_functions - disable unused objects 209ba55f2f5SFrançois Tigeot * @dev: DRM device 210ba55f2f5SFrançois Tigeot * 211ba55f2f5SFrançois Tigeot * This function walks through the entire mode setting configuration of @dev. It 212ba55f2f5SFrançois Tigeot * will remove any crtc links of unused encoders and encoder links of 213ba55f2f5SFrançois Tigeot * disconnected connectors. Then it will disable all unused encoders and crtcs 214ba55f2f5SFrançois Tigeot * either by calling their disable callback if available or by calling their 215ba55f2f5SFrançois Tigeot * dpms callback with DRM_MODE_DPMS_OFF. 216ba55f2f5SFrançois Tigeot */ 217ba55f2f5SFrançois Tigeot void drm_helper_disable_unused_functions(struct drm_device *dev) 218ba55f2f5SFrançois Tigeot { 219ba55f2f5SFrançois Tigeot drm_modeset_lock_all(dev); 220ba55f2f5SFrançois Tigeot __drm_helper_disable_unused_functions(dev); 221ba55f2f5SFrançois Tigeot drm_modeset_unlock_all(dev); 222ba55f2f5SFrançois Tigeot } 2236e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_helper_disable_unused_functions); 2245718399fSFrançois Tigeot 2255718399fSFrançois Tigeot /* 2265718399fSFrançois Tigeot * Check the CRTC we're going to map each output to vs. its current 2275718399fSFrançois Tigeot * CRTC. If they don't match, we have to disable the output and the CRTC 2285718399fSFrançois Tigeot * since the driver will have to re-route things. 2295718399fSFrançois Tigeot */ 2305718399fSFrançois Tigeot static void 2315718399fSFrançois Tigeot drm_crtc_prepare_encoders(struct drm_device *dev) 2325718399fSFrançois Tigeot { 233477eb7f9SFrançois Tigeot const struct drm_encoder_helper_funcs *encoder_funcs; 2345718399fSFrançois Tigeot struct drm_encoder *encoder; 2355718399fSFrançois Tigeot 236*a05eeebfSFrançois Tigeot drm_for_each_encoder(encoder, dev) { 2375718399fSFrançois Tigeot encoder_funcs = encoder->helper_private; 2385718399fSFrançois Tigeot /* Disable unused encoders */ 2395718399fSFrançois Tigeot if (encoder->crtc == NULL) 2405718399fSFrançois Tigeot drm_encoder_disable(encoder); 2415718399fSFrançois Tigeot /* Disable encoders whose CRTC is about to change */ 2425718399fSFrançois Tigeot if (encoder_funcs->get_crtc && 2435718399fSFrançois Tigeot encoder->crtc != (*encoder_funcs->get_crtc)(encoder)) 2445718399fSFrançois Tigeot drm_encoder_disable(encoder); 2455718399fSFrançois Tigeot } 2465718399fSFrançois Tigeot } 2475718399fSFrançois Tigeot 2485718399fSFrançois Tigeot /** 2496e29dde8SFrançois Tigeot * drm_crtc_helper_set_mode - internal helper to set a mode 2505718399fSFrançois Tigeot * @crtc: CRTC to program 2515718399fSFrançois Tigeot * @mode: mode to use 2526e29dde8SFrançois Tigeot * @x: horizontal offset into the surface 2536e29dde8SFrançois Tigeot * @y: vertical offset into the surface 2546e29dde8SFrançois Tigeot * @old_fb: old framebuffer, for cleanup 2555718399fSFrançois Tigeot * 2565718399fSFrançois Tigeot * Try to set @mode on @crtc. Give @crtc and its associated connectors a chance 2576e29dde8SFrançois Tigeot * to fixup or reject the mode prior to trying to set it. This is an internal 2586e29dde8SFrançois Tigeot * helper that drivers could e.g. use to update properties that require the 2596e29dde8SFrançois Tigeot * entire output pipe to be disabled and re-enabled in a new configuration. For 2606e29dde8SFrançois Tigeot * example for changing whether audio is enabled on a hdmi link or for changing 2616e29dde8SFrançois Tigeot * panel fitter or dither attributes. It is also called by the 2626e29dde8SFrançois Tigeot * drm_crtc_helper_set_config() helper function to drive the mode setting 2636e29dde8SFrançois Tigeot * sequence. 2645718399fSFrançois Tigeot * 265ba55f2f5SFrançois Tigeot * Returns: 266ba55f2f5SFrançois Tigeot * True if the mode was set successfully, false otherwise. 2675718399fSFrançois Tigeot */ 2685718399fSFrançois Tigeot bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, 2695718399fSFrançois Tigeot struct drm_display_mode *mode, 2705718399fSFrançois Tigeot int x, int y, 2715718399fSFrançois Tigeot struct drm_framebuffer *old_fb) 2725718399fSFrançois Tigeot { 2735718399fSFrançois Tigeot struct drm_device *dev = crtc->dev; 274477eb7f9SFrançois Tigeot struct drm_display_mode *adjusted_mode, saved_mode, saved_hwmode; 275477eb7f9SFrançois Tigeot const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; 276477eb7f9SFrançois Tigeot const struct drm_encoder_helper_funcs *encoder_funcs; 2775718399fSFrançois Tigeot int saved_x, saved_y; 2789edbd4a0SFrançois Tigeot bool saved_enabled; 2795718399fSFrançois Tigeot struct drm_encoder *encoder; 2805718399fSFrançois Tigeot bool ret = true; 2815718399fSFrançois Tigeot 282ba55f2f5SFrançois Tigeot drm_warn_on_modeset_not_all_locked(dev); 283ba55f2f5SFrançois Tigeot 2849edbd4a0SFrançois Tigeot saved_enabled = crtc->enabled; 2855718399fSFrançois Tigeot crtc->enabled = drm_helper_crtc_in_use(crtc); 2865718399fSFrançois Tigeot if (!crtc->enabled) 2875718399fSFrançois Tigeot return true; 2885718399fSFrançois Tigeot 2895718399fSFrançois Tigeot adjusted_mode = drm_mode_duplicate(dev, mode); 2909edbd4a0SFrançois Tigeot if (!adjusted_mode) { 2919edbd4a0SFrançois Tigeot crtc->enabled = saved_enabled; 2925718399fSFrançois Tigeot return false; 2939edbd4a0SFrançois Tigeot } 2945718399fSFrançois Tigeot 2955718399fSFrançois Tigeot saved_mode = crtc->mode; 296477eb7f9SFrançois Tigeot saved_hwmode = crtc->hwmode; 2975718399fSFrançois Tigeot saved_x = crtc->x; 2985718399fSFrançois Tigeot saved_y = crtc->y; 2995718399fSFrançois Tigeot 3005718399fSFrançois Tigeot /* Update crtc values up front so the driver can rely on them for mode 3015718399fSFrançois Tigeot * setting. 3025718399fSFrançois Tigeot */ 3035718399fSFrançois Tigeot crtc->mode = *mode; 3045718399fSFrançois Tigeot crtc->x = x; 3055718399fSFrançois Tigeot crtc->y = y; 3065718399fSFrançois Tigeot 3075718399fSFrançois Tigeot /* Pass our mode to the connectors and the CRTC to give them a chance to 3085718399fSFrançois Tigeot * adjust it according to limitations or connector properties, and also 3095718399fSFrançois Tigeot * a chance to reject the mode entirely. 3105718399fSFrançois Tigeot */ 311*a05eeebfSFrançois Tigeot drm_for_each_encoder(encoder, dev) { 3125718399fSFrançois Tigeot 3135718399fSFrançois Tigeot if (encoder->crtc != crtc) 3145718399fSFrançois Tigeot continue; 315ba55f2f5SFrançois Tigeot 31619c468b4SFrançois Tigeot ret = drm_bridge_mode_fixup(encoder->bridge, 31719c468b4SFrançois Tigeot mode, adjusted_mode); 318ba55f2f5SFrançois Tigeot if (!ret) { 319ba55f2f5SFrançois Tigeot DRM_DEBUG_KMS("Bridge fixup failed\n"); 320ba55f2f5SFrançois Tigeot goto done; 321ba55f2f5SFrançois Tigeot } 322ba55f2f5SFrançois Tigeot 3235718399fSFrançois Tigeot encoder_funcs = encoder->helper_private; 3245718399fSFrançois Tigeot if (!(ret = encoder_funcs->mode_fixup(encoder, mode, 3255718399fSFrançois Tigeot adjusted_mode))) { 3266e29dde8SFrançois Tigeot DRM_DEBUG_KMS("Encoder fixup failed\n"); 3275718399fSFrançois Tigeot goto done; 3285718399fSFrançois Tigeot } 3295718399fSFrançois Tigeot } 3305718399fSFrançois Tigeot 3315718399fSFrançois Tigeot if (!(ret = crtc_funcs->mode_fixup(crtc, mode, adjusted_mode))) { 3326e29dde8SFrançois Tigeot DRM_DEBUG_KMS("CRTC fixup failed\n"); 3335718399fSFrançois Tigeot goto done; 3345718399fSFrançois Tigeot } 3355718399fSFrançois Tigeot DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); 3365718399fSFrançois Tigeot 337477eb7f9SFrançois Tigeot crtc->hwmode = *adjusted_mode; 338477eb7f9SFrançois Tigeot 3395718399fSFrançois Tigeot /* Prepare the encoders and CRTCs before setting the mode. */ 340*a05eeebfSFrançois Tigeot drm_for_each_encoder(encoder, dev) { 3415718399fSFrançois Tigeot 3425718399fSFrançois Tigeot if (encoder->crtc != crtc) 3435718399fSFrançois Tigeot continue; 344ba55f2f5SFrançois Tigeot 34519c468b4SFrançois Tigeot drm_bridge_disable(encoder->bridge); 346ba55f2f5SFrançois Tigeot 3475718399fSFrançois Tigeot encoder_funcs = encoder->helper_private; 3485718399fSFrançois Tigeot /* Disable the encoders as the first thing we do. */ 3495718399fSFrançois Tigeot encoder_funcs->prepare(encoder); 350ba55f2f5SFrançois Tigeot 35119c468b4SFrançois Tigeot drm_bridge_post_disable(encoder->bridge); 3525718399fSFrançois Tigeot } 3535718399fSFrançois Tigeot 3545718399fSFrançois Tigeot drm_crtc_prepare_encoders(dev); 3555718399fSFrançois Tigeot 3565718399fSFrançois Tigeot crtc_funcs->prepare(crtc); 3575718399fSFrançois Tigeot 3585718399fSFrançois Tigeot /* Set up the DPLL and any encoders state that needs to adjust or depend 3595718399fSFrançois Tigeot * on the DPLL. 3605718399fSFrançois Tigeot */ 3615718399fSFrançois Tigeot ret = !crtc_funcs->mode_set(crtc, mode, adjusted_mode, x, y, old_fb); 3625718399fSFrançois Tigeot if (!ret) 3635718399fSFrançois Tigeot goto done; 3645718399fSFrançois Tigeot 365*a05eeebfSFrançois Tigeot drm_for_each_encoder(encoder, dev) { 3665718399fSFrançois Tigeot 3675718399fSFrançois Tigeot if (encoder->crtc != crtc) 3685718399fSFrançois Tigeot continue; 3695718399fSFrançois Tigeot 3705718399fSFrançois Tigeot DRM_DEBUG_KMS("[ENCODER:%d:%s] set [MODE:%d:%s]\n", 371ba55f2f5SFrançois Tigeot encoder->base.id, encoder->name, 3725718399fSFrançois Tigeot mode->base.id, mode->name); 3735718399fSFrançois Tigeot encoder_funcs = encoder->helper_private; 3745718399fSFrançois Tigeot encoder_funcs->mode_set(encoder, mode, adjusted_mode); 375ba55f2f5SFrançois Tigeot 37619c468b4SFrançois Tigeot drm_bridge_mode_set(encoder->bridge, mode, adjusted_mode); 3775718399fSFrançois Tigeot } 3785718399fSFrançois Tigeot 3795718399fSFrançois Tigeot /* Now enable the clocks, plane, pipe, and connectors that we set up. */ 3805718399fSFrançois Tigeot crtc_funcs->commit(crtc); 3815718399fSFrançois Tigeot 382*a05eeebfSFrançois Tigeot drm_for_each_encoder(encoder, dev) { 3835718399fSFrançois Tigeot 3845718399fSFrançois Tigeot if (encoder->crtc != crtc) 3855718399fSFrançois Tigeot continue; 3865718399fSFrançois Tigeot 38719c468b4SFrançois Tigeot drm_bridge_pre_enable(encoder->bridge); 388ba55f2f5SFrançois Tigeot 3895718399fSFrançois Tigeot encoder_funcs = encoder->helper_private; 3905718399fSFrançois Tigeot encoder_funcs->commit(encoder); 3915718399fSFrançois Tigeot 39219c468b4SFrançois Tigeot drm_bridge_enable(encoder->bridge); 3935718399fSFrançois Tigeot } 3945718399fSFrançois Tigeot 3955718399fSFrançois Tigeot /* Calculate and store various constants which 3965718399fSFrançois Tigeot * are later needed by vblank and swap-completion 3975718399fSFrançois Tigeot * timestamping. They are derived from true hwmode. 3985718399fSFrançois Tigeot */ 3999edbd4a0SFrançois Tigeot drm_calc_timestamping_constants(crtc, &crtc->hwmode); 4005718399fSFrançois Tigeot 4015718399fSFrançois Tigeot /* FIXME: add subpixel order */ 4025718399fSFrançois Tigeot done: 4035718399fSFrançois Tigeot drm_mode_destroy(dev, adjusted_mode); 4045718399fSFrançois Tigeot if (!ret) { 4059edbd4a0SFrançois Tigeot crtc->enabled = saved_enabled; 4065718399fSFrançois Tigeot crtc->mode = saved_mode; 407477eb7f9SFrançois Tigeot crtc->hwmode = saved_hwmode; 4085718399fSFrançois Tigeot crtc->x = saved_x; 4095718399fSFrançois Tigeot crtc->y = saved_y; 4105718399fSFrançois Tigeot } 4115718399fSFrançois Tigeot 4125718399fSFrançois Tigeot return ret; 4135718399fSFrançois Tigeot } 4146e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_crtc_helper_set_mode); 4156e29dde8SFrançois Tigeot 416ba55f2f5SFrançois Tigeot static void 4175718399fSFrançois Tigeot drm_crtc_helper_disable(struct drm_crtc *crtc) 4185718399fSFrançois Tigeot { 4195718399fSFrançois Tigeot struct drm_device *dev = crtc->dev; 4205718399fSFrançois Tigeot struct drm_connector *connector; 4215718399fSFrançois Tigeot struct drm_encoder *encoder; 4225718399fSFrançois Tigeot 4235718399fSFrançois Tigeot /* Decouple all encoders and their attached connectors from this crtc */ 424*a05eeebfSFrançois Tigeot drm_for_each_encoder(encoder, dev) { 4255718399fSFrançois Tigeot if (encoder->crtc != crtc) 4265718399fSFrançois Tigeot continue; 4275718399fSFrançois Tigeot 428*a05eeebfSFrançois Tigeot drm_for_each_connector(connector, dev) { 4295718399fSFrançois Tigeot if (connector->encoder != encoder) 4305718399fSFrançois Tigeot continue; 4315718399fSFrançois Tigeot 4325718399fSFrançois Tigeot connector->encoder = NULL; 4339edbd4a0SFrançois Tigeot 4349edbd4a0SFrançois Tigeot /* 4359edbd4a0SFrançois Tigeot * drm_helper_disable_unused_functions() ought to be 4369edbd4a0SFrançois Tigeot * doing this, but since we've decoupled the encoder 4379edbd4a0SFrançois Tigeot * from the connector above, the required connection 4389edbd4a0SFrançois Tigeot * between them is henceforth no longer available. 4399edbd4a0SFrançois Tigeot */ 4409edbd4a0SFrançois Tigeot connector->dpms = DRM_MODE_DPMS_OFF; 4415718399fSFrançois Tigeot } 4425718399fSFrançois Tigeot } 4435718399fSFrançois Tigeot 444ba55f2f5SFrançois Tigeot __drm_helper_disable_unused_functions(dev); 4455718399fSFrançois Tigeot } 4465718399fSFrançois Tigeot 4475718399fSFrançois Tigeot /** 4485718399fSFrançois Tigeot * drm_crtc_helper_set_config - set a new config from userspace 4496e29dde8SFrançois Tigeot * @set: mode set configuration 4505718399fSFrançois Tigeot * 4516e29dde8SFrançois Tigeot * Setup a new configuration, provided by the upper layers (either an ioctl call 452ba55f2f5SFrançois Tigeot * from userspace or internally e.g. from the fbdev support code) in @set, and 4536e29dde8SFrançois Tigeot * enable it. This is the main helper functions for drivers that implement 4546e29dde8SFrançois Tigeot * kernel mode setting with the crtc helper functions and the assorted 4556e29dde8SFrançois Tigeot * ->prepare(), ->modeset() and ->commit() helper callbacks. 4565718399fSFrançois Tigeot * 457ba55f2f5SFrançois Tigeot * Returns: 458ba55f2f5SFrançois Tigeot * Returns 0 on success, negative errno numbers on failure. 4595718399fSFrançois Tigeot */ 4605718399fSFrançois Tigeot int drm_crtc_helper_set_config(struct drm_mode_set *set) 4615718399fSFrançois Tigeot { 4625718399fSFrançois Tigeot struct drm_device *dev; 4639edbd4a0SFrançois Tigeot struct drm_crtc *new_crtc; 4645718399fSFrançois Tigeot struct drm_encoder *save_encoders, *new_encoder, *encoder; 4655718399fSFrançois Tigeot bool mode_changed = false; /* if true do a full mode set */ 4665718399fSFrançois Tigeot bool fb_changed = false; /* if true and !mode_changed just do a flip */ 4675718399fSFrançois Tigeot struct drm_connector *save_connectors, *connector; 4685718399fSFrançois Tigeot int count = 0, ro, fail = 0; 469477eb7f9SFrançois Tigeot const struct drm_crtc_helper_funcs *crtc_funcs; 4705718399fSFrançois Tigeot struct drm_mode_set save_set; 4716e29dde8SFrançois Tigeot int ret; 4725718399fSFrançois Tigeot int i; 4735718399fSFrançois Tigeot 4745718399fSFrançois Tigeot DRM_DEBUG_KMS("\n"); 4755718399fSFrançois Tigeot 4769edbd4a0SFrançois Tigeot BUG_ON(!set); 4779edbd4a0SFrançois Tigeot BUG_ON(!set->crtc); 4789edbd4a0SFrançois Tigeot BUG_ON(!set->crtc->helper_private); 4795718399fSFrançois Tigeot 4809edbd4a0SFrançois Tigeot /* Enforce sane interface api - has been abused by the fb helper. */ 4819edbd4a0SFrançois Tigeot BUG_ON(!set->mode && set->fb); 4829edbd4a0SFrançois Tigeot BUG_ON(set->fb && set->num_connectors == 0); 4835718399fSFrançois Tigeot 4845718399fSFrançois Tigeot crtc_funcs = set->crtc->helper_private; 4855718399fSFrançois Tigeot 4865718399fSFrançois Tigeot if (!set->mode) 4875718399fSFrançois Tigeot set->fb = NULL; 4885718399fSFrançois Tigeot 4895718399fSFrançois Tigeot if (set->fb) { 4905718399fSFrançois Tigeot DRM_DEBUG_KMS("[CRTC:%d] [FB:%d] #connectors=%d (x y) (%i %i)\n", 4915718399fSFrançois Tigeot set->crtc->base.id, set->fb->base.id, 4925718399fSFrançois Tigeot (int)set->num_connectors, set->x, set->y); 4935718399fSFrançois Tigeot } else { 4945718399fSFrançois Tigeot DRM_DEBUG_KMS("[CRTC:%d] [NOFB]\n", set->crtc->base.id); 495ba55f2f5SFrançois Tigeot drm_crtc_helper_disable(set->crtc); 496ba55f2f5SFrançois Tigeot return 0; 4975718399fSFrançois Tigeot } 4985718399fSFrançois Tigeot 4995718399fSFrançois Tigeot dev = set->crtc->dev; 5005718399fSFrançois Tigeot 501ba55f2f5SFrançois Tigeot drm_warn_on_modeset_not_all_locked(dev); 502ba55f2f5SFrançois Tigeot 5039edbd4a0SFrançois Tigeot /* 5049edbd4a0SFrançois Tigeot * Allocate space for the backup of all (non-pointer) encoder and 5059edbd4a0SFrançois Tigeot * connector data. 5069edbd4a0SFrançois Tigeot */ 5074dbb207bSFrançois Tigeot save_encoders = kzalloc(dev->mode_config.num_encoder * 5084dbb207bSFrançois Tigeot sizeof(struct drm_encoder), GFP_KERNEL); 5099edbd4a0SFrançois Tigeot if (!save_encoders) 5104dbb207bSFrançois Tigeot return -ENOMEM; 5114dbb207bSFrançois Tigeot 5124dbb207bSFrançois Tigeot save_connectors = kzalloc(dev->mode_config.num_connector * 5134dbb207bSFrançois Tigeot sizeof(struct drm_connector), GFP_KERNEL); 5144dbb207bSFrançois Tigeot if (!save_connectors) { 5154dbb207bSFrançois Tigeot kfree(save_encoders); 5164dbb207bSFrançois Tigeot return -ENOMEM; 5174dbb207bSFrançois Tigeot } 5185718399fSFrançois Tigeot 5199edbd4a0SFrançois Tigeot /* 5209edbd4a0SFrançois Tigeot * Copy data. Note that driver private data is not affected. 5215718399fSFrançois Tigeot * Should anything bad happen only the expected state is 5225718399fSFrançois Tigeot * restored, not the drivers personal bookkeeping. 5235718399fSFrançois Tigeot */ 5245718399fSFrançois Tigeot count = 0; 525*a05eeebfSFrançois Tigeot drm_for_each_encoder(encoder, dev) { 5265718399fSFrançois Tigeot save_encoders[count++] = *encoder; 5275718399fSFrançois Tigeot } 5285718399fSFrançois Tigeot 5295718399fSFrançois Tigeot count = 0; 530*a05eeebfSFrançois Tigeot drm_for_each_connector(connector, dev) { 5315718399fSFrançois Tigeot save_connectors[count++] = *connector; 5325718399fSFrançois Tigeot } 5335718399fSFrançois Tigeot 5345718399fSFrançois Tigeot save_set.crtc = set->crtc; 5355718399fSFrançois Tigeot save_set.mode = &set->crtc->mode; 5365718399fSFrançois Tigeot save_set.x = set->crtc->x; 5375718399fSFrançois Tigeot save_set.y = set->crtc->y; 538ba55f2f5SFrançois Tigeot save_set.fb = set->crtc->primary->fb; 5395718399fSFrançois Tigeot 5405718399fSFrançois Tigeot /* We should be able to check here if the fb has the same properties 5415718399fSFrançois Tigeot * and then just flip_or_move it */ 542ba55f2f5SFrançois Tigeot if (set->crtc->primary->fb != set->fb) { 5435718399fSFrançois Tigeot /* If we have no fb then treat it as a full mode set */ 544ba55f2f5SFrançois Tigeot if (set->crtc->primary->fb == NULL) { 5455718399fSFrançois Tigeot DRM_DEBUG_KMS("crtc has no fb, full mode set\n"); 5465718399fSFrançois Tigeot mode_changed = true; 5475718399fSFrançois Tigeot } else if (set->fb == NULL) { 5485718399fSFrançois Tigeot mode_changed = true; 5494dbb207bSFrançois Tigeot } else if (set->fb->pixel_format != 550ba55f2f5SFrançois Tigeot set->crtc->primary->fb->pixel_format) { 5514dbb207bSFrançois Tigeot mode_changed = true; 5525718399fSFrançois Tigeot } else 5535718399fSFrançois Tigeot fb_changed = true; 5545718399fSFrançois Tigeot } 5555718399fSFrançois Tigeot 5565718399fSFrançois Tigeot if (set->x != set->crtc->x || set->y != set->crtc->y) 5575718399fSFrançois Tigeot fb_changed = true; 5585718399fSFrançois Tigeot 5595718399fSFrançois Tigeot if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) { 5605718399fSFrançois Tigeot DRM_DEBUG_KMS("modes are different, full mode set\n"); 5615718399fSFrançois Tigeot drm_mode_debug_printmodeline(&set->crtc->mode); 5625718399fSFrançois Tigeot drm_mode_debug_printmodeline(set->mode); 5635718399fSFrançois Tigeot mode_changed = true; 5645718399fSFrançois Tigeot } 5655718399fSFrançois Tigeot 5665718399fSFrançois Tigeot /* a) traverse passed in connector list and get encoders for them */ 5675718399fSFrançois Tigeot count = 0; 568*a05eeebfSFrançois Tigeot drm_for_each_connector(connector, dev) { 569477eb7f9SFrançois Tigeot const struct drm_connector_helper_funcs *connector_funcs = 5705718399fSFrançois Tigeot connector->helper_private; 5715718399fSFrançois Tigeot new_encoder = connector->encoder; 5725718399fSFrançois Tigeot for (ro = 0; ro < set->num_connectors; ro++) { 5735718399fSFrançois Tigeot if (set->connectors[ro] == connector) { 5745718399fSFrançois Tigeot new_encoder = connector_funcs->best_encoder(connector); 5755718399fSFrançois Tigeot /* if we can't get an encoder for a connector 5765718399fSFrançois Tigeot we are setting now - then fail */ 5775718399fSFrançois Tigeot if (new_encoder == NULL) 5785718399fSFrançois Tigeot /* don't break so fail path works correct */ 5795718399fSFrançois Tigeot fail = 1; 5809edbd4a0SFrançois Tigeot 5819edbd4a0SFrançois Tigeot if (connector->dpms != DRM_MODE_DPMS_ON) { 5829edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("connector dpms not on, full mode switch\n"); 5839edbd4a0SFrançois Tigeot mode_changed = true; 5849edbd4a0SFrançois Tigeot } 585ba55f2f5SFrançois Tigeot 586ba55f2f5SFrançois Tigeot break; 5875718399fSFrançois Tigeot } 5885718399fSFrançois Tigeot } 5895718399fSFrançois Tigeot 5905718399fSFrançois Tigeot if (new_encoder != connector->encoder) { 5915718399fSFrançois Tigeot DRM_DEBUG_KMS("encoder changed, full mode switch\n"); 5925718399fSFrançois Tigeot mode_changed = true; 5935718399fSFrançois Tigeot /* If the encoder is reused for another connector, then 5945718399fSFrançois Tigeot * the appropriate crtc will be set later. 5955718399fSFrançois Tigeot */ 5965718399fSFrançois Tigeot if (connector->encoder) 5975718399fSFrançois Tigeot connector->encoder->crtc = NULL; 5985718399fSFrançois Tigeot connector->encoder = new_encoder; 5995718399fSFrançois Tigeot } 6005718399fSFrançois Tigeot } 6015718399fSFrançois Tigeot 6025718399fSFrançois Tigeot if (fail) { 6035718399fSFrançois Tigeot ret = -EINVAL; 6045718399fSFrançois Tigeot goto fail; 6055718399fSFrançois Tigeot } 6065718399fSFrançois Tigeot 6075718399fSFrançois Tigeot count = 0; 608*a05eeebfSFrançois Tigeot drm_for_each_connector(connector, dev) { 6095718399fSFrançois Tigeot if (!connector->encoder) 6105718399fSFrançois Tigeot continue; 6115718399fSFrançois Tigeot 6125718399fSFrançois Tigeot if (connector->encoder->crtc == set->crtc) 6135718399fSFrançois Tigeot new_crtc = NULL; 6145718399fSFrançois Tigeot else 6155718399fSFrançois Tigeot new_crtc = connector->encoder->crtc; 6165718399fSFrançois Tigeot 6175718399fSFrançois Tigeot for (ro = 0; ro < set->num_connectors; ro++) { 6185718399fSFrançois Tigeot if (set->connectors[ro] == connector) 6195718399fSFrançois Tigeot new_crtc = set->crtc; 6205718399fSFrançois Tigeot } 6215718399fSFrançois Tigeot 6225718399fSFrançois Tigeot /* Make sure the new CRTC will work with the encoder */ 6235718399fSFrançois Tigeot if (new_crtc && 6245718399fSFrançois Tigeot !drm_encoder_crtc_ok(connector->encoder, new_crtc)) { 6255718399fSFrançois Tigeot ret = -EINVAL; 6265718399fSFrançois Tigeot goto fail; 6275718399fSFrançois Tigeot } 6285718399fSFrançois Tigeot if (new_crtc != connector->encoder->crtc) { 6295718399fSFrançois Tigeot DRM_DEBUG_KMS("crtc changed, full mode switch\n"); 6305718399fSFrançois Tigeot mode_changed = true; 6315718399fSFrançois Tigeot connector->encoder->crtc = new_crtc; 6325718399fSFrançois Tigeot } 6335718399fSFrançois Tigeot if (new_crtc) { 6345718399fSFrançois Tigeot DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d]\n", 635ba55f2f5SFrançois Tigeot connector->base.id, connector->name, 6365718399fSFrançois Tigeot new_crtc->base.id); 6375718399fSFrançois Tigeot } else { 6385718399fSFrançois Tigeot DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [NOCRTC]\n", 639ba55f2f5SFrançois Tigeot connector->base.id, connector->name); 6405718399fSFrançois Tigeot } 6415718399fSFrançois Tigeot } 6425718399fSFrançois Tigeot 6435718399fSFrançois Tigeot /* mode_set_base is not a required function */ 6445718399fSFrançois Tigeot if (fb_changed && !crtc_funcs->mode_set_base) 6455718399fSFrançois Tigeot mode_changed = true; 6465718399fSFrançois Tigeot 6475718399fSFrançois Tigeot if (mode_changed) { 6489edbd4a0SFrançois Tigeot if (drm_helper_crtc_in_use(set->crtc)) { 6495718399fSFrançois Tigeot DRM_DEBUG_KMS("attempting to set mode from" 6505718399fSFrançois Tigeot " userspace\n"); 6515718399fSFrançois Tigeot drm_mode_debug_printmodeline(set->mode); 652ba55f2f5SFrançois Tigeot set->crtc->primary->fb = set->fb; 6535718399fSFrançois Tigeot if (!drm_crtc_helper_set_mode(set->crtc, set->mode, 6545718399fSFrançois Tigeot set->x, set->y, 6559edbd4a0SFrançois Tigeot save_set.fb)) { 6565718399fSFrançois Tigeot DRM_ERROR("failed to set mode on [CRTC:%d]\n", 6575718399fSFrançois Tigeot set->crtc->base.id); 658ba55f2f5SFrançois Tigeot set->crtc->primary->fb = save_set.fb; 6595718399fSFrançois Tigeot ret = -EINVAL; 6605718399fSFrançois Tigeot goto fail; 6615718399fSFrançois Tigeot } 6625718399fSFrançois Tigeot DRM_DEBUG_KMS("Setting connector DPMS state to on\n"); 6635718399fSFrançois Tigeot for (i = 0; i < set->num_connectors; i++) { 6645718399fSFrançois Tigeot DRM_DEBUG_KMS("\t[CONNECTOR:%d:%s] set DPMS on\n", set->connectors[i]->base.id, 665ba55f2f5SFrançois Tigeot set->connectors[i]->name); 6666e29dde8SFrançois Tigeot set->connectors[i]->funcs->dpms(set->connectors[i], DRM_MODE_DPMS_ON); 6675718399fSFrançois Tigeot } 6685718399fSFrançois Tigeot } 669ba55f2f5SFrançois Tigeot __drm_helper_disable_unused_functions(dev); 6705718399fSFrançois Tigeot } else if (fb_changed) { 6715718399fSFrançois Tigeot set->crtc->x = set->x; 6725718399fSFrançois Tigeot set->crtc->y = set->y; 673ba55f2f5SFrançois Tigeot set->crtc->primary->fb = set->fb; 6745718399fSFrançois Tigeot ret = crtc_funcs->mode_set_base(set->crtc, 6759edbd4a0SFrançois Tigeot set->x, set->y, save_set.fb); 6765718399fSFrançois Tigeot if (ret != 0) { 6779edbd4a0SFrançois Tigeot set->crtc->x = save_set.x; 6789edbd4a0SFrançois Tigeot set->crtc->y = save_set.y; 679ba55f2f5SFrançois Tigeot set->crtc->primary->fb = save_set.fb; 6805718399fSFrançois Tigeot goto fail; 6815718399fSFrançois Tigeot } 6825718399fSFrançois Tigeot } 6835718399fSFrançois Tigeot 684158486a6SFrançois Tigeot kfree(save_connectors); 685158486a6SFrançois Tigeot kfree(save_encoders); 6865718399fSFrançois Tigeot return 0; 6875718399fSFrançois Tigeot 6885718399fSFrançois Tigeot fail: 6895718399fSFrançois Tigeot /* Restore all previous data. */ 6905718399fSFrançois Tigeot count = 0; 691*a05eeebfSFrançois Tigeot drm_for_each_encoder(encoder, dev) { 6925718399fSFrançois Tigeot *encoder = save_encoders[count++]; 6935718399fSFrançois Tigeot } 6945718399fSFrançois Tigeot 6955718399fSFrançois Tigeot count = 0; 696*a05eeebfSFrançois Tigeot drm_for_each_connector(connector, dev) { 6975718399fSFrançois Tigeot *connector = save_connectors[count++]; 6985718399fSFrançois Tigeot } 6995718399fSFrançois Tigeot 7005718399fSFrançois Tigeot /* Try to restore the config */ 7015718399fSFrançois Tigeot if (mode_changed && 7025718399fSFrançois Tigeot !drm_crtc_helper_set_mode(save_set.crtc, save_set.mode, save_set.x, 7035718399fSFrançois Tigeot save_set.y, save_set.fb)) 7045718399fSFrançois Tigeot DRM_ERROR("failed to restore config after modeset failure\n"); 7055718399fSFrançois Tigeot 7064dbb207bSFrançois Tigeot kfree(save_connectors); 7074dbb207bSFrançois Tigeot kfree(save_encoders); 7085718399fSFrançois Tigeot return ret; 7095718399fSFrançois Tigeot } 7106e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_crtc_helper_set_config); 7115718399fSFrançois Tigeot 7125718399fSFrançois Tigeot static int drm_helper_choose_encoder_dpms(struct drm_encoder *encoder) 7135718399fSFrançois Tigeot { 7145718399fSFrançois Tigeot int dpms = DRM_MODE_DPMS_OFF; 7155718399fSFrançois Tigeot struct drm_connector *connector; 7165718399fSFrançois Tigeot struct drm_device *dev = encoder->dev; 7175718399fSFrançois Tigeot 718*a05eeebfSFrançois Tigeot drm_for_each_connector(connector, dev) 7195718399fSFrançois Tigeot if (connector->encoder == encoder) 7205718399fSFrançois Tigeot if (connector->dpms < dpms) 7215718399fSFrançois Tigeot dpms = connector->dpms; 7225718399fSFrançois Tigeot return dpms; 7235718399fSFrançois Tigeot } 7245718399fSFrançois Tigeot 725ba55f2f5SFrançois Tigeot /* Helper which handles bridge ordering around encoder dpms */ 726ba55f2f5SFrançois Tigeot static void drm_helper_encoder_dpms(struct drm_encoder *encoder, int mode) 727ba55f2f5SFrançois Tigeot { 728ba55f2f5SFrançois Tigeot struct drm_bridge *bridge = encoder->bridge; 729477eb7f9SFrançois Tigeot const struct drm_encoder_helper_funcs *encoder_funcs; 730ba55f2f5SFrançois Tigeot 731ba55f2f5SFrançois Tigeot if (mode == DRM_MODE_DPMS_ON) 73219c468b4SFrançois Tigeot drm_bridge_pre_enable(bridge); 733ba55f2f5SFrançois Tigeot else 73419c468b4SFrançois Tigeot drm_bridge_disable(bridge); 735ba55f2f5SFrançois Tigeot 736ba55f2f5SFrançois Tigeot encoder_funcs = encoder->helper_private; 737ba55f2f5SFrançois Tigeot if (encoder_funcs->dpms) 738ba55f2f5SFrançois Tigeot encoder_funcs->dpms(encoder, mode); 739ba55f2f5SFrançois Tigeot 740ba55f2f5SFrançois Tigeot if (mode == DRM_MODE_DPMS_ON) 74119c468b4SFrançois Tigeot drm_bridge_enable(bridge); 742ba55f2f5SFrançois Tigeot else 74319c468b4SFrançois Tigeot drm_bridge_post_disable(bridge); 744ba55f2f5SFrançois Tigeot } 745ba55f2f5SFrançois Tigeot 7465718399fSFrançois Tigeot static int drm_helper_choose_crtc_dpms(struct drm_crtc *crtc) 7475718399fSFrançois Tigeot { 7485718399fSFrançois Tigeot int dpms = DRM_MODE_DPMS_OFF; 7495718399fSFrançois Tigeot struct drm_connector *connector; 7505718399fSFrançois Tigeot struct drm_device *dev = crtc->dev; 7515718399fSFrançois Tigeot 752*a05eeebfSFrançois Tigeot drm_for_each_connector(connector, dev) 7535718399fSFrançois Tigeot if (connector->encoder && connector->encoder->crtc == crtc) 7545718399fSFrançois Tigeot if (connector->dpms < dpms) 7555718399fSFrançois Tigeot dpms = connector->dpms; 7565718399fSFrançois Tigeot return dpms; 7575718399fSFrançois Tigeot } 7585718399fSFrançois Tigeot 7595718399fSFrançois Tigeot /** 7606e29dde8SFrançois Tigeot * drm_helper_connector_dpms() - connector dpms helper implementation 7616e29dde8SFrançois Tigeot * @connector: affected connector 7626e29dde8SFrançois Tigeot * @mode: DPMS mode 7635718399fSFrançois Tigeot * 7646e29dde8SFrançois Tigeot * This is the main helper function provided by the crtc helper framework for 7656e29dde8SFrançois Tigeot * implementing the DPMS connector attribute. It computes the new desired DPMS 7666e29dde8SFrançois Tigeot * state for all encoders and crtcs in the output mesh and calls the ->dpms() 7676e29dde8SFrançois Tigeot * callback provided by the driver appropriately. 768*a05eeebfSFrançois Tigeot * 769*a05eeebfSFrançois Tigeot * Returns: 770*a05eeebfSFrançois Tigeot * Always returns 0. 7715718399fSFrançois Tigeot */ 772*a05eeebfSFrançois Tigeot int drm_helper_connector_dpms(struct drm_connector *connector, int mode) 7735718399fSFrançois Tigeot { 7745718399fSFrançois Tigeot struct drm_encoder *encoder = connector->encoder; 7755718399fSFrançois Tigeot struct drm_crtc *crtc = encoder ? encoder->crtc : NULL; 776ba55f2f5SFrançois Tigeot int old_dpms, encoder_dpms = DRM_MODE_DPMS_OFF; 7775718399fSFrançois Tigeot 7785718399fSFrançois Tigeot if (mode == connector->dpms) 779*a05eeebfSFrançois Tigeot return 0; 7805718399fSFrançois Tigeot 7815718399fSFrançois Tigeot old_dpms = connector->dpms; 7825718399fSFrançois Tigeot connector->dpms = mode; 7835718399fSFrançois Tigeot 784ba55f2f5SFrançois Tigeot if (encoder) 785ba55f2f5SFrançois Tigeot encoder_dpms = drm_helper_choose_encoder_dpms(encoder); 786ba55f2f5SFrançois Tigeot 7875718399fSFrançois Tigeot /* from off to on, do crtc then encoder */ 7885718399fSFrançois Tigeot if (mode < old_dpms) { 7895718399fSFrançois Tigeot if (crtc) { 790477eb7f9SFrançois Tigeot const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; 7915718399fSFrançois Tigeot if (crtc_funcs->dpms) 7925718399fSFrançois Tigeot (*crtc_funcs->dpms) (crtc, 7935718399fSFrançois Tigeot drm_helper_choose_crtc_dpms(crtc)); 7945718399fSFrançois Tigeot } 795ba55f2f5SFrançois Tigeot if (encoder) 796ba55f2f5SFrançois Tigeot drm_helper_encoder_dpms(encoder, encoder_dpms); 7975718399fSFrançois Tigeot } 7985718399fSFrançois Tigeot 7995718399fSFrançois Tigeot /* from on to off, do encoder then crtc */ 8005718399fSFrançois Tigeot if (mode > old_dpms) { 801ba55f2f5SFrançois Tigeot if (encoder) 802ba55f2f5SFrançois Tigeot drm_helper_encoder_dpms(encoder, encoder_dpms); 8035718399fSFrançois Tigeot if (crtc) { 804477eb7f9SFrançois Tigeot const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; 8055718399fSFrançois Tigeot if (crtc_funcs->dpms) 8065718399fSFrançois Tigeot (*crtc_funcs->dpms) (crtc, 8075718399fSFrançois Tigeot drm_helper_choose_crtc_dpms(crtc)); 8085718399fSFrançois Tigeot } 8095718399fSFrançois Tigeot } 8105718399fSFrançois Tigeot 811*a05eeebfSFrançois Tigeot return 0; 8125718399fSFrançois Tigeot } 8136e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_helper_connector_dpms); 8145718399fSFrançois Tigeot 815ba55f2f5SFrançois Tigeot /** 816ba55f2f5SFrançois Tigeot * drm_helper_mode_fill_fb_struct - fill out framebuffer metadata 817ba55f2f5SFrançois Tigeot * @fb: drm_framebuffer object to fill out 818ba55f2f5SFrançois Tigeot * @mode_cmd: metadata from the userspace fb creation request 819ba55f2f5SFrançois Tigeot * 820ba55f2f5SFrançois Tigeot * This helper can be used in a drivers fb_create callback to pre-fill the fb's 821ba55f2f5SFrançois Tigeot * metadata fields. 822ba55f2f5SFrançois Tigeot */ 823ba55f2f5SFrançois Tigeot void drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, 8245718399fSFrançois Tigeot struct drm_mode_fb_cmd2 *mode_cmd) 8255718399fSFrançois Tigeot { 8265718399fSFrançois Tigeot int i; 8275718399fSFrançois Tigeot 8285718399fSFrançois Tigeot fb->width = mode_cmd->width; 8295718399fSFrançois Tigeot fb->height = mode_cmd->height; 8305718399fSFrançois Tigeot for (i = 0; i < 4; i++) { 8315718399fSFrançois Tigeot fb->pitches[i] = mode_cmd->pitches[i]; 8325718399fSFrançois Tigeot fb->offsets[i] = mode_cmd->offsets[i]; 833477eb7f9SFrançois Tigeot fb->modifier[i] = mode_cmd->modifier[i]; 8345718399fSFrançois Tigeot } 8355718399fSFrançois Tigeot drm_fb_get_bpp_depth(mode_cmd->pixel_format, &fb->depth, 8365718399fSFrançois Tigeot &fb->bits_per_pixel); 8375718399fSFrançois Tigeot fb->pixel_format = mode_cmd->pixel_format; 83824edb884SFrançois Tigeot fb->flags = mode_cmd->flags; 8395718399fSFrançois Tigeot } 8406e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct); 8415718399fSFrançois Tigeot 842ba55f2f5SFrançois Tigeot /** 843ba55f2f5SFrançois Tigeot * drm_helper_resume_force_mode - force-restore mode setting configuration 844ba55f2f5SFrançois Tigeot * @dev: drm_device which should be restored 845ba55f2f5SFrançois Tigeot * 846ba55f2f5SFrançois Tigeot * Drivers which use the mode setting helpers can use this function to 847ba55f2f5SFrançois Tigeot * force-restore the mode setting configuration e.g. on resume or when something 848ba55f2f5SFrançois Tigeot * else might have trampled over the hw state (like some overzealous old BIOSen 849ba55f2f5SFrançois Tigeot * tended to do). 850ba55f2f5SFrançois Tigeot * 851ba55f2f5SFrançois Tigeot * This helper doesn't provide a error return value since restoring the old 852ba55f2f5SFrançois Tigeot * config should never fail due to resource allocation issues since the driver 853ba55f2f5SFrançois Tigeot * has successfully set the restored configuration already. Hence this should 854ba55f2f5SFrançois Tigeot * boil down to the equivalent of a few dpms on calls, which also don't provide 855ba55f2f5SFrançois Tigeot * an error code. 856ba55f2f5SFrançois Tigeot * 857ba55f2f5SFrançois Tigeot * Drivers where simply restoring an old configuration again might fail (e.g. 858ba55f2f5SFrançois Tigeot * due to slight differences in allocating shared resources when the 859ba55f2f5SFrançois Tigeot * configuration is restored in a different order than when userspace set it up) 860ba55f2f5SFrançois Tigeot * need to use their own restore logic. 861ba55f2f5SFrançois Tigeot */ 862ba55f2f5SFrançois Tigeot void drm_helper_resume_force_mode(struct drm_device *dev) 8635718399fSFrançois Tigeot { 8645718399fSFrançois Tigeot struct drm_crtc *crtc; 8655718399fSFrançois Tigeot struct drm_encoder *encoder; 866477eb7f9SFrançois Tigeot const struct drm_crtc_helper_funcs *crtc_funcs; 867ba55f2f5SFrançois Tigeot int encoder_dpms; 868ba55f2f5SFrançois Tigeot bool ret; 8695718399fSFrançois Tigeot 870ba55f2f5SFrançois Tigeot drm_modeset_lock_all(dev); 871*a05eeebfSFrançois Tigeot drm_for_each_crtc(crtc, dev) { 8725718399fSFrançois Tigeot 8735718399fSFrançois Tigeot if (!crtc->enabled) 8745718399fSFrançois Tigeot continue; 8755718399fSFrançois Tigeot 8765718399fSFrançois Tigeot ret = drm_crtc_helper_set_mode(crtc, &crtc->mode, 877ba55f2f5SFrançois Tigeot crtc->x, crtc->y, crtc->primary->fb); 8785718399fSFrançois Tigeot 879ba55f2f5SFrançois Tigeot /* Restoring the old config should never fail! */ 8806e29dde8SFrançois Tigeot if (ret == false) 8815718399fSFrançois Tigeot DRM_ERROR("failed to set mode on crtc %p\n", crtc); 8825718399fSFrançois Tigeot 8835718399fSFrançois Tigeot /* Turn off outputs that were already powered off */ 8845718399fSFrançois Tigeot if (drm_helper_choose_crtc_dpms(crtc)) { 885*a05eeebfSFrançois Tigeot drm_for_each_encoder(encoder, dev) { 8865718399fSFrançois Tigeot 8875718399fSFrançois Tigeot if(encoder->crtc != crtc) 8885718399fSFrançois Tigeot continue; 8895718399fSFrançois Tigeot 890ba55f2f5SFrançois Tigeot encoder_dpms = drm_helper_choose_encoder_dpms( 891ba55f2f5SFrançois Tigeot encoder); 892ba55f2f5SFrançois Tigeot 893ba55f2f5SFrançois Tigeot drm_helper_encoder_dpms(encoder, encoder_dpms); 8945718399fSFrançois Tigeot } 8955718399fSFrançois Tigeot 8965718399fSFrançois Tigeot crtc_funcs = crtc->helper_private; 8975718399fSFrançois Tigeot if (crtc_funcs->dpms) 8985718399fSFrançois Tigeot (*crtc_funcs->dpms) (crtc, 8995718399fSFrançois Tigeot drm_helper_choose_crtc_dpms(crtc)); 9005718399fSFrançois Tigeot } 9015718399fSFrançois Tigeot } 902ba55f2f5SFrançois Tigeot 9035718399fSFrançois Tigeot /* disable the unused connectors while restoring the modesetting */ 904ba55f2f5SFrançois Tigeot __drm_helper_disable_unused_functions(dev); 905ba55f2f5SFrançois Tigeot drm_modeset_unlock_all(dev); 9065718399fSFrançois Tigeot } 9076e29dde8SFrançois Tigeot EXPORT_SYMBOL(drm_helper_resume_force_mode); 9082c9916cdSFrançois Tigeot 9092c9916cdSFrançois Tigeot /** 9102c9916cdSFrançois Tigeot * drm_helper_crtc_mode_set - mode_set implementation for atomic plane helpers 9112c9916cdSFrançois Tigeot * @crtc: DRM CRTC 9122c9916cdSFrançois Tigeot * @mode: DRM display mode which userspace requested 9132c9916cdSFrançois Tigeot * @adjusted_mode: DRM display mode adjusted by ->mode_fixup callbacks 9142c9916cdSFrançois Tigeot * @x: x offset of the CRTC scanout area on the underlying framebuffer 9152c9916cdSFrançois Tigeot * @y: y offset of the CRTC scanout area on the underlying framebuffer 9162c9916cdSFrançois Tigeot * @old_fb: previous framebuffer 9172c9916cdSFrançois Tigeot * 9182c9916cdSFrançois Tigeot * This function implements a callback useable as the ->mode_set callback 9192c9916cdSFrançois Tigeot * required by the crtc helpers. Besides the atomic plane helper functions for 9202c9916cdSFrançois Tigeot * the primary plane the driver must also provide the ->mode_set_nofb callback 9212c9916cdSFrançois Tigeot * to set up the crtc. 9222c9916cdSFrançois Tigeot * 9232c9916cdSFrançois Tigeot * This is a transitional helper useful for converting drivers to the atomic 9242c9916cdSFrançois Tigeot * interfaces. 9252c9916cdSFrançois Tigeot */ 9262c9916cdSFrançois Tigeot int drm_helper_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, 9272c9916cdSFrançois Tigeot struct drm_display_mode *adjusted_mode, int x, int y, 9282c9916cdSFrançois Tigeot struct drm_framebuffer *old_fb) 9292c9916cdSFrançois Tigeot { 9302c9916cdSFrançois Tigeot struct drm_crtc_state *crtc_state; 931477eb7f9SFrançois Tigeot const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; 9322c9916cdSFrançois Tigeot int ret; 9332c9916cdSFrançois Tigeot 9342c9916cdSFrançois Tigeot if (crtc->funcs->atomic_duplicate_state) 9352c9916cdSFrançois Tigeot crtc_state = crtc->funcs->atomic_duplicate_state(crtc); 93619c468b4SFrançois Tigeot else { 9372c9916cdSFrançois Tigeot crtc_state = kzalloc(sizeof(*crtc_state), GFP_KERNEL); 9382c9916cdSFrançois Tigeot if (!crtc_state) 9392c9916cdSFrançois Tigeot return -ENOMEM; 94019c468b4SFrançois Tigeot if (crtc->state) 94119c468b4SFrançois Tigeot __drm_atomic_helper_crtc_duplicate_state(crtc, crtc_state); 94219c468b4SFrançois Tigeot else 9432c9916cdSFrançois Tigeot crtc_state->crtc = crtc; 94419c468b4SFrançois Tigeot } 9452c9916cdSFrançois Tigeot 9462c9916cdSFrançois Tigeot crtc_state->planes_changed = true; 9472c9916cdSFrançois Tigeot crtc_state->mode_changed = true; 94819c468b4SFrançois Tigeot ret = drm_atomic_set_mode_for_crtc(crtc_state, mode); 94919c468b4SFrançois Tigeot if (ret) 95019c468b4SFrançois Tigeot goto out; 9512c9916cdSFrançois Tigeot drm_mode_copy(&crtc_state->adjusted_mode, adjusted_mode); 9522c9916cdSFrançois Tigeot 9532c9916cdSFrançois Tigeot if (crtc_funcs->atomic_check) { 9542c9916cdSFrançois Tigeot ret = crtc_funcs->atomic_check(crtc, crtc_state); 95519c468b4SFrançois Tigeot if (ret) 95619c468b4SFrançois Tigeot goto out; 9572c9916cdSFrançois Tigeot } 9582c9916cdSFrançois Tigeot 9592c9916cdSFrançois Tigeot swap(crtc->state, crtc_state); 9602c9916cdSFrançois Tigeot 9612c9916cdSFrançois Tigeot crtc_funcs->mode_set_nofb(crtc); 9622c9916cdSFrançois Tigeot 96319c468b4SFrançois Tigeot ret = drm_helper_crtc_mode_set_base(crtc, x, y, old_fb); 96419c468b4SFrançois Tigeot 96519c468b4SFrançois Tigeot out: 9662c9916cdSFrançois Tigeot if (crtc->funcs->atomic_destroy_state) 9672c9916cdSFrançois Tigeot crtc->funcs->atomic_destroy_state(crtc, crtc_state); 96819c468b4SFrançois Tigeot else { 96919c468b4SFrançois Tigeot __drm_atomic_helper_crtc_destroy_state(crtc, crtc_state); 9702c9916cdSFrançois Tigeot kfree(crtc_state); 9712c9916cdSFrançois Tigeot } 9722c9916cdSFrançois Tigeot 97319c468b4SFrançois Tigeot return ret; 9742c9916cdSFrançois Tigeot } 9752c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_helper_crtc_mode_set); 9762c9916cdSFrançois Tigeot 9772c9916cdSFrançois Tigeot /** 9782c9916cdSFrançois Tigeot * drm_helper_crtc_mode_set_base - mode_set_base implementation for atomic plane helpers 9792c9916cdSFrançois Tigeot * @crtc: DRM CRTC 9802c9916cdSFrançois Tigeot * @x: x offset of the CRTC scanout area on the underlying framebuffer 9812c9916cdSFrançois Tigeot * @y: y offset of the CRTC scanout area on the underlying framebuffer 9822c9916cdSFrançois Tigeot * @old_fb: previous framebuffer 9832c9916cdSFrançois Tigeot * 9842c9916cdSFrançois Tigeot * This function implements a callback useable as the ->mode_set_base used 9852c9916cdSFrançois Tigeot * required by the crtc helpers. The driver must provide the atomic plane helper 9862c9916cdSFrançois Tigeot * functions for the primary plane. 9872c9916cdSFrançois Tigeot * 9882c9916cdSFrançois Tigeot * This is a transitional helper useful for converting drivers to the atomic 9892c9916cdSFrançois Tigeot * interfaces. 9902c9916cdSFrançois Tigeot */ 9912c9916cdSFrançois Tigeot int drm_helper_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, 9922c9916cdSFrançois Tigeot struct drm_framebuffer *old_fb) 9932c9916cdSFrançois Tigeot { 9942c9916cdSFrançois Tigeot struct drm_plane_state *plane_state; 9952c9916cdSFrançois Tigeot struct drm_plane *plane = crtc->primary; 9962c9916cdSFrançois Tigeot 9972c9916cdSFrançois Tigeot if (plane->funcs->atomic_duplicate_state) 9982c9916cdSFrançois Tigeot plane_state = plane->funcs->atomic_duplicate_state(plane); 9992c9916cdSFrançois Tigeot else if (plane->state) 10002c9916cdSFrançois Tigeot plane_state = drm_atomic_helper_plane_duplicate_state(plane); 10012c9916cdSFrançois Tigeot else 10022c9916cdSFrançois Tigeot plane_state = kzalloc(sizeof(*plane_state), GFP_KERNEL); 10032c9916cdSFrançois Tigeot if (!plane_state) 10042c9916cdSFrançois Tigeot return -ENOMEM; 10052c9916cdSFrançois Tigeot plane_state->plane = plane; 10062c9916cdSFrançois Tigeot 10072c9916cdSFrançois Tigeot plane_state->crtc = crtc; 10082c9916cdSFrançois Tigeot drm_atomic_set_fb_for_plane(plane_state, crtc->primary->fb); 10092c9916cdSFrançois Tigeot plane_state->crtc_x = 0; 10102c9916cdSFrançois Tigeot plane_state->crtc_y = 0; 10112c9916cdSFrançois Tigeot plane_state->crtc_h = crtc->mode.vdisplay; 10122c9916cdSFrançois Tigeot plane_state->crtc_w = crtc->mode.hdisplay; 10132c9916cdSFrançois Tigeot plane_state->src_x = x << 16; 10142c9916cdSFrançois Tigeot plane_state->src_y = y << 16; 10152c9916cdSFrançois Tigeot plane_state->src_h = crtc->mode.vdisplay << 16; 10162c9916cdSFrançois Tigeot plane_state->src_w = crtc->mode.hdisplay << 16; 10172c9916cdSFrançois Tigeot 10182c9916cdSFrançois Tigeot return drm_plane_helper_commit(plane, plane_state, old_fb); 10192c9916cdSFrançois Tigeot } 10202c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_helper_crtc_mode_set_base); 1021