drm/i915: add intel_display_power_enabled_sw() for use in atomic ctx
authorImre Deak <imre.deak@intel.com>
Wed, 27 Nov 2013 20:02:02 +0000 (22:02 +0200)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Thu, 28 Nov 2013 14:05:06 +0000 (15:05 +0100)
Atm we call intel_display_power_enabled() from
i915_capture_error_state() in IRQ context and then take a mutex. To fix
this add a new intel_display_power_enabled_sw() which returns the domain
state based on software tracking as opposed to reading the actual HW
state.

Since we use domain_use_count for this without locking on the reader
side make sure we increase the counter only after enabling all required
power wells and decrease it before disabling any of these power wells.

Regression introduced in
commit 1b02383464b4a915627ef3b8fd0ad7f07168c54c
Author: Imre Deak <imre.deak@intel.com>
Date:   Tue Sep 24 16:17:09 2013 +0300

    drm/i915: support for multiple power wells

Note that atm we depend on the value returned by
intel_display_power_enabled_sw() in i915_capture_error_state() to avoid
unclaimed register access reports. This was never guaranteed though,
since another thread can disable the power concurrently. If this is a
problem we need another explicit way to disable the reporting during
error captures.

v2:
- remove barriers as the caller can't depend on the value
  returned from i915_capture_error_state_sw() anyway (Ville)
- dump the state of pipe/transcoder power domain state (Daniel)

Reported-by: Chris Wilson <chris@chris-wilson.co.uk>
Signed-off-by: Imre Deak <imre.deak@intel.com>
Reviewed-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_pm.c

index 6b18b4714d7ee79f9bf13b0504770b885ebed8ef..64ed8f4d991f92b5bca571b78f2fd43923dba615 100644 (file)
@@ -976,9 +976,7 @@ struct i915_power_domains {
        int power_well_count;
 
        struct mutex lock;
-#if IS_ENABLED(CONFIG_DEBUG_FS)
        int domain_use_count[POWER_DOMAIN_NUM];
-#endif
        struct i915_power_well *power_wells;
 };
 
index ca467cb3c1b4eded966283c3f4a4210a461163e6..5a79088e6da144fef9a05d678369c563d6aae4db 100644 (file)
@@ -11317,6 +11317,7 @@ struct intel_display_error_state {
        } cursor[I915_MAX_PIPES];
 
        struct intel_pipe_error_state {
+               bool power_domain_on;
                u32 source;
        } pipe[I915_MAX_PIPES];
 
@@ -11331,6 +11332,7 @@ struct intel_display_error_state {
        } plane[I915_MAX_PIPES];
 
        struct intel_transcoder_error_state {
+               bool power_domain_on;
                enum transcoder cpu_transcoder;
 
                u32 conf;
@@ -11368,7 +11370,9 @@ intel_display_capture_error_state(struct drm_device *dev)
                error->power_well_driver = I915_READ(HSW_PWR_WELL_DRIVER);
 
        for_each_pipe(i) {
-               if (!intel_display_power_enabled(dev, POWER_DOMAIN_PIPE(i)))
+               error->pipe[i].power_domain_on =
+                       intel_display_power_enabled_sw(dev, POWER_DOMAIN_PIPE(i));
+               if (!error->pipe[i].power_domain_on)
                        continue;
 
                if (INTEL_INFO(dev)->gen <= 6 || IS_VALLEYVIEW(dev)) {
@@ -11404,8 +11408,9 @@ intel_display_capture_error_state(struct drm_device *dev)
        for (i = 0; i < error->num_transcoders; i++) {
                enum transcoder cpu_transcoder = transcoders[i];
 
-               if (!intel_display_power_enabled(dev,
-                               POWER_DOMAIN_TRANSCODER(cpu_transcoder)))
+               error->transcoder[i].power_domain_on =
+                       intel_display_power_enabled_sw(dev, POWER_DOMAIN_PIPE(i));
+               if (!error->transcoder[i].power_domain_on)
                        continue;
 
                error->transcoder[i].cpu_transcoder = cpu_transcoder;
@@ -11440,6 +11445,8 @@ intel_display_print_error_state(struct drm_i915_error_state_buf *m,
                           error->power_well_driver);
        for_each_pipe(i) {
                err_printf(m, "Pipe [%d]:\n", i);
+               err_printf(m, "  Power: %s\n",
+                          error->pipe[i].power_domain_on ? "on" : "off");
                err_printf(m, "  SRC: %08x\n", error->pipe[i].source);
 
                err_printf(m, "Plane [%d]:\n", i);
@@ -11465,6 +11472,8 @@ intel_display_print_error_state(struct drm_i915_error_state_buf *m,
        for (i = 0; i < error->num_transcoders; i++) {
                err_printf(m, "CPU transcoder: %c\n",
                           transcoder_name(error->transcoder[i].cpu_transcoder));
+               err_printf(m, "  Power: %s\n",
+                          error->transcoder[i].power_domain_on ? "on" : "off");
                err_printf(m, "  CONF: %08x\n", error->transcoder[i].conf);
                err_printf(m, "  HTOTAL: %08x\n", error->transcoder[i].htotal);
                err_printf(m, "  HBLANK: %08x\n", error->transcoder[i].hblank);
index 02312810374d7243b88377e1a30cf4a07c067616..5dea389675239e228c1c9ce93d106b734ad94647 100644 (file)
@@ -842,6 +842,8 @@ int intel_power_domains_init(struct drm_device *dev);
 void intel_power_domains_remove(struct drm_device *dev);
 bool intel_display_power_enabled(struct drm_device *dev,
                                 enum intel_display_power_domain domain);
+bool intel_display_power_enabled_sw(struct drm_device *dev,
+                                   enum intel_display_power_domain domain);
 void intel_display_power_get(struct drm_device *dev,
                             enum intel_display_power_domain domain);
 void intel_display_power_put(struct drm_device *dev,
index eac7c4eb345cbf543d4024c9defd06091654efa7..ff47520f8d409f348e4e5d2c640cfccad08029be 100644 (file)
@@ -5641,6 +5641,17 @@ static bool hsw_power_well_enabled(struct drm_device *dev,
                     (HSW_PWR_WELL_ENABLE_REQUEST | HSW_PWR_WELL_STATE_ENABLED);
 }
 
+bool intel_display_power_enabled_sw(struct drm_device *dev,
+                                   enum intel_display_power_domain domain)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct i915_power_domains *power_domains;
+
+       power_domains = &dev_priv->power_domains;
+
+       return power_domains->domain_use_count[domain];
+}
+
 bool intel_display_power_enabled(struct drm_device *dev,
                                 enum intel_display_power_domain domain)
 {
@@ -5761,12 +5772,11 @@ void intel_display_power_get(struct drm_device *dev,
 
        mutex_lock(&power_domains->lock);
 
-#if IS_ENABLED(CONFIG_DEBUG_FS)
-       power_domains->domain_use_count[domain]++;
-#endif
        for_each_power_well(i, power_well, BIT(domain), power_domains)
                __intel_power_well_get(dev, power_well);
 
+       power_domains->domain_use_count[domain]++;
+
        mutex_unlock(&power_domains->lock);
 }
 
@@ -5782,13 +5792,11 @@ void intel_display_power_put(struct drm_device *dev,
 
        mutex_lock(&power_domains->lock);
 
-       for_each_power_well_rev(i, power_well, BIT(domain), power_domains)
-               __intel_power_well_put(dev, power_well);
-
-#if IS_ENABLED(CONFIG_DEBUG_FS)
        WARN_ON(!power_domains->domain_use_count[domain]);
        power_domains->domain_use_count[domain]--;
-#endif
+
+       for_each_power_well_rev(i, power_well, BIT(domain), power_domains)
+               __intel_power_well_put(dev, power_well);
 
        mutex_unlock(&power_domains->lock);
 }