From patchwork Fri Sep 13 08:23:38 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Gupta, Anshuman" X-Patchwork-Id: 11144089 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 94D321599 for ; Fri, 13 Sep 2019 08:30:29 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 7DEB32089F for ; Fri, 13 Sep 2019 08:30:29 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 7DEB32089F Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=intel.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=intel-gfx-bounces@lists.freedesktop.org Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 1CE966EED3; Fri, 13 Sep 2019 08:30:29 +0000 (UTC) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from mga18.intel.com (mga18.intel.com [134.134.136.126]) by gabe.freedesktop.org (Postfix) with ESMTPS id 9C11D6EED3 for ; Fri, 13 Sep 2019 08:30:26 +0000 (UTC) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga007.fm.intel.com ([10.253.24.52]) by orsmga106.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 13 Sep 2019 01:30:26 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.64,500,1559545200"; d="scan'208";a="186375249" Received: from genxfsim-desktop.iind.intel.com ([10.223.74.120]) by fmsmga007.fm.intel.com with ESMTP; 13 Sep 2019 01:30:23 -0700 From: Anshuman Gupta To: intel-gfx@lists.freedesktop.org Date: Fri, 13 Sep 2019 13:53:38 +0530 Message-Id: <20190913082339.1785-7-anshuman.gupta@intel.com> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190913082339.1785-1-anshuman.gupta@intel.com> References: <20190913082339.1785-1-anshuman.gupta@intel.com> MIME-Version: 1.0 Subject: [Intel-gfx] [PATCH v8 6/7] drm/i915/tgl: switch between dc3co and dc5 based on display idleness X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: jani.nikula@intel.com Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" DC3CO is useful power state, when DMC detects PSR2 idle frame while an active video playback, playing 30fps video on 60hz panel is the classic example of this use case. B.Specs:49196 has a restriction to enable DC3CO only for Video Playback. It will be worthy to enable DC3CO after completion of each pageflip and switch back to DC5 when display is idle because driver doesn't differentiate between video playback and a normal pageflip. We will use Frontbuffer flush call tgl_dc3co_flush() to enable DC3CO state only for ORIGIN_FLIP flush call, because DC3CO state has primarily targeted for VPB use case. We are not interested here for frontbuffer invalidates calls because that triggers PSR2 exit, which will explicitly disable DC3CO. DC5 and DC6 saves more power, but can't be entered during video playback because there are not enough idle frames in a row to meet most PSR2 panel deep sleep entry requirement typically 4 frames. As PSR2 existing implementation is using minimum 6 idle frames for deep sleep, it is safer to enable DC5/6 after 6 idle frames (By scheduling a delayed work of 6 idle frames, once DC3CO has been enabled after a pageflip). After manually waiting for 6 idle frames DC5/6 will be enabled and PSR2 deep sleep idle frames will be restored to 6 idle frames, at this point DMC will triggers DC5/6 once PSR2 enters to deep sleep after 6 idle frames. In future when we will enable S/W PSR2 tracking, we can change the PSR2 required deep sleep idle frames to 1 so DMC can trigger the DC5/6 immediately after S/W manual waiting of 6 idle frames get complete. v2: calculated s/w state to switch over dc3co when there is an update. [Imre] Used cancel_delayed_work_sync() in order to avoid any race with already scheduled delayed work. [Imre] v3: Cancel_delayed_work_sync() may blocked the commit work. hence dropping it, dc5_idle_thread() checks the valid wakeref before putting the reference count, which avoids any chances of dropping a zero wakeref. [Imre (IRC)] v4: Used frontbuffer flush mechanism. [Imre] v5: Used psr.pipe to extract frontbuffer busy bits. [Imre] Used cancel_delayed_work_sync() in encoder disable path. [Imre] Used mod_delayed_work() instead of cancelling and scheduling a delayed work. [Imre] Used psr.lock in tgl_dc5_idle_thread() to enable psr2 deep sleep. [Imre] Removed DC5_REQ_IDLE_FRAMES macro. [Imre] Cc: Jani Nikula Cc: Imre Deak Cc: Animesh Manna Signed-off-by: Anshuman Gupta --- .../drm/i915/display/intel_display_power.c | 97 +++++++++++++++++++ .../drm/i915/display/intel_display_power.h | 4 + .../gpu/drm/i915/display/intel_frontbuffer.c | 1 + drivers/gpu/drm/i915/display/intel_psr.c | 1 + drivers/gpu/drm/i915/i915_drv.h | 1 + 5 files changed, 104 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_display_power.c b/drivers/gpu/drm/i915/display/intel_display_power.c index 42eabcdecf00..a605047cb28a 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power.c +++ b/drivers/gpu/drm/i915/display/intel_display_power.c @@ -20,6 +20,7 @@ #include "intel_sideband.h" #include "intel_tc.h" #include "intel_pm.h" +#include "intel_psr.h" bool intel_display_power_well_is_enabled(struct drm_i915_private *dev_priv, enum i915_power_well_id power_well_id); @@ -773,6 +774,27 @@ static void gen9_set_dc_state(struct drm_i915_private *dev_priv, u32 state) dev_priv->csr.dc_state = val & mask; } +static u32 intel_get_frame_time_us(const struct intel_crtc_state *cstate) +{ + u32 pixel_rate, crtc_htotal, crtc_vtotal; + u32 frametime_us; + + if (!cstate || !cstate->base.active) + return 0; + + pixel_rate = cstate->pixel_rate; + + if (WARN_ON(pixel_rate == 0)) + return 0; + + crtc_htotal = cstate->base.adjusted_mode.crtc_htotal; + crtc_vtotal = cstate->base.adjusted_mode.crtc_vtotal; + frametime_us = DIV_ROUND_UP(crtc_htotal * crtc_vtotal * 1000ULL, + pixel_rate); + + return frametime_us; +} + void tgl_disable_psr2_transcoder_exitline(const struct intel_crtc_state *cstate) { struct drm_i915_private *dev_priv = to_i915(cstate->base.crtc->dev); @@ -784,6 +806,9 @@ void tgl_disable_psr2_transcoder_exitline(const struct intel_crtc_state *cstate) val = I915_READ(EXITLINE(cstate->cpu_transcoder)); val &= ~EXITLINE_ENABLE; I915_WRITE(EXITLINE(cstate->cpu_transcoder), val); + + /* As psr2 encoder has disabled, cancel the dc5 idle delayed work */ + cancel_delayed_work_sync(&dev_priv->csr.idle_work); } void tgl_enable_psr2_transcoder_exitline(const struct intel_crtc_state *cstate) @@ -817,6 +842,60 @@ void tgl_enable_psr2_transcoder_exitline(const struct intel_crtc_state *cstate) I915_WRITE(EXITLINE(cstate->cpu_transcoder), val); } +/* + * When we will enable manual PSR2 S/W tracking in future + * we will implement this entire DC3CO flush logic in + * intel_psr_flush(). + */ +void tgl_dc3co_flush(struct drm_i915_private *dev_priv, + unsigned int frontbuffer_bits, enum fb_op_origin origin) +{ + struct intel_crtc_state *cstate; + struct intel_crtc *crtc; + u32 delay; + unsigned int busy_frontbuffer_bits; + + if (!IS_TIGERLAKE(dev_priv)) + return; + + if (!(dev_priv->csr.allowed_dc_mask & DC_STATE_EN_DC3CO)) + return; + + if (origin != ORIGIN_FLIP) + return; + + mutex_lock(&dev_priv->psr.lock); + crtc = intel_get_crtc_for_pipe(dev_priv, dev_priv->psr.pipe); + cstate = to_intel_crtc_state(crtc->base.state); + + frontbuffer_bits &= + INTEL_FRONTBUFFER_ALL_MASK(dev_priv->psr.pipe); + + busy_frontbuffer_bits &= ~frontbuffer_bits; + + if (!dev_priv->psr.psr2_enabled || !dev_priv->psr.active) + goto unlock; + + /* + * At every flip frontbuffer flush modified delay of delayed work, + * when delayed schedules that means display has been idle. + */ + if (!busy_frontbuffer_bits) { + if (!dev_priv->psr.psr2_deep_slp_disabled) { + tgl_psr2_deep_sleep_disable(dev_priv); + dev_priv->psr.psr2_deep_slp_disabled = true; + } + tgl_set_target_dc_state(dev_priv, DC_STATE_EN_DC3CO); + /* DC5/DC6 required idle frames = 6 */ + delay = 6 * intel_get_frame_time_us(cstate); + mod_delayed_work(system_wq, &dev_priv->csr.idle_work, + usecs_to_jiffies(delay)); + } + +unlock: + mutex_unlock(&dev_priv->psr.lock); +} + static bool tgl_dc3co_is_edp_connected(struct intel_crtc_state *crtc_state) { struct drm_atomic_state *state = crtc_state->base.state; @@ -876,6 +955,23 @@ void tgl_dc3co_crtc_get_config(struct intel_crtc_state *crtc_state) crtc_state->has_dc3co_exitline = true; } +void tgl_dc5_idle_thread(struct work_struct *work) +{ + struct drm_i915_private *dev_priv = + container_of(work, typeof(*dev_priv), csr.idle_work.work); + + /* If delayed work is pending, it is not idle */ + if (delayed_work_pending(&dev_priv->csr.idle_work)) + return; + + DRM_DEBUG_KMS("DC5/6 idle thread\n"); + mutex_lock(&dev_priv->psr.lock); + tgl_set_target_dc_state(dev_priv, DC_STATE_EN_UPTO_DC6); + tgl_psr2_deep_sleep_enable(dev_priv); + dev_priv->psr.psr2_deep_slp_disabled = false; + mutex_unlock(&dev_priv->psr.lock); +} + static void allowed_dc_mask_to_target_dc_state(struct drm_i915_private *dev_priv) { @@ -4237,6 +4333,7 @@ int intel_power_domains_init(struct drm_i915_private *dev_priv) INIT_DELAYED_WORK(&power_domains->async_put_work, intel_display_power_put_async_work); + INIT_DELAYED_WORK(&dev_priv->csr.idle_work, tgl_dc5_idle_thread); /* * The enabling order will be from lower to higher indexed wells, diff --git a/drivers/gpu/drm/i915/display/intel_display_power.h b/drivers/gpu/drm/i915/display/intel_display_power.h index bf68f26a5a37..87725a23fead 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power.h +++ b/drivers/gpu/drm/i915/display/intel_display_power.h @@ -9,6 +9,7 @@ #include "intel_display.h" #include "intel_runtime_pm.h" #include "i915_reg.h" +#include "intel_frontbuffer.h" struct drm_i915_private; struct intel_encoder; @@ -265,6 +266,9 @@ void tgl_dc3co_crtc_compute_config(struct drm_i915_private *dev_priv, void tgl_dc3co_crtc_get_config(struct intel_crtc_state *crtc_state); void tgl_disable_psr2_transcoder_exitline(const struct intel_crtc_state *state); void tgl_enable_psr2_transcoder_exitline(const struct intel_crtc_state *state); +void tgl_dc3co_flush(struct drm_i915_private *dev_priv, + unsigned int frontbuffer_bits, enum fb_op_origin origin); +void tgl_dc5_idle_thread(struct work_struct *work); const char * intel_display_power_domain_str(enum intel_display_power_domain domain); diff --git a/drivers/gpu/drm/i915/display/intel_frontbuffer.c b/drivers/gpu/drm/i915/display/intel_frontbuffer.c index fc40dc1fdbcc..c3b10f6e4382 100644 --- a/drivers/gpu/drm/i915/display/intel_frontbuffer.c +++ b/drivers/gpu/drm/i915/display/intel_frontbuffer.c @@ -90,6 +90,7 @@ static void frontbuffer_flush(struct drm_i915_private *i915, might_sleep(); intel_edp_drrs_flush(i915, frontbuffer_bits); intel_psr_flush(i915, frontbuffer_bits, origin); + tgl_dc3co_flush(i915, frontbuffer_bits, origin); intel_fbc_flush(i915, frontbuffer_bits, origin); } diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index 11d37f96ce71..552eaf2bd16f 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -574,6 +574,7 @@ static void tgl_disallow_dc3co_on_psr2_exit(struct drm_i915_private *dev_priv) cancel_delayed_work(&dev_priv->csr.idle_work); /* Before PSR2 exit disallow dc3co*/ tgl_set_target_dc_state(dev_priv, DC_STATE_EN_UPTO_DC6); + dev_priv->psr.psr2_deep_slp_disabled = false; } static bool intel_psr2_config_valid(struct intel_dp *intel_dp, diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 4521b9381db3..25591c128c59 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -339,6 +339,7 @@ struct intel_csr { u32 dc_state; u32 target_dc_state; u32 allowed_dc_mask; + struct delayed_work idle_work; intel_wakeref_t wakeref; };