[PATCH v2] drm/fb-helper: Add drm_fb_helper_set_suspend_unlocked()

Noralf Trønnes noralf at tronnes.org
Tue Aug 23 11:54:06 UTC 2016


This adds a function that also takes the console lock before calling
fb_set_suspend() in contrast to drm_fb_helper_set_suspend() which is
a plain wrapper around fb_set_suspend().
Resume is run asynchronously using a worker if the console lock is
already taken. This is modelled after the i915 driver.

Signed-off-by: Noralf Trønnes <noralf at tronnes.org>
---

Daniel,

I haven't got time to convert drivers to use this and now that simpledrm
won't be using it we can wait with this if you prefer, and I'll bundle it
with tinydrm when the time comes.

Noralf.


Changes from version 1:
- Change function name to drm_fb_helper_set_suspend_unlocked()
- Use suspend as argument name to increase readability

 drivers/gpu/drm/drm_fb_helper.c | 62 ++++++++++++++++++++++++++++++++++++++++-
 include/drm/drm_fb_helper.h     |  9 ++++++
 2 files changed, 70 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index ce54e98..2886df5 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -29,6 +29,7 @@
  */
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

+#include <linux/console.h>
 #include <linux/kernel.h>
 #include <linux/sysrq.h>
 #include <linux/slab.h>
@@ -618,6 +619,16 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
 	kfree(helper->crtc_info);
 }

+static void drm_fb_helper_resume_worker(struct work_struct *work)
+{
+	struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper,
+						    resume_work);
+
+	console_lock();
+	fb_set_suspend(helper->fbdev, 0);
+	console_unlock();
+}
+
 static void drm_fb_helper_dirty_work(struct work_struct *work)
 {
 	struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper,
@@ -649,6 +660,7 @@ void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
 {
 	INIT_LIST_HEAD(&helper->kernel_fb_list);
 	spin_lock_init(&helper->dirty_lock);
+	INIT_WORK(&helper->resume_work, drm_fb_helper_resume_worker);
 	INIT_WORK(&helper->dirty_work, drm_fb_helper_dirty_work);
 	helper->dirty_clip.x1 = helper->dirty_clip.y1 = ~0;
 	helper->funcs = funcs;
@@ -1026,7 +1038,9 @@ EXPORT_SYMBOL(drm_fb_helper_cfb_imageblit);
  * @fb_helper: driver-allocated fbdev helper
  * @state: desired state, zero to resume, non-zero to suspend
  *
- * A wrapper around fb_set_suspend implemented by fbdev core
+ * A wrapper around fb_set_suspend implemented by fbdev core.
+ * Use drm_fb_helper_set_suspend_unlocked() if you don't need to take
+ * the lock yourself
  */
 void drm_fb_helper_set_suspend(struct drm_fb_helper *fb_helper, int state)
 {
@@ -1035,6 +1049,52 @@ void drm_fb_helper_set_suspend(struct drm_fb_helper *fb_helper, int state)
 }
 EXPORT_SYMBOL(drm_fb_helper_set_suspend);

+/**
+ * drm_fb_helper_set_suspend_unlocked - wrapper around fb_set_suspend that also
+ *                                      takes the console lock
+ * @fb_helper: driver-allocated fbdev helper
+ * @state: desired state, zero to resume, non-zero to suspend
+ *
+ * A wrapper around fb_set_suspend() that takes the console lock. If the lock
+ * isn't available on resume, a worker is tasked with waiting for the lock
+ * to become available. The console lock can be pretty contented on resume
+ * due to all the printk activity.
+ *
+ * This function can be called multiple times with the same state since
+ * &fb_info->state is checked to see if fbdev is running or not before locking.
+ *
+ * Use drm_fb_helper_set_suspend() if you need to take the lock yourself.
+ */
+void drm_fb_helper_set_suspend_unlocked(struct drm_fb_helper *fb_helper,
+					int suspend)
+{
+	if (!fb_helper || !fb_helper->fbdev)
+		return;
+
+	/* make sure there's no pending/ongoing resume */
+	flush_work(&fb_helper->resume_work);
+
+	if (suspend) {
+		if (fb_helper->fbdev->state != FBINFO_STATE_RUNNING)
+			return;
+
+		console_lock();
+
+	} else {
+		if (fb_helper->fbdev->state == FBINFO_STATE_RUNNING)
+			return;
+
+		if (!console_trylock()) {
+			schedule_work(&fb_helper->resume_work);
+			return;
+		}
+	}
+
+	fb_set_suspend(fb_helper->fbdev, suspend);
+	console_unlock();
+}
+EXPORT_SYMBOL(drm_fb_helper_set_suspend_unlocked);
+
 static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
 		     u16 blue, u16 regno, struct fb_info *info)
 {
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
index db8d478..5bde508 100644
--- a/include/drm/drm_fb_helper.h
+++ b/include/drm/drm_fb_helper.h
@@ -176,6 +176,7 @@ struct drm_fb_helper_connector {
  *              the screen buffer
  * @dirty_lock: spinlock protecting @dirty_clip
  * @dirty_work: worker used to flush the framebuffer
+ * @resume_work: worker used during resume if the console lock is already taken
  *
  * This is the main structure used by the fbdev helpers. Drivers supporting
  * fbdev emulation should embedded this into their overall driver structure.
@@ -196,6 +197,7 @@ struct drm_fb_helper {
 	struct drm_clip_rect dirty_clip;
 	spinlock_t dirty_lock;
 	struct work_struct dirty_work;
+	struct work_struct resume_work;

 	/**
 	 * @kernel_fb_list:
@@ -264,6 +266,8 @@ void drm_fb_helper_cfb_imageblit(struct fb_info *info,
 				 const struct fb_image *image);

 void drm_fb_helper_set_suspend(struct drm_fb_helper *fb_helper, int state);
+void drm_fb_helper_set_suspend_unlocked(struct drm_fb_helper *fb_helper,
+					int suspend);

 int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info);

@@ -421,6 +425,11 @@ static inline void drm_fb_helper_set_suspend(struct drm_fb_helper *fb_helper,
 {
 }

+static inline void
+drm_fb_helper_set_suspend_unlocked(struct drm_fb_helper *fb_helper, int suspend)
+{
+}
+
 static inline int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
 {
 	return 0;
--
2.8.2



More information about the dri-devel mailing list