drm/i915: Add way to specify the power-off delay of a display power domain
authorImre Deak <imre.deak@intel.com>
Fri, 16 Jun 2023 18:51:03 +0000 (21:51 +0300)
committerImre Deak <imre.deak@intel.com>
Mon, 26 Jun 2023 11:25:28 +0000 (14:25 +0300)
Add support for specifying a delay different than the current 100 ms
default for powering off a display power domain. This is needed by the
next patch which delays re-enabling DC states during modesets to avoid
the off->on->off toggling overhead of the DC_off power well, but does
this using a < 100 ms delay for a better utilization of DC power saving
states.

Reviewed-by: Jouni Högander <jouni.hogander@intel.com>
Signed-off-by: Imre Deak <imre.deak@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20230616185104.2502003-3-imre.deak@intel.com
drivers/gpu/drm/i915/display/intel_display_power.c
drivers/gpu/drm/i915/display/intel_display_power.h

index d3310c7..38225e5 100644 (file)
@@ -458,6 +458,17 @@ async_put_domains_clear_domain(struct i915_power_domains *power_domains,
        clear_bit(domain, power_domains->async_put_domains[1].bits);
 }
 
+static void
+cancel_async_put_work(struct i915_power_domains *power_domains, bool sync)
+{
+       if (sync)
+               cancel_delayed_work_sync(&power_domains->async_put_work);
+       else
+               cancel_delayed_work(&power_domains->async_put_work);
+
+       power_domains->async_put_next_delay = 0;
+}
+
 static bool
 intel_display_power_grab_async_put_ref(struct drm_i915_private *dev_priv,
                                       enum intel_display_power_domain domain)
@@ -478,7 +489,7 @@ intel_display_power_grab_async_put_ref(struct drm_i915_private *dev_priv,
        if (!bitmap_empty(async_put_mask.bits, POWER_DOMAIN_NUM))
                goto out_verify;
 
-       cancel_delayed_work(&power_domains->async_put_work);
+       cancel_async_put_work(power_domains, false);
        intel_runtime_pm_put_raw(&dev_priv->runtime_pm,
                                 fetch_and_zero(&power_domains->async_put_wakeref));
 out_verify:
@@ -609,7 +620,8 @@ static void __intel_display_power_put(struct drm_i915_private *dev_priv,
 
 static void
 queue_async_put_domains_work(struct i915_power_domains *power_domains,
-                            intel_wakeref_t wakeref)
+                            intel_wakeref_t wakeref,
+                            int delay_ms)
 {
        struct drm_i915_private *i915 = container_of(power_domains,
                                                     struct drm_i915_private,
@@ -618,7 +630,7 @@ queue_async_put_domains_work(struct i915_power_domains *power_domains,
        power_domains->async_put_wakeref = wakeref;
        drm_WARN_ON(&i915->drm, !queue_delayed_work(system_unbound_wq,
                                                    &power_domains->async_put_work,
-                                                   msecs_to_jiffies(100)));
+                                                   msecs_to_jiffies(delay_ms)));
 }
 
 static void
@@ -681,13 +693,15 @@ intel_display_power_put_async_work(struct work_struct *work)
                bitmap_zero(power_domains->async_put_domains[1].bits,
                            POWER_DOMAIN_NUM);
                queue_async_put_domains_work(power_domains,
-                                            fetch_and_zero(&new_work_wakeref));
+                                            fetch_and_zero(&new_work_wakeref),
+                                            power_domains->async_put_next_delay);
+               power_domains->async_put_next_delay = 0;
        } else {
                /*
                 * Cancel the work that got queued after this one got dequeued,
                 * since here we released the corresponding async-put reference.
                 */
-               cancel_delayed_work(&power_domains->async_put_work);
+               cancel_async_put_work(power_domains, false);
        }
 
 out_verify:
@@ -706,19 +720,25 @@ out_verify:
  * @i915: i915 device instance
  * @domain: power domain to reference
  * @wakeref: wakeref acquired for the reference that is being released
+ * @delay_ms: delay of powering down the power domain
  *
  * This function drops the power domain reference obtained by
  * intel_display_power_get*() and schedules a work to power down the
  * corresponding hardware block if this is the last reference.
+ * The power down is delayed by @delay_ms if this is >= 0, or by a default
+ * 100 ms otherwise.
  */
 void __intel_display_power_put_async(struct drm_i915_private *i915,
                                     enum intel_display_power_domain domain,
-                                    intel_wakeref_t wakeref)
+                                    intel_wakeref_t wakeref,
+                                    int delay_ms)
 {
        struct i915_power_domains *power_domains = &i915->display.power.domains;
        struct intel_runtime_pm *rpm = &i915->runtime_pm;
        intel_wakeref_t work_wakeref = intel_runtime_pm_get_raw(rpm);
 
+       delay_ms = delay_ms >= 0 ? delay_ms : 100;
+
        mutex_lock(&power_domains->lock);
 
        if (power_domains->domain_use_count[domain] > 1) {
@@ -732,10 +752,13 @@ void __intel_display_power_put_async(struct drm_i915_private *i915,
        /* Let a pending work requeue itself or queue a new one. */
        if (power_domains->async_put_wakeref) {
                set_bit(domain, power_domains->async_put_domains[1].bits);
+               power_domains->async_put_next_delay = max(power_domains->async_put_next_delay,
+                                                         delay_ms);
        } else {
                set_bit(domain, power_domains->async_put_domains[0].bits);
                queue_async_put_domains_work(power_domains,
-                                            fetch_and_zero(&work_wakeref));
+                                            fetch_and_zero(&work_wakeref),
+                                            delay_ms);
        }
 
 out_verify:
@@ -775,7 +798,7 @@ void intel_display_power_flush_work(struct drm_i915_private *i915)
 
        async_put_domains_mask(power_domains, &async_put_mask);
        release_async_put_domains(power_domains, &async_put_mask);
-       cancel_delayed_work(&power_domains->async_put_work);
+       cancel_async_put_work(power_domains, false);
 
 out_verify:
        verify_async_put_domains_state(power_domains);
@@ -799,7 +822,7 @@ intel_display_power_flush_work_sync(struct drm_i915_private *i915)
        struct i915_power_domains *power_domains = &i915->display.power.domains;
 
        intel_display_power_flush_work(i915);
-       cancel_delayed_work_sync(&power_domains->async_put_work);
+       cancel_async_put_work(power_domains, true);
 
        verify_async_put_domains_state(power_domains);
 
index 83ceab0..d3b5d04 100644 (file)
@@ -151,6 +151,7 @@ struct i915_power_domains {
        struct delayed_work async_put_work;
        intel_wakeref_t async_put_wakeref;
        struct intel_power_domain_mask async_put_domains[2];
+       int async_put_next_delay;
 
        struct i915_power_well *power_wells;
 };
@@ -197,7 +198,8 @@ intel_display_power_get_if_enabled(struct drm_i915_private *dev_priv,
                                   enum intel_display_power_domain domain);
 void __intel_display_power_put_async(struct drm_i915_private *i915,
                                     enum intel_display_power_domain domain,
-                                    intel_wakeref_t wakeref);
+                                    intel_wakeref_t wakeref,
+                                    int delay_ms);
 void intel_display_power_flush_work(struct drm_i915_private *i915);
 #if IS_ENABLED(CONFIG_DRM_I915_DEBUG_RUNTIME_PM)
 void intel_display_power_put(struct drm_i915_private *dev_priv,
@@ -208,7 +210,16 @@ intel_display_power_put_async(struct drm_i915_private *i915,
                              enum intel_display_power_domain domain,
                              intel_wakeref_t wakeref)
 {
-       __intel_display_power_put_async(i915, domain, wakeref);
+       __intel_display_power_put_async(i915, domain, wakeref, -1);
+}
+
+static inline void
+intel_display_power_put_async_delay(struct drm_i915_private *i915,
+                                   enum intel_display_power_domain domain,
+                                   intel_wakeref_t wakeref,
+                                   int delay_ms)
+{
+       __intel_display_power_put_async(i915, domain, wakeref, delay_ms);
 }
 #else
 void intel_display_power_put_unchecked(struct drm_i915_private *dev_priv,
@@ -227,7 +238,16 @@ intel_display_power_put_async(struct drm_i915_private *i915,
                              enum intel_display_power_domain domain,
                              intel_wakeref_t wakeref)
 {
-       __intel_display_power_put_async(i915, domain, -1);
+       __intel_display_power_put_async(i915, domain, -1, -1);
+}
+
+static inline void
+intel_display_power_put_async_delay(struct drm_i915_private *i915,
+                                   enum intel_display_power_domain domain,
+                                   intel_wakeref_t wakeref,
+                                   int delay_ms)
+{
+       __intel_display_power_put_async(i915, domain, -1, delay_ms);
 }
 #endif