[14/18] drm/i915/gen9: Use flush_work to synchronize with dmc loader
diff mbox

Message ID 1437850839-16782-15-git-send-email-animesh.manna@intel.com
State New
Headers show

Commit Message

Animesh Manna July 25, 2015, 7 p.m. UTC
As power well 1 is superset of power well 2 and always pw2
will be disabled first and then pw1. On the other hand dmc
is responsible to save & restore back pw1 when display
engine goes and come back from low power state. Before
disabling pw1 dmc must be loaded, so adding flush_work()
while disabling pw2 which ensure that firmware will be
available before disabling pw1 in suspend flow.

Cc: Daniel Vetter <daniel.vetter@intel.com>
Cc: Damien Lespiau <damien.lespiau@intel.com>
Cc: Imre Deak <imre.deak@intel.com>
Cc: Sunil Kamath <sunil.kamath@intel.com>
Signed-off-by: Animesh Manna <animesh.manna@intel.com>
---
 drivers/gpu/drm/i915/i915_drv.c         | 2 --
 drivers/gpu/drm/i915/intel_csr.c        | 2 ++
 drivers/gpu/drm/i915/intel_runtime_pm.c | 1 +
 3 files changed, 3 insertions(+), 2 deletions(-)

Comments

Daniel Vetter July 28, 2015, 8:09 a.m. UTC | #1
On Sun, Jul 26, 2015 at 12:30:35AM +0530, Animesh Manna wrote:
> As power well 1 is superset of power well 2 and always pw2
> will be disabled first and then pw1. On the other hand dmc
> is responsible to save & restore back pw1 when display
> engine goes and come back from low power state. Before
> disabling pw1 dmc must be loaded, so adding flush_work()
> while disabling pw2 which ensure that firmware will be
> available before disabling pw1 in suspend flow.
> 
> Cc: Daniel Vetter <daniel.vetter@intel.com>
> Cc: Damien Lespiau <damien.lespiau@intel.com>
> Cc: Imre Deak <imre.deak@intel.com>
> Cc: Sunil Kamath <sunil.kamath@intel.com>
> Signed-off-by: Animesh Manna <animesh.manna@intel.com>
> ---
>  drivers/gpu/drm/i915/i915_drv.c         | 2 --
>  drivers/gpu/drm/i915/intel_csr.c        | 2 ++
>  drivers/gpu/drm/i915/intel_runtime_pm.c | 1 +
>  3 files changed, 3 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
> index 98343eb..ddf8a25 100644
> --- a/drivers/gpu/drm/i915/i915_drv.c
> +++ b/drivers/gpu/drm/i915/i915_drv.c
> @@ -995,8 +995,6 @@ static int i915_pm_resume(struct device *dev)
>  
>  static int skl_suspend_complete(struct drm_i915_private *dev_priv)
>  {
> -	/* Enabling DC6 is not a hard requirement to enter runtime D3 */

Don't we need a flush_work here to make sure dmc is ready before going
into suspend?

> -
>  	skl_uninit_cdclk(dev_priv);
>  
>  	return 0;
> diff --git a/drivers/gpu/drm/i915/intel_csr.c b/drivers/gpu/drm/i915/intel_csr.c
> index 36386324..1858f02 100644
> --- a/drivers/gpu/drm/i915/intel_csr.c
> +++ b/drivers/gpu/drm/i915/intel_csr.c
> @@ -417,5 +417,7 @@ void intel_csr_ucode_fini(struct drm_i915_private *dev_priv)
>  	if (!HAS_CSR(dev_priv))
>  		return;
>  
> +	flush_work(&dev_priv->csr.work);
> +
>  	kfree(dev_priv->csr.dmc_payload);
>  }
> diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c
> index 5f1ae23..a5059e8 100644
> --- a/drivers/gpu/drm/i915/intel_runtime_pm.c
> +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c
> @@ -652,6 +652,7 @@ static void skl_set_power_well(struct drm_i915_private *dev_priv,
>  
>  			if ((GEN9_ENABLE_DC5(dev) || SKL_ENABLE_DC6(dev)) &&
>  				power_well->data == SKL_DISP_PW_2) {
> +				flush_work(&dev_priv->csr.work);

This flush_work here has two problems:
- It's an inversion of control since this is low-level code blocking on
  high-level. I think this is here is safe but in general it's very easy
  to create deadlocks and hence this pattern is discouraged.
- dmc loading might take a while (especially on cros/android where i915 is
  built-in but fw needs to wait for the full system before it can be
  loaded). We do it asynchronously so that i915 init can proceed and we
  can enable outputs really early, but the modeset code also does power
  well get/put calls and might end up in here, resulting in a stall.

If we instead prevent execution of this low-level power-well code by
grabbing a power_domain reference then both problems are solved:
- No flush_work needed since we'll only get into this code when the last
  power domain references is dropped, which necessarily means dmc loader
  has completed.
- No blocking of modesets while dmc loading is still in progress since the
  get/put power_domain calls will simply be no-ops too.

Long story short: If you apply the two patches I mentioned in my reply to
patch 4 there shouldn't be a need for this flush_work here in
skl_set_power_well.

Thanks, Daniel

>  				if (SKL_ENABLE_DC6(dev))
>  					skl_enable_dc6(dev_priv);
>  				else
> -- 
> 2.0.2
> 
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/intel-gfx

Patch
diff mbox

diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 98343eb..ddf8a25 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -995,8 +995,6 @@  static int i915_pm_resume(struct device *dev)
 
 static int skl_suspend_complete(struct drm_i915_private *dev_priv)
 {
-	/* Enabling DC6 is not a hard requirement to enter runtime D3 */
-
 	skl_uninit_cdclk(dev_priv);
 
 	return 0;
diff --git a/drivers/gpu/drm/i915/intel_csr.c b/drivers/gpu/drm/i915/intel_csr.c
index 36386324..1858f02 100644
--- a/drivers/gpu/drm/i915/intel_csr.c
+++ b/drivers/gpu/drm/i915/intel_csr.c
@@ -417,5 +417,7 @@  void intel_csr_ucode_fini(struct drm_i915_private *dev_priv)
 	if (!HAS_CSR(dev_priv))
 		return;
 
+	flush_work(&dev_priv->csr.work);
+
 	kfree(dev_priv->csr.dmc_payload);
 }
diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c
index 5f1ae23..a5059e8 100644
--- a/drivers/gpu/drm/i915/intel_runtime_pm.c
+++ b/drivers/gpu/drm/i915/intel_runtime_pm.c
@@ -652,6 +652,7 @@  static void skl_set_power_well(struct drm_i915_private *dev_priv,
 
 			if ((GEN9_ENABLE_DC5(dev) || SKL_ENABLE_DC6(dev)) &&
 				power_well->data == SKL_DISP_PW_2) {
+				flush_work(&dev_priv->csr.work);
 				if (SKL_ENABLE_DC6(dev))
 					skl_enable_dc6(dev_priv);
 				else