[Intel-gfx] [PATCH] drm/i915/skl: replace csr_mutex by completion in csr firmware loading

Daniel Vetter daniel at ffwll.ch
Thu May 21 05:11:06 PDT 2015


On Thu, May 21, 2015 at 03:49:52PM +0530, Animesh Manna wrote:
> Before enabling dc5/dc6, used wait for completion instead of busy waiting.
> 
> v1:
> - Based on review comment from Daniel replaced mutex and related
> implementation with completion. In current patch not used power
> domain lock, don't want to block runtime power management if dmc
> firmware failed to load. Will analyzing further and possibly send
> as a incremental patch.
> - Based on review comment from Damien, warning for firmware loading
> failure is removed.
> 
> Signed-off-by: Animesh Manna <animesh.manna at intel.com>

Sorry if this cross with my comments, but upon further digging into this
code we don't even need a completion at all. As long as we prevent the
power well from getting disabled by holding a reference for it before we
launch the firmware loader it'll all be fine. And the async work can then
just drop that reference when everything is set up.

Yes this means runtime D3 requires the firmware to be loaded, but that's
imo not a problem.

Also doing it this way is more in-line with how we do all the other async
setup. But there's another serious issue with this design here, see below.


> ---
>  drivers/gpu/drm/i915/i915_dma.c         |  1 -
>  drivers/gpu/drm/i915/i915_drv.c         |  2 +-
>  drivers/gpu/drm/i915/i915_drv.h         | 12 ++-----
>  drivers/gpu/drm/i915/intel_csr.c        | 63 +++------------------------------
>  drivers/gpu/drm/i915/intel_drv.h        |  3 --
>  drivers/gpu/drm/i915/intel_runtime_pm.c | 16 +++------
>  6 files changed, 12 insertions(+), 85 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
> index 78e6ae8..1f5c86c 100644
> --- a/drivers/gpu/drm/i915/i915_dma.c
> +++ b/drivers/gpu/drm/i915/i915_dma.c
> @@ -816,7 +816,6 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
>  	spin_lock_init(&dev_priv->mmio_flip_lock);
>  	mutex_init(&dev_priv->dpio_lock);
>  	mutex_init(&dev_priv->modeset_restore_lock);
> -	mutex_init(&dev_priv->csr.csr_lock);
>  
>  	intel_pm_setup(dev);
>  
> diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
> index 6bb6c47..c102268 100644
> --- a/drivers/gpu/drm/i915/i915_drv.c
> +++ b/drivers/gpu/drm/i915/i915_drv.c
> @@ -1034,7 +1034,7 @@ static int skl_suspend_complete(struct drm_i915_private *dev_priv)
>  	 * This is to ensure that CSR isn't identified as loaded before
>  	 * CSR-loading program is called during runtime-resume.
>  	 */
> -	intel_csr_load_status_set(dev_priv, FW_UNINITIALIZED);
> +	reinit_completion(&dev_priv->csr.is_loaded);
>  
>  	return 0;
>  }
> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> index 43011d7..423afa9 100644
> --- a/drivers/gpu/drm/i915/i915_drv.h
> +++ b/drivers/gpu/drm/i915/i915_drv.h
> @@ -50,6 +50,7 @@
>  #include <linux/intel-iommu.h>
>  #include <linux/kref.h>
>  #include <linux/pm_qos.h>
> +#include <linux/completion.h>
>  
>  /* General customization:
>   */
> @@ -669,23 +670,14 @@ struct intel_uncore {
>  #define for_each_fw_domain(domain__, dev_priv__, i__) \
>  	for_each_fw_domain_mask(domain__, FORCEWAKE_ALL, dev_priv__, i__)
>  
> -enum csr_state {
> -	FW_UNINITIALIZED = 0,
> -	FW_LOADED,
> -	FW_FAILED
> -};
> -
>  struct intel_csr {
> -	/* CSR protection, used to protect firmware loading status: csr_state */
> -	struct mutex csr_lock;
> -
> +	struct completion is_loaded;
>  	const char *fw_path;
>  	__be32 *dmc_payload;
>  	uint32_t dmc_fw_size;
>  	uint32_t mmio_count;
>  	uint32_t mmioaddr[8];
>  	uint32_t mmiodata[8];
> -	enum csr_state state;
>  };
>  
>  #define DEV_INFO_FOR_EACH_FLAG(func, sep) \
> diff --git a/drivers/gpu/drm/i915/intel_csr.c b/drivers/gpu/drm/i915/intel_csr.c
> index d1da2db..fec2bc5 100644
> --- a/drivers/gpu/drm/i915/intel_csr.c
> +++ b/drivers/gpu/drm/i915/intel_csr.c
> @@ -32,13 +32,6 @@
>   * onwards to drive newly added DMC (Display microcontroller) in display
>   * engine to save and restore the state of display engine when it enter into
>   * low-power state and comes back to normal.
> - *
> - * Firmware loading status will be one of the below states: FW_UNINITIALIZED,
> - * FW_LOADED, FW_FAILED.
> - *
> - * Once the firmware is written into the registers status will be moved from
> - * FW_UNINITIALIZED to FW_LOADED and for any erroneous condition status will
> - * be moved to FW_FAILED.
>   */
>  
>  #define I915_CSR_SKL "i915/skl_dmc_ver4.bin"
> @@ -200,40 +193,6 @@ static char intel_get_substepping(struct drm_device *dev)
>  }
>  
>  /**
> - * intel_csr_load_status_get() - to get firmware loading status.
> - * @dev_priv: i915 device.
> - *
> - * This function helps to get the firmware loading status.
> - *
> - * Return: Firmware loading status.
> - */
> -enum csr_state intel_csr_load_status_get(struct drm_i915_private *dev_priv)
> -{
> -	enum csr_state state;
> -
> -	mutex_lock(&dev_priv->csr.csr_lock);
> -	state = dev_priv->csr.state;
> -	mutex_unlock(&dev_priv->csr.csr_lock);
> -
> -	return state;
> -}
> -
> -/**
> - * intel_csr_load_status_set() - help to set firmware loading status.
> - * @dev_priv: i915 device.
> - * @state: enumeration of firmware loading status.
> - *
> - * Set the firmware loading status.
> - */
> -void intel_csr_load_status_set(struct drm_i915_private *dev_priv,
> -			enum csr_state state)
> -{
> -	mutex_lock(&dev_priv->csr.csr_lock);
> -	dev_priv->csr.state = state;
> -	mutex_unlock(&dev_priv->csr.csr_lock);
> -}
> -
> -/**
>   * intel_csr_load_program() - write the firmware from memory to register.
>   * @dev: drm device.
>   *
> @@ -252,7 +211,6 @@ void intel_csr_load_program(struct drm_device *dev)
>  		return;
>  	}
>  
> -	mutex_lock(&dev_priv->csr.csr_lock);
>  	fw_size = dev_priv->csr.dmc_fw_size;
>  	for (i = 0; i < fw_size; i++)
>  		I915_WRITE(CSR_PROGRAM_BASE + i * 4,
> @@ -262,9 +220,7 @@ void intel_csr_load_program(struct drm_device *dev)
>  		I915_WRITE(dev_priv->csr.mmioaddr[i],
>  			dev_priv->csr.mmiodata[i]);
>  	}
> -
> -	dev_priv->csr.state = FW_LOADED;
> -	mutex_unlock(&dev_priv->csr.csr_lock);
> +	complete(&dev_priv->csr.is_loaded);
>  }
>  
>  static void finish_csr_load(const struct firmware *fw, void *context)
> @@ -280,7 +236,6 @@ static void finish_csr_load(const struct firmware *fw, void *context)
>  	uint32_t dmc_offset = CSR_DEFAULT_FW_OFFSET, readcount = 0, nbytes;
>  	uint32_t i;
>  	__be32 *dmc_payload;
> -	bool fw_loaded = false;
>  
>  	if (!fw) {
>  		i915_firmware_load_error_print(csr->fw_path, 0);
> @@ -387,14 +342,9 @@ static void finish_csr_load(const struct firmware *fw, void *context)
>  
>  	/* load csr program during system boot, as needed for DC states */
>  	intel_csr_load_program(dev);
> -	fw_loaded = true;
>  
>  out:
> -	if (fw_loaded)
> -		intel_runtime_pm_put(dev_priv);
> -	else
> -		intel_csr_load_status_set(dev_priv, FW_FAILED);
> -
> +	intel_runtime_pm_put(dev_priv);
>  	release_firmware(fw);
>  }
>  
> @@ -418,7 +368,6 @@ void intel_csr_ucode_init(struct drm_device *dev)
>  		csr->fw_path = I915_CSR_SKL;
>  	else {
>  		DRM_ERROR("Unexpected: no known CSR firmware for platform\n");
> -		intel_csr_load_status_set(dev_priv, FW_FAILED);
>  		return;
>  	}
>  
> @@ -428,15 +377,15 @@ void intel_csr_ucode_init(struct drm_device *dev)
>  	 */
>  	intel_runtime_pm_get(dev_priv);
>  
> +	init_completion(&csr->is_loaded);
> +
>  	/* CSR supported for platform, load firmware */
>  	ret = request_firmware_nowait(THIS_MODULE, true, csr->fw_path,
>  				&dev_priv->dev->pdev->dev,
>  				GFP_KERNEL, dev_priv,
>  				finish_csr_load);
> -	if (ret) {
> +	if (ret)
>  		i915_firmware_load_error_print(csr->fw_path, ret);
> -		intel_csr_load_status_set(dev_priv, FW_FAILED);
> -	}
>  }
>  
>  /**
> @@ -453,13 +402,11 @@ void intel_csr_ucode_fini(struct drm_device *dev)
>  	if (!HAS_CSR(dev))
>  		return;
>  
> -	intel_csr_load_status_set(dev_priv, FW_FAILED);
>  	kfree(dev_priv->csr.dmc_payload);
>  }
>  
>  void assert_csr_loaded(struct drm_i915_private *dev_priv)
>  {
> -	WARN((intel_csr_load_status_get(dev_priv) != FW_LOADED), "CSR is not loaded.\n");
>  	WARN(!I915_READ(CSR_PROGRAM_BASE),
>  				"CSR program storage start is NULL\n");
>  	WARN(!I915_READ(CSR_SSP_BASE), "CSR SSP Base Not fine\n");
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index ea20edb..e022c98 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -1155,9 +1155,6 @@ u32 skl_plane_ctl_rotation(unsigned int rotation);
>  
>  /* intel_csr.c */
>  void intel_csr_ucode_init(struct drm_device *dev);
> -enum csr_state intel_csr_load_status_get(struct drm_i915_private *dev_priv);
> -void intel_csr_load_status_set(struct drm_i915_private *dev_priv,
> -					enum csr_state state);
>  void intel_csr_load_program(struct drm_device *dev);
>  void intel_csr_ucode_fini(struct drm_device *dev);
>  void assert_csr_loaded(struct drm_i915_private *dev_priv);
> diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c
> index b393db7..5a830d3 100644
> --- a/drivers/gpu/drm/i915/intel_runtime_pm.c
> +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c
> @@ -525,7 +525,6 @@ static void assert_can_disable_dc6(struct drm_i915_private *dev_priv)
>  	if (dev_priv->power_domains.initializing)
>  		return;
>  
> -	assert_csr_loaded(dev_priv);
>  	WARN(!(I915_READ(DC_STATE_EN) & DC_STATE_EN_UPTO_DC6),
>  		"DC6 already programmed to be disabled.\n");
>  }
> @@ -642,21 +641,14 @@ 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) {
> -				enum csr_state state;
> -				/* TODO: wait for a completion event or
> -				 * similar here instead of busy
> -				 * waiting using wait_for function.
> -				 */
> -				wait_for((state = intel_csr_load_status_get(dev_priv)) !=
> -						FW_UNINITIALIZED, 1000);
> -				if (state != FW_LOADED)
> -					DRM_ERROR("CSR firmware not ready (%d)\n",
> -							state);
> -				else
> +				if (wait_for_completion_timeout(
> +					&dev_priv->csr.is_loaded, 100)) {

The problem is with this design that this wait here isn't allowed to ever
time out, since if it does the book-keeping gets out of sync. And for a
bunch of reasons (delays, deadlocks) we can't have that wait here really
at all, much less one that never times out.

Chandra has already hit this on a system where the firmware just isn't
available.

Thanks, Daniel

>  					if (SKL_ENABLE_DC6(dev))
>  						skl_enable_dc6(dev_priv);
>  					else
>  						gen9_enable_dc5(dev_priv);
> +				} else
> +					DRM_ERROR("CSR firmware not ready\n");
>  			}
>  		}
>  	}
> -- 
> 2.0.2
> 
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/intel-gfx

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


More information about the Intel-gfx mailing list