From patchwork Thu Nov 5 18:50:06 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Rodrigo Vivi X-Patchwork-Id: 7563261 Return-Path: X-Original-To: patchwork-intel-gfx@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id D23F59F2F7 for ; Thu, 5 Nov 2015 18:50:00 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id A572F20791 for ; Thu, 5 Nov 2015 18:49:58 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id 21B5F206EE for ; Thu, 5 Nov 2015 18:49:55 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 293186E4B9; Thu, 5 Nov 2015 10:49:54 -0800 (PST) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from mga09.intel.com (mga09.intel.com [134.134.136.24]) by gabe.freedesktop.org (Postfix) with ESMTP id 300236E4B7 for ; Thu, 5 Nov 2015 10:49:50 -0800 (PST) Received: from orsmga002.jf.intel.com ([10.7.209.21]) by orsmga102.jf.intel.com with ESMTP; 05 Nov 2015 10:49:45 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.20,248,1444719600"; d="scan'208";a="843493289" Received: from rdvivi-hillsboro.jf.intel.com ([10.7.196.156]) by orsmga002.jf.intel.com with ESMTP; 05 Nov 2015 10:49:42 -0800 From: Rodrigo Vivi To: intel-gfx@lists.freedesktop.org Date: Thu, 5 Nov 2015 10:50:06 -0800 Message-Id: <1446749423-4789-15-git-send-email-rodrigo.vivi@intel.com> X-Mailer: git-send-email 2.4.3 In-Reply-To: <1446749423-4789-1-git-send-email-rodrigo.vivi@intel.com> References: <1446749423-4789-1-git-send-email-rodrigo.vivi@intel.com> MIME-Version: 1.0 Cc: Rodrigo Vivi Subject: [Intel-gfx] [PATCH 14/31] drm/i915: Create intel_drrs.c X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP No functional change. Jusr organizing display pm features in a standardized and uniform way. This first step allow us to fix drrs initialization that is broken with fastboot by default. Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/i915/Makefile | 3 +- drivers/gpu/drm/i915/intel_ddi.c | 4 +- drivers/gpu/drm/i915/intel_dp.c | 386 +---------------------------- drivers/gpu/drm/i915/intel_drrs.c | 408 +++++++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_drv.h | 15 +- drivers/gpu/drm/i915/intel_frontbuffer.c | 4 +- 6 files changed, 426 insertions(+), 394 deletions(-) create mode 100644 drivers/gpu/drm/i915/intel_drrs.c diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 5c92a47..da3d2cc 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -51,7 +51,8 @@ i915-y += intel_renderstate_gen6.o \ intel_renderstate_gen9.o # display pm -i915-y += intel_fbc.o \ +i915-y += intel_drrs.o \ + intel_fbc.o \ intel_ips.o \ intel_psr.o diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 36db970..972acef 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -2404,7 +2404,7 @@ static void intel_enable_ddi(struct intel_encoder *intel_encoder) intel_dp_stop_link_train(intel_dp); intel_edp_backlight_on(intel_dp); - intel_edp_drrs_enable(intel_dp); + intel_drrs_enable(intel_dp); } if (intel_crtc->config->has_audio) { @@ -2430,7 +2430,7 @@ static void intel_disable_ddi(struct intel_encoder *intel_encoder) if (type == INTEL_OUTPUT_EDP) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); - intel_edp_drrs_disable(intel_dp); + intel_drrs_disable(intel_dp); intel_edp_backlight_off(intel_dp); } } diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index f0ee497..a421c17 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -5212,388 +5212,6 @@ intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev, I915_READ(pp_div_reg)); } -/** - * intel_dp_set_drrs_state - program registers for RR switch to take effect - * @dev: DRM device - * @refresh_rate: RR to be programmed - * - * This function gets called when refresh rate (RR) has to be changed from - * one frequency to another. Switches can be between high and low RR - * supported by the panel or to any other RR based on media playback (in - * this case, RR value needs to be passed from user space). - * - * The caller of this function needs to take a lock on dev_priv->drrs. - */ -static void intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_encoder *encoder; - struct intel_digital_port *dig_port = NULL; - struct intel_dp *intel_dp = dev_priv->drrs.dp; - struct intel_crtc_state *config = NULL; - struct intel_crtc *intel_crtc = NULL; - enum drrs_refresh_rate_type index = DRRS_HIGH_RR; - - if (refresh_rate <= 0) { - DRM_DEBUG_KMS("Refresh rate should be positive non-zero.\n"); - return; - } - - if (intel_dp == NULL) { - DRM_DEBUG_KMS("DRRS not supported.\n"); - return; - } - - dig_port = dp_to_dig_port(intel_dp); - encoder = &dig_port->base; - intel_crtc = to_intel_crtc(encoder->base.crtc); - - if (!intel_crtc) { - DRM_DEBUG_KMS("DRRS: intel_crtc not initialized\n"); - return; - } - - config = intel_crtc->config; - - if (dev_priv->drrs.type < SEAMLESS_DRRS_SUPPORT) { - DRM_DEBUG_KMS("Only Seamless DRRS supported.\n"); - return; - } - - if (intel_dp->attached_connector->panel.downclock_mode->vrefresh == - refresh_rate) - index = DRRS_LOW_RR; - - if (index == dev_priv->drrs.refresh_rate_type) { - DRM_DEBUG_KMS( - "DRRS requested for previously set RR...ignoring\n"); - return; - } - - if (!intel_crtc->active) { - DRM_DEBUG_KMS("eDP encoder disabled. CRTC not Active\n"); - return; - } - - if (INTEL_INFO(dev)->gen >= 8 && !IS_CHERRYVIEW(dev)) { - switch (index) { - case DRRS_HIGH_RR: - intel_dp_set_m_n(intel_crtc, M1_N1); - break; - case DRRS_LOW_RR: - intel_dp_set_m_n(intel_crtc, M2_N2); - break; - case DRRS_MAX_RR: - default: - DRM_ERROR("Unsupported refreshrate type\n"); - } - } else if (INTEL_INFO(dev)->gen > 6) { - u32 reg = PIPECONF(intel_crtc->config->cpu_transcoder); - u32 val; - - val = I915_READ(reg); - if (index > DRRS_HIGH_RR) { - if (IS_VALLEYVIEW(dev)) - val |= PIPECONF_EDP_RR_MODE_SWITCH_VLV; - else - val |= PIPECONF_EDP_RR_MODE_SWITCH; - } else { - if (IS_VALLEYVIEW(dev)) - val &= ~PIPECONF_EDP_RR_MODE_SWITCH_VLV; - else - val &= ~PIPECONF_EDP_RR_MODE_SWITCH; - } - I915_WRITE(reg, val); - } - - dev_priv->drrs.refresh_rate_type = index; - - DRM_DEBUG_KMS("eDP Refresh Rate set to : %dHz\n", refresh_rate); -} - -/** - * intel_edp_drrs_enable - init drrs struct if supported - * @intel_dp: DP struct - * - * Initializes frontbuffer_bits and drrs.dp - */ -void intel_edp_drrs_enable(struct intel_dp *intel_dp) -{ - struct drm_device *dev = intel_dp_to_dev(intel_dp); - struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); - struct drm_crtc *crtc = dig_port->base.base.crtc; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - - if (!intel_crtc->config->has_drrs) { - DRM_DEBUG_KMS("Panel doesn't support DRRS\n"); - return; - } - - if (intel_crtc->config->psr_ready && i915.enable_psr) { - DRM_DEBUG_KMS("DRRS: PSR will be enabled on this crtc\n"); - return; - } - - mutex_lock(&dev_priv->drrs.mutex); - if (WARN_ON(dev_priv->drrs.dp)) { - DRM_ERROR("DRRS already enabled\n"); - goto unlock; - } - - dev_priv->drrs.busy_frontbuffer_bits = 0; - - dev_priv->drrs.dp = intel_dp; - -unlock: - mutex_unlock(&dev_priv->drrs.mutex); -} - -/** - * intel_edp_drrs_disable - Disable DRRS - * @intel_dp: DP struct - * - */ -void intel_edp_drrs_disable(struct intel_dp *intel_dp) -{ - struct drm_device *dev = intel_dp_to_dev(intel_dp); - struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); - struct drm_crtc *crtc = dig_port->base.base.crtc; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - - if (!intel_crtc->config->has_drrs) - return; - - mutex_lock(&dev_priv->drrs.mutex); - if (!dev_priv->drrs.dp) { - mutex_unlock(&dev_priv->drrs.mutex); - return; - } - - if (dev_priv->drrs.refresh_rate_type == DRRS_LOW_RR) - intel_dp_set_drrs_state(dev_priv->dev, - intel_dp->attached_connector->panel. - fixed_mode->vrefresh); - - dev_priv->drrs.dp = NULL; - mutex_unlock(&dev_priv->drrs.mutex); - - cancel_delayed_work_sync(&dev_priv->drrs.work); -} - -static void intel_edp_drrs_downclock_work(struct work_struct *work) -{ - struct drm_i915_private *dev_priv = - container_of(work, typeof(*dev_priv), drrs.work.work); - struct intel_dp *intel_dp; - - mutex_lock(&dev_priv->drrs.mutex); - - intel_dp = dev_priv->drrs.dp; - - if (!intel_dp) - goto unlock; - - /* - * The delayed work can race with an invalidate hence we need to - * recheck. - */ - - if (dev_priv->drrs.busy_frontbuffer_bits) - goto unlock; - - if (dev_priv->drrs.refresh_rate_type != DRRS_LOW_RR) - intel_dp_set_drrs_state(dev_priv->dev, - intel_dp->attached_connector->panel. - downclock_mode->vrefresh); - -unlock: - mutex_unlock(&dev_priv->drrs.mutex); -} - -/** - * intel_edp_drrs_invalidate - Disable Idleness DRRS - * @dev: DRM device - * @frontbuffer_bits: frontbuffer plane tracking bits - * - * This function gets called everytime rendering on the given planes start. - * Hence DRRS needs to be Upclocked, i.e. (LOW_RR -> HIGH_RR). - * - * Dirty frontbuffers relevant to DRRS are tracked in busy_frontbuffer_bits. - */ -void intel_edp_drrs_invalidate(struct drm_device *dev, - unsigned frontbuffer_bits) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_crtc *crtc; - enum pipe pipe; - - if (dev_priv->drrs.type == DRRS_NOT_SUPPORTED) - return; - - cancel_delayed_work(&dev_priv->drrs.work); - - mutex_lock(&dev_priv->drrs.mutex); - if (!dev_priv->drrs.dp) { - mutex_unlock(&dev_priv->drrs.mutex); - return; - } - - crtc = dp_to_dig_port(dev_priv->drrs.dp)->base.base.crtc; - pipe = to_intel_crtc(crtc)->pipe; - - frontbuffer_bits &= INTEL_FRONTBUFFER_ALL_MASK(pipe); - dev_priv->drrs.busy_frontbuffer_bits |= frontbuffer_bits; - - /* invalidate means busy screen hence upclock */ - if (frontbuffer_bits && dev_priv->drrs.refresh_rate_type == DRRS_LOW_RR) - intel_dp_set_drrs_state(dev_priv->dev, - dev_priv->drrs.dp->attached_connector->panel. - fixed_mode->vrefresh); - - mutex_unlock(&dev_priv->drrs.mutex); -} - -/** - * intel_edp_drrs_flush - Restart Idleness DRRS - * @dev: DRM device - * @frontbuffer_bits: frontbuffer plane tracking bits - * - * This function gets called every time rendering on the given planes has - * completed or flip on a crtc is completed. So DRRS should be upclocked - * (LOW_RR -> HIGH_RR). And also Idleness detection should be started again, - * if no other planes are dirty. - * - * Dirty frontbuffers relevant to DRRS are tracked in busy_frontbuffer_bits. - */ -void intel_edp_drrs_flush(struct drm_device *dev, - unsigned frontbuffer_bits) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_crtc *crtc; - enum pipe pipe; - - if (dev_priv->drrs.type == DRRS_NOT_SUPPORTED) - return; - - cancel_delayed_work(&dev_priv->drrs.work); - - mutex_lock(&dev_priv->drrs.mutex); - if (!dev_priv->drrs.dp) { - mutex_unlock(&dev_priv->drrs.mutex); - return; - } - - crtc = dp_to_dig_port(dev_priv->drrs.dp)->base.base.crtc; - pipe = to_intel_crtc(crtc)->pipe; - - frontbuffer_bits &= INTEL_FRONTBUFFER_ALL_MASK(pipe); - dev_priv->drrs.busy_frontbuffer_bits &= ~frontbuffer_bits; - - /* flush means busy screen hence upclock */ - if (frontbuffer_bits && dev_priv->drrs.refresh_rate_type == DRRS_LOW_RR) - intel_dp_set_drrs_state(dev_priv->dev, - dev_priv->drrs.dp->attached_connector->panel. - fixed_mode->vrefresh); - - /* - * flush also means no more activity hence schedule downclock, if all - * other fbs are quiescent too - */ - if (!dev_priv->drrs.busy_frontbuffer_bits) - schedule_delayed_work(&dev_priv->drrs.work, - msecs_to_jiffies(1000)); - mutex_unlock(&dev_priv->drrs.mutex); -} - -/** - * DOC: Display Refresh Rate Switching (DRRS) - * - * Display Refresh Rate Switching (DRRS) is a power conservation feature - * which enables swtching between low and high refresh rates, - * dynamically, based on the usage scenario. This feature is applicable - * for internal panels. - * - * Indication that the panel supports DRRS is given by the panel EDID, which - * would list multiple refresh rates for one resolution. - * - * DRRS is of 2 types - static and seamless. - * Static DRRS involves changing refresh rate (RR) by doing a full modeset - * (may appear as a blink on screen) and is used in dock-undock scenario. - * Seamless DRRS involves changing RR without any visual effect to the user - * and can be used during normal system usage. This is done by programming - * certain registers. - * - * Support for static/seamless DRRS may be indicated in the VBT based on - * inputs from the panel spec. - * - * DRRS saves power by switching to low RR based on usage scenarios. - * - * eDP DRRS:- - * The implementation is based on frontbuffer tracking implementation. - * When there is a disturbance on the screen triggered by user activity or a - * periodic system activity, DRRS is disabled (RR is changed to high RR). - * When there is no movement on screen, after a timeout of 1 second, a switch - * to low RR is made. - * For integration with frontbuffer tracking code, - * intel_edp_drrs_invalidate() and intel_edp_drrs_flush() are called. - * - * DRRS can be further extended to support other internal panels and also - * the scenario of video playback wherein RR is set based on the rate - * requested by userspace. - */ - -/** - * intel_dp_drrs_init - Init basic DRRS work and mutex. - * @intel_connector: eDP connector - * @fixed_mode: preferred mode of panel - * - * This function is called only once at driver load to initialize basic - * DRRS stuff. - * - * Returns: - * Downclock mode if panel supports it, else return NULL. - * DRRS support is determined by the presence of downclock mode (apart - * from VBT setting). - */ -static struct drm_display_mode * -intel_dp_drrs_init(struct intel_connector *intel_connector, - struct drm_display_mode *fixed_mode) -{ - struct drm_connector *connector = &intel_connector->base; - struct drm_device *dev = connector->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_display_mode *downclock_mode = NULL; - - INIT_DELAYED_WORK(&dev_priv->drrs.work, intel_edp_drrs_downclock_work); - mutex_init(&dev_priv->drrs.mutex); - - if (INTEL_INFO(dev)->gen <= 6) { - DRM_DEBUG_KMS("DRRS supported for Gen7 and above\n"); - return NULL; - } - - if (dev_priv->vbt.drrs_type != SEAMLESS_DRRS_SUPPORT) { - DRM_DEBUG_KMS("VBT doesn't support DRRS\n"); - return NULL; - } - - downclock_mode = intel_find_panel_downclock - (dev, fixed_mode, connector); - - if (!downclock_mode) { - DRM_DEBUG_KMS("Downclock mode is not found. DRRS not supported\n"); - return NULL; - } - - dev_priv->drrs.type = dev_priv->vbt.drrs_type; - - dev_priv->drrs.refresh_rate_type = DRRS_HIGH_RR; - DRM_DEBUG_KMS("seamless DRRS supported for eDP panel.\n"); - return downclock_mode; -} - static bool intel_edp_init_connector(struct intel_dp *intel_dp, struct intel_connector *intel_connector) { @@ -5655,8 +5273,8 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp, list_for_each_entry(scan, &connector->probed_modes, head) { if ((scan->type & DRM_MODE_TYPE_PREFERRED)) { fixed_mode = drm_mode_duplicate(dev, scan); - downclock_mode = intel_dp_drrs_init( - intel_connector, fixed_mode); + downclock_mode = intel_drrs_init(intel_connector, + fixed_mode); break; } } diff --git a/drivers/gpu/drm/i915/intel_drrs.c b/drivers/gpu/drm/i915/intel_drrs.c new file mode 100644 index 0000000..dce16ea --- /dev/null +++ b/drivers/gpu/drm/i915/intel_drrs.c @@ -0,0 +1,408 @@ +/* + * Copyright © 2015 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "intel_drv.h" +#include "i915_drv.h" + +/** + * DOC: Display Refresh Rate Switching (DRRS) + * + * Display Refresh Rate Switching (DRRS) is a power conservation feature + * which enables swtching between low and high refresh rates, + * dynamically, based on the usage scenario. This feature is applicable + * for internal panels. + * + * Indication that the panel supports DRRS is given by the panel EDID, which + * would list multiple refresh rates for one resolution. + * + * DRRS is of 2 types - static and seamless. + * Static DRRS involves changing refresh rate (RR) by doing a full modeset + * (may appear as a blink on screen) and is used in dock-undock scenario. + * Seamless DRRS involves changing RR without any visual effect to the user + * and can be used during normal system usage. This is done by programming + * certain registers. + * + * Support for static/seamless DRRS may be indicated in the VBT based on + * inputs from the panel spec. + * + * DRRS saves power by switching to low RR based on usage scenarios. + * + * eDP DRRS:- + * The implementation is based on frontbuffer tracking implementation. + * When there is a disturbance on the screen triggered by user activity or a + * periodic system activity, DRRS is disabled (RR is changed to high RR). + * When there is no movement on screen, after a timeout of 1 second, a switch + * to low RR is made. + * For integration with frontbuffer tracking code, + * intel_edp_drrs_invalidate() and intel_edp_drrs_flush() are called. + * + * DRRS can be further extended to support other internal panels and also + * the scenario of video playback wherein RR is set based on the rate + * requested by userspace. + */ + +/** + * intel_drrs_set_state - program registers for RR switch to take effect + * @dev: DRM device + * @refresh_rate: RR to be programmed + * + * This function gets called when refresh rate (RR) has to be changed from + * one frequency to another. Switches can be between high and low RR + * supported by the panel or to any other RR based on media playback (in + * this case, RR value needs to be passed from user space). + * + * The caller of this function needs to take a lock on dev_priv->drrs. + */ +static void intel_drrs_set_state(struct drm_device *dev, int refresh_rate) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_encoder *encoder; + struct intel_digital_port *dig_port = NULL; + struct intel_dp *intel_dp = dev_priv->drrs.dp; + struct intel_crtc_state *config = NULL; + struct intel_crtc *intel_crtc = NULL; + enum drrs_refresh_rate_type index = DRRS_HIGH_RR; + + if (refresh_rate <= 0) { + DRM_DEBUG_KMS("Refresh rate should be positive non-zero.\n"); + return; + } + + if (intel_dp == NULL) { + DRM_DEBUG_KMS("DRRS not supported.\n"); + return; + } + + dig_port = dp_to_dig_port(intel_dp); + encoder = &dig_port->base; + intel_crtc = to_intel_crtc(encoder->base.crtc); + + if (!intel_crtc) { + DRM_DEBUG_KMS("DRRS: intel_crtc not initialized\n"); + return; + } + + config = intel_crtc->config; + + if (dev_priv->drrs.type < SEAMLESS_DRRS_SUPPORT) { + DRM_DEBUG_KMS("Only Seamless DRRS supported.\n"); + return; + } + + if (intel_dp->attached_connector->panel.downclock_mode->vrefresh == + refresh_rate) + index = DRRS_LOW_RR; + + if (index == dev_priv->drrs.refresh_rate_type) { + DRM_DEBUG_KMS( + "DRRS requested for previously set RR...ignoring\n"); + return; + } + + if (!intel_crtc->active) { + DRM_DEBUG_KMS("eDP encoder disabled. CRTC not Active\n"); + return; + } + + if (INTEL_INFO(dev)->gen >= 8 && !IS_CHERRYVIEW(dev)) { + switch (index) { + case DRRS_HIGH_RR: + intel_dp_set_m_n(intel_crtc, M1_N1); + break; + case DRRS_LOW_RR: + intel_dp_set_m_n(intel_crtc, M2_N2); + break; + case DRRS_MAX_RR: + default: + DRM_ERROR("Unsupported refreshrate type\n"); + } + } else if (INTEL_INFO(dev)->gen > 6) { + u32 reg = PIPECONF(intel_crtc->config->cpu_transcoder); + u32 val; + + val = I915_READ(reg); + if (index > DRRS_HIGH_RR) { + if (IS_VALLEYVIEW(dev)) + val |= PIPECONF_EDP_RR_MODE_SWITCH_VLV; + else + val |= PIPECONF_EDP_RR_MODE_SWITCH; + } else { + if (IS_VALLEYVIEW(dev)) + val &= ~PIPECONF_EDP_RR_MODE_SWITCH_VLV; + else + val &= ~PIPECONF_EDP_RR_MODE_SWITCH; + } + I915_WRITE(reg, val); + } + + dev_priv->drrs.refresh_rate_type = index; + + DRM_DEBUG_KMS("eDP Refresh Rate set to : %dHz\n", refresh_rate); +} + +/** + * intel_drrs_enable - init drrs struct if supported + * @intel_dp: DP struct + * + * Initializes frontbuffer_bits and drrs.dp + */ +void intel_drrs_enable(struct intel_dp *intel_dp) +{ + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); + struct drm_device *dev = dig_port->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_crtc *crtc = dig_port->base.base.crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + + if (!intel_crtc->config->has_drrs) { + DRM_DEBUG_KMS("Panel doesn't support DRRS\n"); + return; + } + + if (intel_crtc->config->psr_ready && i915.enable_psr) { + DRM_DEBUG_KMS("DRRS: PSR will be enabled on this crtc\n"); + return; + } + + mutex_lock(&dev_priv->drrs.mutex); + if (WARN_ON(dev_priv->drrs.dp)) { + DRM_ERROR("DRRS already enabled\n"); + goto unlock; + } + + dev_priv->drrs.busy_frontbuffer_bits = 0; + + dev_priv->drrs.dp = intel_dp; + +unlock: + mutex_unlock(&dev_priv->drrs.mutex); +} + +/** + * intel_edp_drrs_disable - Disable DRRS + * @intel_dp: DP struct + * + */ +void intel_drrs_disable(struct intel_dp *intel_dp) +{ + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); + struct drm_device *dev = dig_port->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_crtc *crtc = dig_port->base.base.crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_panel *panel = &intel_dp->attached_connector->panel; + + if (!intel_crtc->config->has_drrs) + return; + + mutex_lock(&dev_priv->drrs.mutex); + if (!dev_priv->drrs.dp) { + mutex_unlock(&dev_priv->drrs.mutex); + return; + } + + if (dev_priv->drrs.refresh_rate_type == DRRS_LOW_RR) + intel_drrs_set_state(dev_priv->dev, + panel->fixed_mode->vrefresh); + + dev_priv->drrs.dp = NULL; + mutex_unlock(&dev_priv->drrs.mutex); + + cancel_delayed_work_sync(&dev_priv->drrs.work); +} + +static void intel_drrs_downclock_work(struct work_struct *work) +{ + struct drm_i915_private *dev_priv = + container_of(work, typeof(*dev_priv), drrs.work.work); + struct intel_dp *intel_dp; + struct intel_panel *panel; + + mutex_lock(&dev_priv->drrs.mutex); + intel_dp = dev_priv->drrs.dp; + + if (!intel_dp) + goto unlock; + + panel = &intel_dp->attached_connector->panel; + /* + * The delayed work can race with an invalidate hence we need to + * recheck. + */ + + if (dev_priv->drrs.busy_frontbuffer_bits) + goto unlock; + + if (dev_priv->drrs.refresh_rate_type != DRRS_LOW_RR) + intel_drrs_set_state(dev_priv->dev, + panel->fixed_mode->vrefresh); + +unlock: + mutex_unlock(&dev_priv->drrs.mutex); +} + +/** + * intel_drrs_invalidate - Disable Idleness DRRS + * @dev: DRM device + * @frontbuffer_bits: frontbuffer plane tracking bits + * + * This function gets called everytime rendering on the given planes start. + * Hence DRRS needs to be Upclocked, i.e. (LOW_RR -> HIGH_RR). + * + * Dirty frontbuffers relevant to DRRS are tracked in busy_frontbuffer_bits. + */ +void intel_drrs_invalidate(struct drm_device *dev, + unsigned frontbuffer_bits) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_crtc *crtc; + struct intel_panel *panel; + enum pipe pipe; + + if (dev_priv->drrs.type == DRRS_NOT_SUPPORTED) + return; + + cancel_delayed_work(&dev_priv->drrs.work); + + mutex_lock(&dev_priv->drrs.mutex); + if (!dev_priv->drrs.dp) { + mutex_unlock(&dev_priv->drrs.mutex); + return; + } + + panel = &dev_priv->drrs.dp->attached_connector->panel; + crtc = dp_to_dig_port(dev_priv->drrs.dp)->base.base.crtc; + pipe = to_intel_crtc(crtc)->pipe; + + frontbuffer_bits &= INTEL_FRONTBUFFER_ALL_MASK(pipe); + dev_priv->drrs.busy_frontbuffer_bits |= frontbuffer_bits; + + /* invalidate means busy screen hence upclock */ + if (frontbuffer_bits && dev_priv->drrs.refresh_rate_type == DRRS_LOW_RR) + intel_drrs_set_state(dev_priv->dev, + panel->fixed_mode->vrefresh); + + mutex_unlock(&dev_priv->drrs.mutex); +} + +/** + * intel_drrs_flush - Restart Idleness DRRS + * @dev: DRM device + * @frontbuffer_bits: frontbuffer plane tracking bits + * + * This function gets called every time rendering on the given planes has + * completed or flip on a crtc is completed. So DRRS should be upclocked + * (LOW_RR -> HIGH_RR). And also Idleness detection should be started again, + * if no other planes are dirty. + * + * Dirty frontbuffers relevant to DRRS are tracked in busy_frontbuffer_bits. + */ +void intel_drrs_flush(struct drm_device *dev, + unsigned frontbuffer_bits) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_crtc *crtc; + struct intel_panel *panel; + enum pipe pipe; + + if (dev_priv->drrs.type == DRRS_NOT_SUPPORTED) + return; + + cancel_delayed_work(&dev_priv->drrs.work); + + mutex_lock(&dev_priv->drrs.mutex); + if (!dev_priv->drrs.dp) { + mutex_unlock(&dev_priv->drrs.mutex); + return; + } + panel = &dev_priv->drrs.dp->attached_connector->panel; + crtc = dp_to_dig_port(dev_priv->drrs.dp)->base.base.crtc; + pipe = to_intel_crtc(crtc)->pipe; + + frontbuffer_bits &= INTEL_FRONTBUFFER_ALL_MASK(pipe); + dev_priv->drrs.busy_frontbuffer_bits &= ~frontbuffer_bits; + + /* flush means busy screen hence upclock */ + if (frontbuffer_bits && dev_priv->drrs.refresh_rate_type == DRRS_LOW_RR) + intel_drrs_set_state(dev_priv->dev, + panel->fixed_mode->vrefresh); + + /* + * flush also means no more activity hence schedule downclock, if all + * other fbs are quiescent too + */ + if (!dev_priv->drrs.busy_frontbuffer_bits) + schedule_delayed_work(&dev_priv->drrs.work, + msecs_to_jiffies(1000)); + mutex_unlock(&dev_priv->drrs.mutex); +} + +/** + * intel_drrs_init - Init basic DRRS work and mutex. + * @intel_connector: eDP connector + * @fixed_mode: preferred mode of panel + * + * This function is called only once at driver load to initialize basic + * DRRS stuff. + * + * Returns: + * Downclock mode if panel supports it, else return NULL. + * DRRS support is determined by the presence of downclock mode (apart + * from VBT setting). + */ +struct drm_display_mode * +intel_drrs_init(struct intel_connector *intel_connector, + struct drm_display_mode *fixed_mode) +{ + struct drm_connector *connector = &intel_connector->base; + struct drm_device *dev = connector->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_display_mode *downclock_mode = NULL; + + INIT_DELAYED_WORK(&dev_priv->drrs.work, intel_drrs_downclock_work); + mutex_init(&dev_priv->drrs.mutex); + + if (INTEL_INFO(dev)->gen <= 6) { + DRM_DEBUG_KMS("DRRS supported for Gen7 and above\n"); + return NULL; + } + + if (dev_priv->vbt.drrs_type != SEAMLESS_DRRS_SUPPORT) { + DRM_DEBUG_KMS("VBT doesn't support DRRS\n"); + return NULL; + } + + downclock_mode = intel_find_panel_downclock + (dev, fixed_mode, connector); + + if (!downclock_mode) { + DRM_DEBUG_KMS("Downclock mode is not found. DRRS not supported\n"); + return NULL; + } + + dev_priv->drrs.type = dev_priv->vbt.drrs_type; + + dev_priv->drrs.refresh_rate_type = DRRS_HIGH_RR; + DRM_DEBUG_KMS("seamless DRRS supported for eDP panel.\n"); + return downclock_mode; +} diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index d599d54..73406c3 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -1209,6 +1209,16 @@ u32 skl_plane_ctl_format(uint32_t pixel_format); u32 skl_plane_ctl_tiling(uint64_t fb_modifier); u32 skl_plane_ctl_rotation(unsigned int rotation); +/* intel_drrs.c */ +void intel_drrs_enable(struct intel_dp *intel_dp); +void intel_drrs_disable(struct intel_dp *intel_dp); +void intel_drrs_invalidate(struct drm_device *dev, + unsigned frontbuffer_bits); +void intel_drrs_flush(struct drm_device *dev, unsigned frontbuffer_bits); +struct drm_display_mode * +intel_drrs_init(struct intel_connector *intel_connector, + struct drm_display_mode *fixed_mode); + /* intel_ips.c */ bool intel_ips_ready(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state); @@ -1256,11 +1266,6 @@ void intel_dp_hot_plug(struct intel_encoder *intel_encoder); void vlv_power_sequencer_reset(struct drm_i915_private *dev_priv); uint32_t intel_dp_pack_aux(const uint8_t *src, int src_bytes); void intel_plane_destroy(struct drm_plane *plane); -void intel_edp_drrs_enable(struct intel_dp *intel_dp); -void intel_edp_drrs_disable(struct intel_dp *intel_dp); -void intel_edp_drrs_invalidate(struct drm_device *dev, - unsigned frontbuffer_bits); -void intel_edp_drrs_flush(struct drm_device *dev, unsigned frontbuffer_bits); bool intel_digital_port_connected(struct drm_i915_private *dev_priv, struct intel_digital_port *port); void hsw_dp_set_ddi_pll_sel(struct intel_crtc_state *pipe_config); diff --git a/drivers/gpu/drm/i915/intel_frontbuffer.c b/drivers/gpu/drm/i915/intel_frontbuffer.c index ac85357..f76952a 100644 --- a/drivers/gpu/drm/i915/intel_frontbuffer.c +++ b/drivers/gpu/drm/i915/intel_frontbuffer.c @@ -97,7 +97,7 @@ void intel_fb_obj_invalidate(struct drm_i915_gem_object *obj, } intel_psr_invalidate(dev, obj->frontbuffer_bits); - intel_edp_drrs_invalidate(dev, obj->frontbuffer_bits); + intel_drrs_invalidate(dev, obj->frontbuffer_bits); intel_fbc_invalidate(dev_priv, obj->frontbuffer_bits, origin); } @@ -127,7 +127,7 @@ static void intel_frontbuffer_flush(struct drm_device *dev, if (!frontbuffer_bits) return; - intel_edp_drrs_flush(dev, frontbuffer_bits); + intel_drrs_flush(dev, frontbuffer_bits); intel_psr_flush(dev, frontbuffer_bits, origin); intel_fbc_flush(dev_priv, frontbuffer_bits, origin); }