[PATCH 2/4] drm/i915: Introduce a cancellable wrapper for delayed resume tasks
Chris Wilson
chris at chris-wilson.co.uk
Mon May 16 11:26:41 UTC 2016
Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
---
drivers/gpu/drm/i915/i915_dma.c | 7 ++++
drivers/gpu/drm/i915/i915_drv.c | 89 +++++++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/i915/i915_drv.h | 16 ++++++++
3 files changed, 112 insertions(+)
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 65a593ac8148..00a0eb40e79b 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -1094,6 +1094,12 @@ static int i915_driver_init_early(struct drm_i915_private *dev_priv,
if (ret < 0)
return ret;
+ ret = intel_async_resume_init(&dev_priv->async_resume);
+ if (ret < 0) {
+ i915_workqueues_cleanup(dev_priv);
+ return ret;
+ }
+
/* This must be called before any calls to HAS_PCH_* */
intel_detect_pch(dev);
@@ -1128,6 +1134,7 @@ static int i915_driver_init_early(struct drm_i915_private *dev_priv,
static void i915_driver_cleanup_early(struct drm_i915_private *dev_priv)
{
i915_gem_load_cleanup(dev_priv->dev);
+ intel_async_resume_cleanup(&dev_priv->async_resume);
i915_workqueues_cleanup(dev_priv);
}
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index dba03c026151..a4253e0426ba 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -580,6 +580,93 @@ static bool suspend_to_idle(struct drm_i915_private *dev_priv)
return false;
}
+struct intel_async_resume_cb {
+ struct delayed_work work;
+ struct list_head link;
+
+ struct drm_i915_private *i915;
+ void (*func)(struct drm_i915_private *);
+};
+
+int intel_async_resume_init(struct intel_async_resume *ar)
+{
+ ar->wq = alloc_workqueue("i915-resume", WQ_UNBOUND, 0);
+ if (ar->wq == NULL)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&ar->list);
+ spin_lock_init(&ar->lock);
+ return 0;
+}
+
+static void intel_async_resume_cb(struct work_struct *dwork)
+{
+ struct intel_async_resume_cb *cb =
+ container_of(dwork, typeof(*cb), work.work);
+ bool cancelled;
+
+ spin_lock(&cb->i915->async_resume.lock);
+ cancelled = list_empty(&cb->link);
+ if (!cancelled)
+ list_del_init(&cb->link);
+ spin_unlock(&cb->i915->async_resume.lock);
+ if (cancelled)
+ return;
+
+ cb->func(cb->i915);
+ kfree(cb);
+}
+
+void intel_async_resume(struct intel_async_resume *ar,
+ void (*func)(struct drm_i915_private *),
+ int delay_ms)
+{
+ struct drm_i915_private *dev_priv =
+ container_of(ar, struct drm_i915_private, async_resume);
+ struct intel_async_resume_cb *cb;
+
+ cb = kmalloc(sizeof(*cb), GFP_ATOMIC);
+ if (!cb)
+ return func(dev_priv);
+
+ cb->i915 = dev_priv;
+ cb->func = func;
+
+ INIT_DELAYED_WORK(&cb->work, intel_async_resume_cb);
+
+ spin_lock(&ar->lock);
+ list_add(&cb->link, &ar->list);
+ spin_unlock(&ar->lock);
+
+ queue_delayed_work(ar->wq, &cb->work, msecs_to_jiffies(delay_ms));
+}
+
+static void intel_cancel_async_resume(struct drm_i915_private *dev_priv)
+{
+ struct intel_async_resume *ar = &dev_priv->async_resume;
+
+ spin_lock(&ar->lock);
+ while (!list_empty(&ar->list)) {
+ struct intel_async_resume_cb *cb =
+ list_first_entry(&ar->list, typeof(*cb), link);
+
+ list_del_init(&cb->link);
+ spin_unlock(&ar->lock);
+
+ if (cancel_delayed_work_sync(&cb->work))
+ kfree(cb);
+
+ spin_lock(&ar->lock);
+ }
+ spin_unlock(&ar->lock);
+}
+
+void intel_async_resume_cleanup(struct intel_async_resume *ar)
+{
+ WARN_ON(!list_empty(&ar->list));
+ destroy_workqueue(ar->wq);
+}
+
static int i915_drm_suspend(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -593,6 +680,8 @@ static int i915_drm_suspend(struct drm_device *dev)
disable_rpm_wakeref_asserts(dev_priv);
+ intel_cancel_async_resume(dev_priv);
+
/* We do a lot of poking in a lot of registers, make sure they work
* properly. */
intel_display_set_init_power(dev_priv, true);
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 72f0b02a8372..e16f1f99d695 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -1924,6 +1924,12 @@ struct drm_i915_private {
struct work_struct fbdev_suspend_work;
#endif
+ struct intel_async_resume {
+ struct spinlock lock;
+ struct list_head list;
+ struct workqueue_struct *wq;
+ } async_resume;
+
struct drm_property *broadcast_rgb_property;
struct drm_property *force_audio_property;
@@ -3499,6 +3505,16 @@ int i915_parse_cmds(struct intel_engine_cs *engine,
u32 batch_len,
bool is_master);
+int intel_async_resume_init(struct intel_async_resume *ar);
+void intel_async_resume(struct intel_async_resume *ar,
+ void (*func)(struct drm_i915_private *),
+ int delay_ms);
+static inline void intel_async_resume_flush(struct intel_async_resume *ar)
+{
+ flush_workqueue(ar->wq);
+}
+void intel_async_resume_cleanup(struct intel_async_resume *ar);
+
/* i915_suspend.c */
extern int i915_save_state(struct drm_device *dev);
extern int i915_restore_state(struct drm_device *dev);
--
2.8.1
More information about the Intel-gfx-trybot
mailing list