162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2006-2008 Intel Corporation 362306a36Sopenharmony_ci * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * DRM core CRTC related functions 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Permission to use, copy, modify, distribute, and sell this software and its 862306a36Sopenharmony_ci * documentation for any purpose is hereby granted without fee, provided that 962306a36Sopenharmony_ci * the above copyright notice appear in all copies and that both that copyright 1062306a36Sopenharmony_ci * notice and this permission notice appear in supporting documentation, and 1162306a36Sopenharmony_ci * that the name of the copyright holders not be used in advertising or 1262306a36Sopenharmony_ci * publicity pertaining to distribution of the software without specific, 1362306a36Sopenharmony_ci * written prior permission. The copyright holders make no representations 1462306a36Sopenharmony_ci * about the suitability of this software for any purpose. It is provided "as 1562306a36Sopenharmony_ci * is" without express or implied warranty. 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 1862306a36Sopenharmony_ci * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 1962306a36Sopenharmony_ci * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 2062306a36Sopenharmony_ci * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 2162306a36Sopenharmony_ci * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 2262306a36Sopenharmony_ci * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 2362306a36Sopenharmony_ci * OF THIS SOFTWARE. 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci * Authors: 2662306a36Sopenharmony_ci * Keith Packard 2762306a36Sopenharmony_ci * Eric Anholt <eric@anholt.net> 2862306a36Sopenharmony_ci * Dave Airlie <airlied@linux.ie> 2962306a36Sopenharmony_ci * Jesse Barnes <jesse.barnes@intel.com> 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include <linux/export.h> 3362306a36Sopenharmony_ci#include <linux/kernel.h> 3462306a36Sopenharmony_ci#include <linux/moduleparam.h> 3562306a36Sopenharmony_ci#include <linux/dynamic_debug.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include <drm/drm_atomic.h> 3862306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 3962306a36Sopenharmony_ci#include <drm/drm_atomic_uapi.h> 4062306a36Sopenharmony_ci#include <drm/drm_bridge.h> 4162306a36Sopenharmony_ci#include <drm/drm_crtc.h> 4262306a36Sopenharmony_ci#include <drm/drm_crtc_helper.h> 4362306a36Sopenharmony_ci#include <drm/drm_drv.h> 4462306a36Sopenharmony_ci#include <drm/drm_edid.h> 4562306a36Sopenharmony_ci#include <drm/drm_encoder.h> 4662306a36Sopenharmony_ci#include <drm/drm_fourcc.h> 4762306a36Sopenharmony_ci#include <drm/drm_framebuffer.h> 4862306a36Sopenharmony_ci#include <drm/drm_print.h> 4962306a36Sopenharmony_ci#include <drm/drm_vblank.h> 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#include "drm_crtc_helper_internal.h" 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ciDECLARE_DYNDBG_CLASSMAP(drm_debug_classes, DD_CLASS_TYPE_DISJOINT_BITS, 0, 5462306a36Sopenharmony_ci "DRM_UT_CORE", 5562306a36Sopenharmony_ci "DRM_UT_DRIVER", 5662306a36Sopenharmony_ci "DRM_UT_KMS", 5762306a36Sopenharmony_ci "DRM_UT_PRIME", 5862306a36Sopenharmony_ci "DRM_UT_ATOMIC", 5962306a36Sopenharmony_ci "DRM_UT_VBL", 6062306a36Sopenharmony_ci "DRM_UT_STATE", 6162306a36Sopenharmony_ci "DRM_UT_LEASE", 6262306a36Sopenharmony_ci "DRM_UT_DP", 6362306a36Sopenharmony_ci "DRM_UT_DRMRES"); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/** 6662306a36Sopenharmony_ci * DOC: overview 6762306a36Sopenharmony_ci * 6862306a36Sopenharmony_ci * The CRTC modeset helper library provides a default set_config implementation 6962306a36Sopenharmony_ci * in drm_crtc_helper_set_config(). Plus a few other convenience functions using 7062306a36Sopenharmony_ci * the same callbacks which drivers can use to e.g. restore the modeset 7162306a36Sopenharmony_ci * configuration on resume with drm_helper_resume_force_mode(). 7262306a36Sopenharmony_ci * 7362306a36Sopenharmony_ci * Note that this helper library doesn't track the current power state of CRTCs 7462306a36Sopenharmony_ci * and encoders. It can call callbacks like &drm_encoder_helper_funcs.dpms even 7562306a36Sopenharmony_ci * though the hardware is already in the desired state. This deficiency has been 7662306a36Sopenharmony_ci * fixed in the atomic helpers. 7762306a36Sopenharmony_ci * 7862306a36Sopenharmony_ci * The driver callbacks are mostly compatible with the atomic modeset helpers, 7962306a36Sopenharmony_ci * except for the handling of the primary plane: Atomic helpers require that the 8062306a36Sopenharmony_ci * primary plane is implemented as a real standalone plane and not directly tied 8162306a36Sopenharmony_ci * to the CRTC state. For easier transition this library provides functions to 8262306a36Sopenharmony_ci * implement the old semantics required by the CRTC helpers using the new plane 8362306a36Sopenharmony_ci * and atomic helper callbacks. 8462306a36Sopenharmony_ci * 8562306a36Sopenharmony_ci * Drivers are strongly urged to convert to the atomic helpers (by way of first 8662306a36Sopenharmony_ci * converting to the plane helpers). New drivers must not use these functions 8762306a36Sopenharmony_ci * but need to implement the atomic interface instead, potentially using the 8862306a36Sopenharmony_ci * atomic helpers for that. 8962306a36Sopenharmony_ci * 9062306a36Sopenharmony_ci * These legacy modeset helpers use the same function table structures as 9162306a36Sopenharmony_ci * all other modesetting helpers. See the documentation for struct 9262306a36Sopenharmony_ci * &drm_crtc_helper_funcs, &struct drm_encoder_helper_funcs and struct 9362306a36Sopenharmony_ci * &drm_connector_helper_funcs. 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/** 9762306a36Sopenharmony_ci * drm_helper_encoder_in_use - check if a given encoder is in use 9862306a36Sopenharmony_ci * @encoder: encoder to check 9962306a36Sopenharmony_ci * 10062306a36Sopenharmony_ci * Checks whether @encoder is with the current mode setting output configuration 10162306a36Sopenharmony_ci * in use by any connector. This doesn't mean that it is actually enabled since 10262306a36Sopenharmony_ci * the DPMS state is tracked separately. 10362306a36Sopenharmony_ci * 10462306a36Sopenharmony_ci * Returns: 10562306a36Sopenharmony_ci * True if @encoder is used, false otherwise. 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_cibool drm_helper_encoder_in_use(struct drm_encoder *encoder) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci struct drm_connector *connector; 11062306a36Sopenharmony_ci struct drm_connector_list_iter conn_iter; 11162306a36Sopenharmony_ci struct drm_device *dev = encoder->dev; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci WARN_ON(drm_drv_uses_atomic_modeset(dev)); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci /* 11662306a36Sopenharmony_ci * We can expect this mutex to be locked if we are not panicking. 11762306a36Sopenharmony_ci * Locking is currently fubar in the panic handler. 11862306a36Sopenharmony_ci */ 11962306a36Sopenharmony_ci if (!oops_in_progress) { 12062306a36Sopenharmony_ci WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); 12162306a36Sopenharmony_ci WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci drm_connector_list_iter_begin(dev, &conn_iter); 12662306a36Sopenharmony_ci drm_for_each_connector_iter(connector, &conn_iter) { 12762306a36Sopenharmony_ci if (connector->encoder == encoder) { 12862306a36Sopenharmony_ci drm_connector_list_iter_end(&conn_iter); 12962306a36Sopenharmony_ci return true; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci drm_connector_list_iter_end(&conn_iter); 13362306a36Sopenharmony_ci return false; 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ciEXPORT_SYMBOL(drm_helper_encoder_in_use); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/** 13862306a36Sopenharmony_ci * drm_helper_crtc_in_use - check if a given CRTC is in a mode_config 13962306a36Sopenharmony_ci * @crtc: CRTC to check 14062306a36Sopenharmony_ci * 14162306a36Sopenharmony_ci * Checks whether @crtc is with the current mode setting output configuration 14262306a36Sopenharmony_ci * in use by any connector. This doesn't mean that it is actually enabled since 14362306a36Sopenharmony_ci * the DPMS state is tracked separately. 14462306a36Sopenharmony_ci * 14562306a36Sopenharmony_ci * Returns: 14662306a36Sopenharmony_ci * True if @crtc is used, false otherwise. 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_cibool drm_helper_crtc_in_use(struct drm_crtc *crtc) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct drm_encoder *encoder; 15162306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci WARN_ON(drm_drv_uses_atomic_modeset(dev)); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* 15662306a36Sopenharmony_ci * We can expect this mutex to be locked if we are not panicking. 15762306a36Sopenharmony_ci * Locking is currently fubar in the panic handler. 15862306a36Sopenharmony_ci */ 15962306a36Sopenharmony_ci if (!oops_in_progress) 16062306a36Sopenharmony_ci WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci drm_for_each_encoder(encoder, dev) 16362306a36Sopenharmony_ci if (encoder->crtc == crtc && drm_helper_encoder_in_use(encoder)) 16462306a36Sopenharmony_ci return true; 16562306a36Sopenharmony_ci return false; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_helper_crtc_in_use); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic void 17062306a36Sopenharmony_cidrm_encoder_disable(struct drm_encoder *encoder) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci const struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (!encoder_funcs) 17562306a36Sopenharmony_ci return; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (encoder_funcs->disable) 17862306a36Sopenharmony_ci (*encoder_funcs->disable)(encoder); 17962306a36Sopenharmony_ci else if (encoder_funcs->dpms) 18062306a36Sopenharmony_ci (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic void __drm_helper_disable_unused_functions(struct drm_device *dev) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci struct drm_encoder *encoder; 18662306a36Sopenharmony_ci struct drm_crtc *crtc; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci drm_warn_on_modeset_not_all_locked(dev); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci drm_for_each_encoder(encoder, dev) { 19162306a36Sopenharmony_ci if (!drm_helper_encoder_in_use(encoder)) { 19262306a36Sopenharmony_ci drm_encoder_disable(encoder); 19362306a36Sopenharmony_ci /* disconnect encoder from any connector */ 19462306a36Sopenharmony_ci encoder->crtc = NULL; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci drm_for_each_crtc(crtc, dev) { 19962306a36Sopenharmony_ci const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci crtc->enabled = drm_helper_crtc_in_use(crtc); 20262306a36Sopenharmony_ci if (!crtc->enabled) { 20362306a36Sopenharmony_ci if (crtc_funcs->disable) 20462306a36Sopenharmony_ci (*crtc_funcs->disable)(crtc); 20562306a36Sopenharmony_ci else 20662306a36Sopenharmony_ci (*crtc_funcs->dpms)(crtc, DRM_MODE_DPMS_OFF); 20762306a36Sopenharmony_ci crtc->primary->fb = NULL; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci/** 21362306a36Sopenharmony_ci * drm_helper_disable_unused_functions - disable unused objects 21462306a36Sopenharmony_ci * @dev: DRM device 21562306a36Sopenharmony_ci * 21662306a36Sopenharmony_ci * This function walks through the entire mode setting configuration of @dev. It 21762306a36Sopenharmony_ci * will remove any CRTC links of unused encoders and encoder links of 21862306a36Sopenharmony_ci * disconnected connectors. Then it will disable all unused encoders and CRTCs 21962306a36Sopenharmony_ci * either by calling their disable callback if available or by calling their 22062306a36Sopenharmony_ci * dpms callback with DRM_MODE_DPMS_OFF. 22162306a36Sopenharmony_ci * 22262306a36Sopenharmony_ci * NOTE: 22362306a36Sopenharmony_ci * 22462306a36Sopenharmony_ci * This function is part of the legacy modeset helper library and will cause 22562306a36Sopenharmony_ci * major confusion with atomic drivers. This is because atomic helpers guarantee 22662306a36Sopenharmony_ci * to never call ->disable() hooks on a disabled function, or ->enable() hooks 22762306a36Sopenharmony_ci * on an enabled functions. drm_helper_disable_unused_functions() on the other 22862306a36Sopenharmony_ci * hand throws such guarantees into the wind and calls disable hooks 22962306a36Sopenharmony_ci * unconditionally on unused functions. 23062306a36Sopenharmony_ci */ 23162306a36Sopenharmony_civoid drm_helper_disable_unused_functions(struct drm_device *dev) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci WARN_ON(drm_drv_uses_atomic_modeset(dev)); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci drm_modeset_lock_all(dev); 23662306a36Sopenharmony_ci __drm_helper_disable_unused_functions(dev); 23762306a36Sopenharmony_ci drm_modeset_unlock_all(dev); 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ciEXPORT_SYMBOL(drm_helper_disable_unused_functions); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci/* 24262306a36Sopenharmony_ci * Check the CRTC we're going to map each output to vs. its current 24362306a36Sopenharmony_ci * CRTC. If they don't match, we have to disable the output and the CRTC 24462306a36Sopenharmony_ci * since the driver will have to re-route things. 24562306a36Sopenharmony_ci */ 24662306a36Sopenharmony_cistatic void 24762306a36Sopenharmony_cidrm_crtc_prepare_encoders(struct drm_device *dev) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci const struct drm_encoder_helper_funcs *encoder_funcs; 25062306a36Sopenharmony_ci struct drm_encoder *encoder; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci drm_for_each_encoder(encoder, dev) { 25362306a36Sopenharmony_ci encoder_funcs = encoder->helper_private; 25462306a36Sopenharmony_ci if (!encoder_funcs) 25562306a36Sopenharmony_ci continue; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci /* Disable unused encoders */ 25862306a36Sopenharmony_ci if (encoder->crtc == NULL) 25962306a36Sopenharmony_ci drm_encoder_disable(encoder); 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci/** 26462306a36Sopenharmony_ci * drm_crtc_helper_set_mode - internal helper to set a mode 26562306a36Sopenharmony_ci * @crtc: CRTC to program 26662306a36Sopenharmony_ci * @mode: mode to use 26762306a36Sopenharmony_ci * @x: horizontal offset into the surface 26862306a36Sopenharmony_ci * @y: vertical offset into the surface 26962306a36Sopenharmony_ci * @old_fb: old framebuffer, for cleanup 27062306a36Sopenharmony_ci * 27162306a36Sopenharmony_ci * Try to set @mode on @crtc. Give @crtc and its associated connectors a chance 27262306a36Sopenharmony_ci * to fixup or reject the mode prior to trying to set it. This is an internal 27362306a36Sopenharmony_ci * helper that drivers could e.g. use to update properties that require the 27462306a36Sopenharmony_ci * entire output pipe to be disabled and re-enabled in a new configuration. For 27562306a36Sopenharmony_ci * example for changing whether audio is enabled on a hdmi link or for changing 27662306a36Sopenharmony_ci * panel fitter or dither attributes. It is also called by the 27762306a36Sopenharmony_ci * drm_crtc_helper_set_config() helper function to drive the mode setting 27862306a36Sopenharmony_ci * sequence. 27962306a36Sopenharmony_ci * 28062306a36Sopenharmony_ci * Returns: 28162306a36Sopenharmony_ci * True if the mode was set successfully, false otherwise. 28262306a36Sopenharmony_ci */ 28362306a36Sopenharmony_cibool drm_crtc_helper_set_mode(struct drm_crtc *crtc, 28462306a36Sopenharmony_ci struct drm_display_mode *mode, 28562306a36Sopenharmony_ci int x, int y, 28662306a36Sopenharmony_ci struct drm_framebuffer *old_fb) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 28962306a36Sopenharmony_ci struct drm_display_mode *adjusted_mode, saved_mode, saved_hwmode; 29062306a36Sopenharmony_ci const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; 29162306a36Sopenharmony_ci const struct drm_encoder_helper_funcs *encoder_funcs; 29262306a36Sopenharmony_ci int saved_x, saved_y; 29362306a36Sopenharmony_ci bool saved_enabled; 29462306a36Sopenharmony_ci struct drm_encoder *encoder; 29562306a36Sopenharmony_ci bool ret = true; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci WARN_ON(drm_drv_uses_atomic_modeset(dev)); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci drm_warn_on_modeset_not_all_locked(dev); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci saved_enabled = crtc->enabled; 30262306a36Sopenharmony_ci crtc->enabled = drm_helper_crtc_in_use(crtc); 30362306a36Sopenharmony_ci if (!crtc->enabled) 30462306a36Sopenharmony_ci return true; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci adjusted_mode = drm_mode_duplicate(dev, mode); 30762306a36Sopenharmony_ci if (!adjusted_mode) { 30862306a36Sopenharmony_ci crtc->enabled = saved_enabled; 30962306a36Sopenharmony_ci return false; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci drm_mode_init(&saved_mode, &crtc->mode); 31362306a36Sopenharmony_ci drm_mode_init(&saved_hwmode, &crtc->hwmode); 31462306a36Sopenharmony_ci saved_x = crtc->x; 31562306a36Sopenharmony_ci saved_y = crtc->y; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* Update crtc values up front so the driver can rely on them for mode 31862306a36Sopenharmony_ci * setting. 31962306a36Sopenharmony_ci */ 32062306a36Sopenharmony_ci drm_mode_copy(&crtc->mode, mode); 32162306a36Sopenharmony_ci crtc->x = x; 32262306a36Sopenharmony_ci crtc->y = y; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /* Pass our mode to the connectors and the CRTC to give them a chance to 32562306a36Sopenharmony_ci * adjust it according to limitations or connector properties, and also 32662306a36Sopenharmony_ci * a chance to reject the mode entirely. 32762306a36Sopenharmony_ci */ 32862306a36Sopenharmony_ci drm_for_each_encoder(encoder, dev) { 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (encoder->crtc != crtc) 33162306a36Sopenharmony_ci continue; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci encoder_funcs = encoder->helper_private; 33462306a36Sopenharmony_ci if (!encoder_funcs) 33562306a36Sopenharmony_ci continue; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci encoder_funcs = encoder->helper_private; 33862306a36Sopenharmony_ci if (encoder_funcs->mode_fixup) { 33962306a36Sopenharmony_ci if (!(ret = encoder_funcs->mode_fixup(encoder, mode, 34062306a36Sopenharmony_ci adjusted_mode))) { 34162306a36Sopenharmony_ci DRM_DEBUG_KMS("Encoder fixup failed\n"); 34262306a36Sopenharmony_ci goto done; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (crtc_funcs->mode_fixup) { 34862306a36Sopenharmony_ci if (!(ret = crtc_funcs->mode_fixup(crtc, mode, 34962306a36Sopenharmony_ci adjusted_mode))) { 35062306a36Sopenharmony_ci DRM_DEBUG_KMS("CRTC fixup failed\n"); 35162306a36Sopenharmony_ci goto done; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci drm_mode_copy(&crtc->hwmode, adjusted_mode); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci /* Prepare the encoders and CRTCs before setting the mode. */ 35962306a36Sopenharmony_ci drm_for_each_encoder(encoder, dev) { 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (encoder->crtc != crtc) 36262306a36Sopenharmony_ci continue; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci encoder_funcs = encoder->helper_private; 36562306a36Sopenharmony_ci if (!encoder_funcs) 36662306a36Sopenharmony_ci continue; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci /* Disable the encoders as the first thing we do. */ 36962306a36Sopenharmony_ci if (encoder_funcs->prepare) 37062306a36Sopenharmony_ci encoder_funcs->prepare(encoder); 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci drm_crtc_prepare_encoders(dev); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci crtc_funcs->prepare(crtc); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci /* Set up the DPLL and any encoders state that needs to adjust or depend 37862306a36Sopenharmony_ci * on the DPLL. 37962306a36Sopenharmony_ci */ 38062306a36Sopenharmony_ci ret = !crtc_funcs->mode_set(crtc, mode, adjusted_mode, x, y, old_fb); 38162306a36Sopenharmony_ci if (!ret) 38262306a36Sopenharmony_ci goto done; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci drm_for_each_encoder(encoder, dev) { 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (encoder->crtc != crtc) 38762306a36Sopenharmony_ci continue; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci encoder_funcs = encoder->helper_private; 39062306a36Sopenharmony_ci if (!encoder_funcs) 39162306a36Sopenharmony_ci continue; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci DRM_DEBUG_KMS("[ENCODER:%d:%s] set [MODE:%s]\n", 39462306a36Sopenharmony_ci encoder->base.id, encoder->name, mode->name); 39562306a36Sopenharmony_ci if (encoder_funcs->mode_set) 39662306a36Sopenharmony_ci encoder_funcs->mode_set(encoder, mode, adjusted_mode); 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci /* Now enable the clocks, plane, pipe, and connectors that we set up. */ 40062306a36Sopenharmony_ci crtc_funcs->commit(crtc); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci drm_for_each_encoder(encoder, dev) { 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (encoder->crtc != crtc) 40562306a36Sopenharmony_ci continue; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci encoder_funcs = encoder->helper_private; 40862306a36Sopenharmony_ci if (!encoder_funcs) 40962306a36Sopenharmony_ci continue; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (encoder_funcs->commit) 41262306a36Sopenharmony_ci encoder_funcs->commit(encoder); 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci /* Calculate and store various constants which 41662306a36Sopenharmony_ci * are later needed by vblank and swap-completion 41762306a36Sopenharmony_ci * timestamping. They are derived from true hwmode. 41862306a36Sopenharmony_ci */ 41962306a36Sopenharmony_ci drm_calc_timestamping_constants(crtc, &crtc->hwmode); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci /* FIXME: add subpixel order */ 42262306a36Sopenharmony_cidone: 42362306a36Sopenharmony_ci drm_mode_destroy(dev, adjusted_mode); 42462306a36Sopenharmony_ci if (!ret) { 42562306a36Sopenharmony_ci crtc->enabled = saved_enabled; 42662306a36Sopenharmony_ci drm_mode_copy(&crtc->mode, &saved_mode); 42762306a36Sopenharmony_ci drm_mode_copy(&crtc->hwmode, &saved_hwmode); 42862306a36Sopenharmony_ci crtc->x = saved_x; 42962306a36Sopenharmony_ci crtc->y = saved_y; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci return ret; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ciEXPORT_SYMBOL(drm_crtc_helper_set_mode); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci/** 43762306a36Sopenharmony_ci * drm_crtc_helper_atomic_check() - Helper to check CRTC atomic-state 43862306a36Sopenharmony_ci * @crtc: CRTC to check 43962306a36Sopenharmony_ci * @state: atomic state object 44062306a36Sopenharmony_ci * 44162306a36Sopenharmony_ci * Provides a default CRTC-state check handler for CRTCs that only have 44262306a36Sopenharmony_ci * one primary plane attached to it. 44362306a36Sopenharmony_ci * 44462306a36Sopenharmony_ci * This is often the case for the CRTC of simple framebuffers. See also 44562306a36Sopenharmony_ci * drm_plane_helper_atomic_check() for the respective plane-state check 44662306a36Sopenharmony_ci * helper function. 44762306a36Sopenharmony_ci * 44862306a36Sopenharmony_ci * RETURNS: 44962306a36Sopenharmony_ci * Zero on success, or an errno code otherwise. 45062306a36Sopenharmony_ci */ 45162306a36Sopenharmony_ciint drm_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci if (!new_crtc_state->enable) 45662306a36Sopenharmony_ci return 0; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci return drm_atomic_helper_check_crtc_primary_plane(new_crtc_state); 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ciEXPORT_SYMBOL(drm_crtc_helper_atomic_check); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cistatic void 46362306a36Sopenharmony_cidrm_crtc_helper_disable(struct drm_crtc *crtc) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 46662306a36Sopenharmony_ci struct drm_connector *connector; 46762306a36Sopenharmony_ci struct drm_encoder *encoder; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci /* Decouple all encoders and their attached connectors from this crtc */ 47062306a36Sopenharmony_ci drm_for_each_encoder(encoder, dev) { 47162306a36Sopenharmony_ci struct drm_connector_list_iter conn_iter; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (encoder->crtc != crtc) 47462306a36Sopenharmony_ci continue; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci drm_connector_list_iter_begin(dev, &conn_iter); 47762306a36Sopenharmony_ci drm_for_each_connector_iter(connector, &conn_iter) { 47862306a36Sopenharmony_ci if (connector->encoder != encoder) 47962306a36Sopenharmony_ci continue; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci connector->encoder = NULL; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci /* 48462306a36Sopenharmony_ci * drm_helper_disable_unused_functions() ought to be 48562306a36Sopenharmony_ci * doing this, but since we've decoupled the encoder 48662306a36Sopenharmony_ci * from the connector above, the required connection 48762306a36Sopenharmony_ci * between them is henceforth no longer available. 48862306a36Sopenharmony_ci */ 48962306a36Sopenharmony_ci connector->dpms = DRM_MODE_DPMS_OFF; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci /* we keep a reference while the encoder is bound */ 49262306a36Sopenharmony_ci drm_connector_put(connector); 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci drm_connector_list_iter_end(&conn_iter); 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci __drm_helper_disable_unused_functions(dev); 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci/* 50162306a36Sopenharmony_ci * For connectors that support multiple encoders, either the 50262306a36Sopenharmony_ci * .atomic_best_encoder() or .best_encoder() operation must be implemented. 50362306a36Sopenharmony_ci */ 50462306a36Sopenharmony_cistruct drm_encoder * 50562306a36Sopenharmony_cidrm_connector_get_single_encoder(struct drm_connector *connector) 50662306a36Sopenharmony_ci{ 50762306a36Sopenharmony_ci struct drm_encoder *encoder; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci WARN_ON(hweight32(connector->possible_encoders) > 1); 51062306a36Sopenharmony_ci drm_connector_for_each_possible_encoder(connector, encoder) 51162306a36Sopenharmony_ci return encoder; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci return NULL; 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci/** 51762306a36Sopenharmony_ci * drm_crtc_helper_set_config - set a new config from userspace 51862306a36Sopenharmony_ci * @set: mode set configuration 51962306a36Sopenharmony_ci * @ctx: lock acquire context, not used here 52062306a36Sopenharmony_ci * 52162306a36Sopenharmony_ci * The drm_crtc_helper_set_config() helper function implements the of 52262306a36Sopenharmony_ci * &drm_crtc_funcs.set_config callback for drivers using the legacy CRTC 52362306a36Sopenharmony_ci * helpers. 52462306a36Sopenharmony_ci * 52562306a36Sopenharmony_ci * It first tries to locate the best encoder for each connector by calling the 52662306a36Sopenharmony_ci * connector @drm_connector_helper_funcs.best_encoder helper operation. 52762306a36Sopenharmony_ci * 52862306a36Sopenharmony_ci * After locating the appropriate encoders, the helper function will call the 52962306a36Sopenharmony_ci * mode_fixup encoder and CRTC helper operations to adjust the requested mode, 53062306a36Sopenharmony_ci * or reject it completely in which case an error will be returned to the 53162306a36Sopenharmony_ci * application. If the new configuration after mode adjustment is identical to 53262306a36Sopenharmony_ci * the current configuration the helper function will return without performing 53362306a36Sopenharmony_ci * any other operation. 53462306a36Sopenharmony_ci * 53562306a36Sopenharmony_ci * If the adjusted mode is identical to the current mode but changes to the 53662306a36Sopenharmony_ci * frame buffer need to be applied, the drm_crtc_helper_set_config() function 53762306a36Sopenharmony_ci * will call the CRTC &drm_crtc_helper_funcs.mode_set_base helper operation. 53862306a36Sopenharmony_ci * 53962306a36Sopenharmony_ci * If the adjusted mode differs from the current mode, or if the 54062306a36Sopenharmony_ci * ->mode_set_base() helper operation is not provided, the helper function 54162306a36Sopenharmony_ci * performs a full mode set sequence by calling the ->prepare(), ->mode_set() 54262306a36Sopenharmony_ci * and ->commit() CRTC and encoder helper operations, in that order. 54362306a36Sopenharmony_ci * Alternatively it can also use the dpms and disable helper operations. For 54462306a36Sopenharmony_ci * details see &struct drm_crtc_helper_funcs and struct 54562306a36Sopenharmony_ci * &drm_encoder_helper_funcs. 54662306a36Sopenharmony_ci * 54762306a36Sopenharmony_ci * This function is deprecated. New drivers must implement atomic modeset 54862306a36Sopenharmony_ci * support, for which this function is unsuitable. Instead drivers should use 54962306a36Sopenharmony_ci * drm_atomic_helper_set_config(). 55062306a36Sopenharmony_ci * 55162306a36Sopenharmony_ci * Returns: 55262306a36Sopenharmony_ci * Returns 0 on success, negative errno numbers on failure. 55362306a36Sopenharmony_ci */ 55462306a36Sopenharmony_ciint drm_crtc_helper_set_config(struct drm_mode_set *set, 55562306a36Sopenharmony_ci struct drm_modeset_acquire_ctx *ctx) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci struct drm_device *dev; 55862306a36Sopenharmony_ci struct drm_crtc **save_encoder_crtcs, *new_crtc; 55962306a36Sopenharmony_ci struct drm_encoder **save_connector_encoders, *new_encoder, *encoder; 56062306a36Sopenharmony_ci bool mode_changed = false; /* if true do a full mode set */ 56162306a36Sopenharmony_ci bool fb_changed = false; /* if true and !mode_changed just do a flip */ 56262306a36Sopenharmony_ci struct drm_connector *connector; 56362306a36Sopenharmony_ci struct drm_connector_list_iter conn_iter; 56462306a36Sopenharmony_ci int count = 0, ro, fail = 0; 56562306a36Sopenharmony_ci const struct drm_crtc_helper_funcs *crtc_funcs; 56662306a36Sopenharmony_ci struct drm_mode_set save_set; 56762306a36Sopenharmony_ci int ret; 56862306a36Sopenharmony_ci int i; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci DRM_DEBUG_KMS("\n"); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci BUG_ON(!set); 57362306a36Sopenharmony_ci BUG_ON(!set->crtc); 57462306a36Sopenharmony_ci BUG_ON(!set->crtc->helper_private); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci /* Enforce sane interface api - has been abused by the fb helper. */ 57762306a36Sopenharmony_ci BUG_ON(!set->mode && set->fb); 57862306a36Sopenharmony_ci BUG_ON(set->fb && set->num_connectors == 0); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci crtc_funcs = set->crtc->helper_private; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci dev = set->crtc->dev; 58362306a36Sopenharmony_ci WARN_ON(drm_drv_uses_atomic_modeset(dev)); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (!set->mode) 58662306a36Sopenharmony_ci set->fb = NULL; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci if (set->fb) { 58962306a36Sopenharmony_ci DRM_DEBUG_KMS("[CRTC:%d:%s] [FB:%d] #connectors=%d (x y) (%i %i)\n", 59062306a36Sopenharmony_ci set->crtc->base.id, set->crtc->name, 59162306a36Sopenharmony_ci set->fb->base.id, 59262306a36Sopenharmony_ci (int)set->num_connectors, set->x, set->y); 59362306a36Sopenharmony_ci } else { 59462306a36Sopenharmony_ci DRM_DEBUG_KMS("[CRTC:%d:%s] [NOFB]\n", 59562306a36Sopenharmony_ci set->crtc->base.id, set->crtc->name); 59662306a36Sopenharmony_ci drm_crtc_helper_disable(set->crtc); 59762306a36Sopenharmony_ci return 0; 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci drm_warn_on_modeset_not_all_locked(dev); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci /* 60362306a36Sopenharmony_ci * Allocate space for the backup of all (non-pointer) encoder and 60462306a36Sopenharmony_ci * connector data. 60562306a36Sopenharmony_ci */ 60662306a36Sopenharmony_ci save_encoder_crtcs = kcalloc(dev->mode_config.num_encoder, 60762306a36Sopenharmony_ci sizeof(struct drm_crtc *), GFP_KERNEL); 60862306a36Sopenharmony_ci if (!save_encoder_crtcs) 60962306a36Sopenharmony_ci return -ENOMEM; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci save_connector_encoders = kcalloc(dev->mode_config.num_connector, 61262306a36Sopenharmony_ci sizeof(struct drm_encoder *), GFP_KERNEL); 61362306a36Sopenharmony_ci if (!save_connector_encoders) { 61462306a36Sopenharmony_ci kfree(save_encoder_crtcs); 61562306a36Sopenharmony_ci return -ENOMEM; 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci /* 61962306a36Sopenharmony_ci * Copy data. Note that driver private data is not affected. 62062306a36Sopenharmony_ci * Should anything bad happen only the expected state is 62162306a36Sopenharmony_ci * restored, not the drivers personal bookkeeping. 62262306a36Sopenharmony_ci */ 62362306a36Sopenharmony_ci count = 0; 62462306a36Sopenharmony_ci drm_for_each_encoder(encoder, dev) { 62562306a36Sopenharmony_ci save_encoder_crtcs[count++] = encoder->crtc; 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci count = 0; 62962306a36Sopenharmony_ci drm_connector_list_iter_begin(dev, &conn_iter); 63062306a36Sopenharmony_ci drm_for_each_connector_iter(connector, &conn_iter) 63162306a36Sopenharmony_ci save_connector_encoders[count++] = connector->encoder; 63262306a36Sopenharmony_ci drm_connector_list_iter_end(&conn_iter); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci save_set.crtc = set->crtc; 63562306a36Sopenharmony_ci save_set.mode = &set->crtc->mode; 63662306a36Sopenharmony_ci save_set.x = set->crtc->x; 63762306a36Sopenharmony_ci save_set.y = set->crtc->y; 63862306a36Sopenharmony_ci save_set.fb = set->crtc->primary->fb; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci /* We should be able to check here if the fb has the same properties 64162306a36Sopenharmony_ci * and then just flip_or_move it */ 64262306a36Sopenharmony_ci if (set->crtc->primary->fb != set->fb) { 64362306a36Sopenharmony_ci /* If we have no fb then treat it as a full mode set */ 64462306a36Sopenharmony_ci if (set->crtc->primary->fb == NULL) { 64562306a36Sopenharmony_ci DRM_DEBUG_KMS("crtc has no fb, full mode set\n"); 64662306a36Sopenharmony_ci mode_changed = true; 64762306a36Sopenharmony_ci } else if (set->fb->format != set->crtc->primary->fb->format) { 64862306a36Sopenharmony_ci mode_changed = true; 64962306a36Sopenharmony_ci } else 65062306a36Sopenharmony_ci fb_changed = true; 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci if (set->x != set->crtc->x || set->y != set->crtc->y) 65462306a36Sopenharmony_ci fb_changed = true; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci if (!drm_mode_equal(set->mode, &set->crtc->mode)) { 65762306a36Sopenharmony_ci DRM_DEBUG_KMS("modes are different, full mode set\n"); 65862306a36Sopenharmony_ci drm_mode_debug_printmodeline(&set->crtc->mode); 65962306a36Sopenharmony_ci drm_mode_debug_printmodeline(set->mode); 66062306a36Sopenharmony_ci mode_changed = true; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci /* take a reference on all unbound connectors in set, reuse the 66462306a36Sopenharmony_ci * already taken reference for bound connectors 66562306a36Sopenharmony_ci */ 66662306a36Sopenharmony_ci for (ro = 0; ro < set->num_connectors; ro++) { 66762306a36Sopenharmony_ci if (set->connectors[ro]->encoder) 66862306a36Sopenharmony_ci continue; 66962306a36Sopenharmony_ci drm_connector_get(set->connectors[ro]); 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci /* a) traverse passed in connector list and get encoders for them */ 67362306a36Sopenharmony_ci count = 0; 67462306a36Sopenharmony_ci drm_connector_list_iter_begin(dev, &conn_iter); 67562306a36Sopenharmony_ci drm_for_each_connector_iter(connector, &conn_iter) { 67662306a36Sopenharmony_ci const struct drm_connector_helper_funcs *connector_funcs = 67762306a36Sopenharmony_ci connector->helper_private; 67862306a36Sopenharmony_ci new_encoder = connector->encoder; 67962306a36Sopenharmony_ci for (ro = 0; ro < set->num_connectors; ro++) { 68062306a36Sopenharmony_ci if (set->connectors[ro] == connector) { 68162306a36Sopenharmony_ci if (connector_funcs->best_encoder) 68262306a36Sopenharmony_ci new_encoder = connector_funcs->best_encoder(connector); 68362306a36Sopenharmony_ci else 68462306a36Sopenharmony_ci new_encoder = drm_connector_get_single_encoder(connector); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci /* if we can't get an encoder for a connector 68762306a36Sopenharmony_ci we are setting now - then fail */ 68862306a36Sopenharmony_ci if (new_encoder == NULL) 68962306a36Sopenharmony_ci /* don't break so fail path works correct */ 69062306a36Sopenharmony_ci fail = 1; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci if (connector->dpms != DRM_MODE_DPMS_ON) { 69362306a36Sopenharmony_ci DRM_DEBUG_KMS("connector dpms not on, full mode switch\n"); 69462306a36Sopenharmony_ci mode_changed = true; 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci break; 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci if (new_encoder != connector->encoder) { 70262306a36Sopenharmony_ci DRM_DEBUG_KMS("encoder changed, full mode switch\n"); 70362306a36Sopenharmony_ci mode_changed = true; 70462306a36Sopenharmony_ci /* If the encoder is reused for another connector, then 70562306a36Sopenharmony_ci * the appropriate crtc will be set later. 70662306a36Sopenharmony_ci */ 70762306a36Sopenharmony_ci if (connector->encoder) 70862306a36Sopenharmony_ci connector->encoder->crtc = NULL; 70962306a36Sopenharmony_ci connector->encoder = new_encoder; 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci drm_connector_list_iter_end(&conn_iter); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci if (fail) { 71562306a36Sopenharmony_ci ret = -EINVAL; 71662306a36Sopenharmony_ci goto fail; 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci count = 0; 72062306a36Sopenharmony_ci drm_connector_list_iter_begin(dev, &conn_iter); 72162306a36Sopenharmony_ci drm_for_each_connector_iter(connector, &conn_iter) { 72262306a36Sopenharmony_ci if (!connector->encoder) 72362306a36Sopenharmony_ci continue; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci if (connector->encoder->crtc == set->crtc) 72662306a36Sopenharmony_ci new_crtc = NULL; 72762306a36Sopenharmony_ci else 72862306a36Sopenharmony_ci new_crtc = connector->encoder->crtc; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci for (ro = 0; ro < set->num_connectors; ro++) { 73162306a36Sopenharmony_ci if (set->connectors[ro] == connector) 73262306a36Sopenharmony_ci new_crtc = set->crtc; 73362306a36Sopenharmony_ci } 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci /* Make sure the new CRTC will work with the encoder */ 73662306a36Sopenharmony_ci if (new_crtc && 73762306a36Sopenharmony_ci !drm_encoder_crtc_ok(connector->encoder, new_crtc)) { 73862306a36Sopenharmony_ci ret = -EINVAL; 73962306a36Sopenharmony_ci drm_connector_list_iter_end(&conn_iter); 74062306a36Sopenharmony_ci goto fail; 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci if (new_crtc != connector->encoder->crtc) { 74362306a36Sopenharmony_ci DRM_DEBUG_KMS("crtc changed, full mode switch\n"); 74462306a36Sopenharmony_ci mode_changed = true; 74562306a36Sopenharmony_ci connector->encoder->crtc = new_crtc; 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci if (new_crtc) { 74862306a36Sopenharmony_ci DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d:%s]\n", 74962306a36Sopenharmony_ci connector->base.id, connector->name, 75062306a36Sopenharmony_ci new_crtc->base.id, new_crtc->name); 75162306a36Sopenharmony_ci } else { 75262306a36Sopenharmony_ci DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [NOCRTC]\n", 75362306a36Sopenharmony_ci connector->base.id, connector->name); 75462306a36Sopenharmony_ci } 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci drm_connector_list_iter_end(&conn_iter); 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci /* mode_set_base is not a required function */ 75962306a36Sopenharmony_ci if (fb_changed && !crtc_funcs->mode_set_base) 76062306a36Sopenharmony_ci mode_changed = true; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci if (mode_changed) { 76362306a36Sopenharmony_ci if (drm_helper_crtc_in_use(set->crtc)) { 76462306a36Sopenharmony_ci DRM_DEBUG_KMS("attempting to set mode from" 76562306a36Sopenharmony_ci " userspace\n"); 76662306a36Sopenharmony_ci drm_mode_debug_printmodeline(set->mode); 76762306a36Sopenharmony_ci set->crtc->primary->fb = set->fb; 76862306a36Sopenharmony_ci if (!drm_crtc_helper_set_mode(set->crtc, set->mode, 76962306a36Sopenharmony_ci set->x, set->y, 77062306a36Sopenharmony_ci save_set.fb)) { 77162306a36Sopenharmony_ci DRM_ERROR("failed to set mode on [CRTC:%d:%s]\n", 77262306a36Sopenharmony_ci set->crtc->base.id, set->crtc->name); 77362306a36Sopenharmony_ci set->crtc->primary->fb = save_set.fb; 77462306a36Sopenharmony_ci ret = -EINVAL; 77562306a36Sopenharmony_ci goto fail; 77662306a36Sopenharmony_ci } 77762306a36Sopenharmony_ci DRM_DEBUG_KMS("Setting connector DPMS state to on\n"); 77862306a36Sopenharmony_ci for (i = 0; i < set->num_connectors; i++) { 77962306a36Sopenharmony_ci DRM_DEBUG_KMS("\t[CONNECTOR:%d:%s] set DPMS on\n", set->connectors[i]->base.id, 78062306a36Sopenharmony_ci set->connectors[i]->name); 78162306a36Sopenharmony_ci set->connectors[i]->funcs->dpms(set->connectors[i], DRM_MODE_DPMS_ON); 78262306a36Sopenharmony_ci } 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci __drm_helper_disable_unused_functions(dev); 78562306a36Sopenharmony_ci } else if (fb_changed) { 78662306a36Sopenharmony_ci set->crtc->x = set->x; 78762306a36Sopenharmony_ci set->crtc->y = set->y; 78862306a36Sopenharmony_ci set->crtc->primary->fb = set->fb; 78962306a36Sopenharmony_ci ret = crtc_funcs->mode_set_base(set->crtc, 79062306a36Sopenharmony_ci set->x, set->y, save_set.fb); 79162306a36Sopenharmony_ci if (ret != 0) { 79262306a36Sopenharmony_ci set->crtc->x = save_set.x; 79362306a36Sopenharmony_ci set->crtc->y = save_set.y; 79462306a36Sopenharmony_ci set->crtc->primary->fb = save_set.fb; 79562306a36Sopenharmony_ci goto fail; 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci } 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci kfree(save_connector_encoders); 80062306a36Sopenharmony_ci kfree(save_encoder_crtcs); 80162306a36Sopenharmony_ci return 0; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_cifail: 80462306a36Sopenharmony_ci /* Restore all previous data. */ 80562306a36Sopenharmony_ci count = 0; 80662306a36Sopenharmony_ci drm_for_each_encoder(encoder, dev) { 80762306a36Sopenharmony_ci encoder->crtc = save_encoder_crtcs[count++]; 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci count = 0; 81162306a36Sopenharmony_ci drm_connector_list_iter_begin(dev, &conn_iter); 81262306a36Sopenharmony_ci drm_for_each_connector_iter(connector, &conn_iter) 81362306a36Sopenharmony_ci connector->encoder = save_connector_encoders[count++]; 81462306a36Sopenharmony_ci drm_connector_list_iter_end(&conn_iter); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci /* after fail drop reference on all unbound connectors in set, let 81762306a36Sopenharmony_ci * bound connectors keep their reference 81862306a36Sopenharmony_ci */ 81962306a36Sopenharmony_ci for (ro = 0; ro < set->num_connectors; ro++) { 82062306a36Sopenharmony_ci if (set->connectors[ro]->encoder) 82162306a36Sopenharmony_ci continue; 82262306a36Sopenharmony_ci drm_connector_put(set->connectors[ro]); 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci /* Try to restore the config */ 82662306a36Sopenharmony_ci if (mode_changed && 82762306a36Sopenharmony_ci !drm_crtc_helper_set_mode(save_set.crtc, save_set.mode, save_set.x, 82862306a36Sopenharmony_ci save_set.y, save_set.fb)) 82962306a36Sopenharmony_ci DRM_ERROR("failed to restore config after modeset failure\n"); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci kfree(save_connector_encoders); 83262306a36Sopenharmony_ci kfree(save_encoder_crtcs); 83362306a36Sopenharmony_ci return ret; 83462306a36Sopenharmony_ci} 83562306a36Sopenharmony_ciEXPORT_SYMBOL(drm_crtc_helper_set_config); 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_cistatic int drm_helper_choose_encoder_dpms(struct drm_encoder *encoder) 83862306a36Sopenharmony_ci{ 83962306a36Sopenharmony_ci int dpms = DRM_MODE_DPMS_OFF; 84062306a36Sopenharmony_ci struct drm_connector *connector; 84162306a36Sopenharmony_ci struct drm_connector_list_iter conn_iter; 84262306a36Sopenharmony_ci struct drm_device *dev = encoder->dev; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci drm_connector_list_iter_begin(dev, &conn_iter); 84562306a36Sopenharmony_ci drm_for_each_connector_iter(connector, &conn_iter) 84662306a36Sopenharmony_ci if (connector->encoder == encoder) 84762306a36Sopenharmony_ci if (connector->dpms < dpms) 84862306a36Sopenharmony_ci dpms = connector->dpms; 84962306a36Sopenharmony_ci drm_connector_list_iter_end(&conn_iter); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci return dpms; 85262306a36Sopenharmony_ci} 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci/* Helper which handles bridge ordering around encoder dpms */ 85562306a36Sopenharmony_cistatic void drm_helper_encoder_dpms(struct drm_encoder *encoder, int mode) 85662306a36Sopenharmony_ci{ 85762306a36Sopenharmony_ci const struct drm_encoder_helper_funcs *encoder_funcs; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci encoder_funcs = encoder->helper_private; 86062306a36Sopenharmony_ci if (!encoder_funcs) 86162306a36Sopenharmony_ci return; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci if (encoder_funcs->dpms) 86462306a36Sopenharmony_ci encoder_funcs->dpms(encoder, mode); 86562306a36Sopenharmony_ci} 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_cistatic int drm_helper_choose_crtc_dpms(struct drm_crtc *crtc) 86862306a36Sopenharmony_ci{ 86962306a36Sopenharmony_ci int dpms = DRM_MODE_DPMS_OFF; 87062306a36Sopenharmony_ci struct drm_connector *connector; 87162306a36Sopenharmony_ci struct drm_connector_list_iter conn_iter; 87262306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci drm_connector_list_iter_begin(dev, &conn_iter); 87562306a36Sopenharmony_ci drm_for_each_connector_iter(connector, &conn_iter) 87662306a36Sopenharmony_ci if (connector->encoder && connector->encoder->crtc == crtc) 87762306a36Sopenharmony_ci if (connector->dpms < dpms) 87862306a36Sopenharmony_ci dpms = connector->dpms; 87962306a36Sopenharmony_ci drm_connector_list_iter_end(&conn_iter); 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci return dpms; 88262306a36Sopenharmony_ci} 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci/** 88562306a36Sopenharmony_ci * drm_helper_connector_dpms() - connector dpms helper implementation 88662306a36Sopenharmony_ci * @connector: affected connector 88762306a36Sopenharmony_ci * @mode: DPMS mode 88862306a36Sopenharmony_ci * 88962306a36Sopenharmony_ci * The drm_helper_connector_dpms() helper function implements the 89062306a36Sopenharmony_ci * &drm_connector_funcs.dpms callback for drivers using the legacy CRTC 89162306a36Sopenharmony_ci * helpers. 89262306a36Sopenharmony_ci * 89362306a36Sopenharmony_ci * This is the main helper function provided by the CRTC helper framework for 89462306a36Sopenharmony_ci * implementing the DPMS connector attribute. It computes the new desired DPMS 89562306a36Sopenharmony_ci * state for all encoders and CRTCs in the output mesh and calls the 89662306a36Sopenharmony_ci * &drm_crtc_helper_funcs.dpms and &drm_encoder_helper_funcs.dpms callbacks 89762306a36Sopenharmony_ci * provided by the driver. 89862306a36Sopenharmony_ci * 89962306a36Sopenharmony_ci * This function is deprecated. New drivers must implement atomic modeset 90062306a36Sopenharmony_ci * support, where DPMS is handled in the DRM core. 90162306a36Sopenharmony_ci * 90262306a36Sopenharmony_ci * Returns: 90362306a36Sopenharmony_ci * Always returns 0. 90462306a36Sopenharmony_ci */ 90562306a36Sopenharmony_ciint drm_helper_connector_dpms(struct drm_connector *connector, int mode) 90662306a36Sopenharmony_ci{ 90762306a36Sopenharmony_ci struct drm_encoder *encoder = connector->encoder; 90862306a36Sopenharmony_ci struct drm_crtc *crtc = encoder ? encoder->crtc : NULL; 90962306a36Sopenharmony_ci int old_dpms, encoder_dpms = DRM_MODE_DPMS_OFF; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci WARN_ON(drm_drv_uses_atomic_modeset(connector->dev)); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci if (mode == connector->dpms) 91462306a36Sopenharmony_ci return 0; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci old_dpms = connector->dpms; 91762306a36Sopenharmony_ci connector->dpms = mode; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci if (encoder) 92062306a36Sopenharmony_ci encoder_dpms = drm_helper_choose_encoder_dpms(encoder); 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci /* from off to on, do crtc then encoder */ 92362306a36Sopenharmony_ci if (mode < old_dpms) { 92462306a36Sopenharmony_ci if (crtc) { 92562306a36Sopenharmony_ci const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci if (crtc_funcs->dpms) 92862306a36Sopenharmony_ci (*crtc_funcs->dpms) (crtc, 92962306a36Sopenharmony_ci drm_helper_choose_crtc_dpms(crtc)); 93062306a36Sopenharmony_ci } 93162306a36Sopenharmony_ci if (encoder) 93262306a36Sopenharmony_ci drm_helper_encoder_dpms(encoder, encoder_dpms); 93362306a36Sopenharmony_ci } 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci /* from on to off, do encoder then crtc */ 93662306a36Sopenharmony_ci if (mode > old_dpms) { 93762306a36Sopenharmony_ci if (encoder) 93862306a36Sopenharmony_ci drm_helper_encoder_dpms(encoder, encoder_dpms); 93962306a36Sopenharmony_ci if (crtc) { 94062306a36Sopenharmony_ci const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci if (crtc_funcs->dpms) 94362306a36Sopenharmony_ci (*crtc_funcs->dpms) (crtc, 94462306a36Sopenharmony_ci drm_helper_choose_crtc_dpms(crtc)); 94562306a36Sopenharmony_ci } 94662306a36Sopenharmony_ci } 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci return 0; 94962306a36Sopenharmony_ci} 95062306a36Sopenharmony_ciEXPORT_SYMBOL(drm_helper_connector_dpms); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci/** 95362306a36Sopenharmony_ci * drm_helper_resume_force_mode - force-restore mode setting configuration 95462306a36Sopenharmony_ci * @dev: drm_device which should be restored 95562306a36Sopenharmony_ci * 95662306a36Sopenharmony_ci * Drivers which use the mode setting helpers can use this function to 95762306a36Sopenharmony_ci * force-restore the mode setting configuration e.g. on resume or when something 95862306a36Sopenharmony_ci * else might have trampled over the hw state (like some overzealous old BIOSen 95962306a36Sopenharmony_ci * tended to do). 96062306a36Sopenharmony_ci * 96162306a36Sopenharmony_ci * This helper doesn't provide a error return value since restoring the old 96262306a36Sopenharmony_ci * config should never fail due to resource allocation issues since the driver 96362306a36Sopenharmony_ci * has successfully set the restored configuration already. Hence this should 96462306a36Sopenharmony_ci * boil down to the equivalent of a few dpms on calls, which also don't provide 96562306a36Sopenharmony_ci * an error code. 96662306a36Sopenharmony_ci * 96762306a36Sopenharmony_ci * Drivers where simply restoring an old configuration again might fail (e.g. 96862306a36Sopenharmony_ci * due to slight differences in allocating shared resources when the 96962306a36Sopenharmony_ci * configuration is restored in a different order than when userspace set it up) 97062306a36Sopenharmony_ci * need to use their own restore logic. 97162306a36Sopenharmony_ci * 97262306a36Sopenharmony_ci * This function is deprecated. New drivers should implement atomic mode- 97362306a36Sopenharmony_ci * setting and use the atomic suspend/resume helpers. 97462306a36Sopenharmony_ci * 97562306a36Sopenharmony_ci * See also: 97662306a36Sopenharmony_ci * drm_atomic_helper_suspend(), drm_atomic_helper_resume() 97762306a36Sopenharmony_ci */ 97862306a36Sopenharmony_civoid drm_helper_resume_force_mode(struct drm_device *dev) 97962306a36Sopenharmony_ci{ 98062306a36Sopenharmony_ci struct drm_crtc *crtc; 98162306a36Sopenharmony_ci struct drm_encoder *encoder; 98262306a36Sopenharmony_ci const struct drm_crtc_helper_funcs *crtc_funcs; 98362306a36Sopenharmony_ci int encoder_dpms; 98462306a36Sopenharmony_ci bool ret; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci WARN_ON(drm_drv_uses_atomic_modeset(dev)); 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci drm_modeset_lock_all(dev); 98962306a36Sopenharmony_ci drm_for_each_crtc(crtc, dev) { 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci if (!crtc->enabled) 99262306a36Sopenharmony_ci continue; 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci ret = drm_crtc_helper_set_mode(crtc, &crtc->mode, 99562306a36Sopenharmony_ci crtc->x, crtc->y, crtc->primary->fb); 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci /* Restoring the old config should never fail! */ 99862306a36Sopenharmony_ci if (ret == false) 99962306a36Sopenharmony_ci DRM_ERROR("failed to set mode on crtc %p\n", crtc); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci /* Turn off outputs that were already powered off */ 100262306a36Sopenharmony_ci if (drm_helper_choose_crtc_dpms(crtc)) { 100362306a36Sopenharmony_ci drm_for_each_encoder(encoder, dev) { 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci if(encoder->crtc != crtc) 100662306a36Sopenharmony_ci continue; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci encoder_dpms = drm_helper_choose_encoder_dpms( 100962306a36Sopenharmony_ci encoder); 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci drm_helper_encoder_dpms(encoder, encoder_dpms); 101262306a36Sopenharmony_ci } 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci crtc_funcs = crtc->helper_private; 101562306a36Sopenharmony_ci if (crtc_funcs->dpms) 101662306a36Sopenharmony_ci (*crtc_funcs->dpms) (crtc, 101762306a36Sopenharmony_ci drm_helper_choose_crtc_dpms(crtc)); 101862306a36Sopenharmony_ci } 101962306a36Sopenharmony_ci } 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci /* disable the unused connectors while restoring the modesetting */ 102262306a36Sopenharmony_ci __drm_helper_disable_unused_functions(dev); 102362306a36Sopenharmony_ci drm_modeset_unlock_all(dev); 102462306a36Sopenharmony_ci} 102562306a36Sopenharmony_ciEXPORT_SYMBOL(drm_helper_resume_force_mode); 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci/** 102862306a36Sopenharmony_ci * drm_helper_force_disable_all - Forcibly turn off all enabled CRTCs 102962306a36Sopenharmony_ci * @dev: DRM device whose CRTCs to turn off 103062306a36Sopenharmony_ci * 103162306a36Sopenharmony_ci * Drivers may want to call this on unload to ensure that all displays are 103262306a36Sopenharmony_ci * unlit and the GPU is in a consistent, low power state. Takes modeset locks. 103362306a36Sopenharmony_ci * 103462306a36Sopenharmony_ci * Note: This should only be used by non-atomic legacy drivers. For an atomic 103562306a36Sopenharmony_ci * version look at drm_atomic_helper_shutdown(). 103662306a36Sopenharmony_ci * 103762306a36Sopenharmony_ci * Returns: 103862306a36Sopenharmony_ci * Zero on success, error code on failure. 103962306a36Sopenharmony_ci */ 104062306a36Sopenharmony_ciint drm_helper_force_disable_all(struct drm_device *dev) 104162306a36Sopenharmony_ci{ 104262306a36Sopenharmony_ci struct drm_crtc *crtc; 104362306a36Sopenharmony_ci int ret = 0; 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci drm_modeset_lock_all(dev); 104662306a36Sopenharmony_ci drm_for_each_crtc(crtc, dev) 104762306a36Sopenharmony_ci if (crtc->enabled) { 104862306a36Sopenharmony_ci struct drm_mode_set set = { 104962306a36Sopenharmony_ci .crtc = crtc, 105062306a36Sopenharmony_ci }; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci ret = drm_mode_set_config_internal(&set); 105362306a36Sopenharmony_ci if (ret) 105462306a36Sopenharmony_ci goto out; 105562306a36Sopenharmony_ci } 105662306a36Sopenharmony_ciout: 105762306a36Sopenharmony_ci drm_modeset_unlock_all(dev); 105862306a36Sopenharmony_ci return ret; 105962306a36Sopenharmony_ci} 106062306a36Sopenharmony_ciEXPORT_SYMBOL(drm_helper_force_disable_all); 1061