diff mbox

[5/6] drm/kms/fb: add polling support for when nothing is connected.

Message ID 1269927258-13498-6-git-send-email-airlied@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Dave Airlie March 30, 2010, 5:34 a.m. UTC
None
diff mbox

Patch

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 305c590..be5aa7d 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -23,6 +23,7 @@  config DRM_KMS_HELPER
 	depends on DRM
 	select FB
 	select FRAMEBUFFER_CONSOLE if !EMBEDDED
+	select SLOW_WORK
 	help
 	  FB and CRTC helpers for KMS drivers.
 
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 8192a56..f879b3a 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -1306,9 +1306,14 @@  bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper)
 	/*
 	 * we shouldn't end up with no modes here.
 	 */
-	if (count == 0)
-		printk(KERN_INFO "No connectors reported connected with modes\n");
-
+	if (count == 0) {
+		if (fb_helper->poll_enabled) {
+			delayed_slow_work_enqueue(&fb_helper->output_poll_slow_work,
+						  5*HZ);
+			printk(KERN_INFO "No connectors reported connected with modes - started polling\n");
+		} else
+			printk(KERN_INFO "No connectors reported connected with modes\n");
+	}
 	drm_setup_crtcs(fb_helper);
 
 	return 0;
@@ -1316,15 +1321,80 @@  bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper)
 EXPORT_SYMBOL(drm_fb_helper_initial_config);
 
 bool drm_helper_fb_hotplug_event(struct drm_fb_helper *fb_helper,
-				 u32 max_width, u32 max_height)
+				 u32 max_width, u32 max_height, bool polled)
 {
+	int count = 0;
+	int ret;
 	DRM_DEBUG_KMS("\n");
 
-	drm_fb_helper_probe_connector_modes(fb_helper, max_width,
+	count = drm_fb_helper_probe_connector_modes(fb_helper, max_width,
 						    max_height);
-
+	if (fb_helper->poll_enabled && !polled) {
+		if (count) {
+			delayed_slow_work_cancel(&fb_helper->output_poll_slow_work);
+		} else {
+			ret = delayed_slow_work_enqueue(&fb_helper->output_poll_slow_work, 5*HZ);
+		}
+	}
 	drm_setup_crtcs(fb_helper);
 
 	return true;
 }
 EXPORT_SYMBOL(drm_helper_fb_hotplug_event);
+
+static void output_poll_execute(struct slow_work *work)
+{
+	struct delayed_slow_work *delayed_work = container_of(work, struct delayed_slow_work, work);
+	struct drm_fb_helper *fb_helper = container_of(delayed_work, struct drm_fb_helper, output_poll_slow_work);
+	struct drm_device *dev = fb_helper->dev;
+	struct drm_connector *connector;
+	enum drm_connector_status old_status, status;
+	bool repoll = true, changed = false;
+	int ret;
+
+	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+		old_status = connector->status;
+		status = connector->funcs->detect(connector);
+		if (old_status != status) {
+			changed = true;
+			/* something changed */
+		}
+		if (status == connector_status_connected) {
+			DRM_DEBUG("%s is connected - stop polling\n", drm_get_connector_name(connector));
+			repoll = false;
+		}
+	}
+
+	if (repoll) {
+		ret = delayed_slow_work_enqueue(delayed_work, 5*HZ);
+		if (ret)
+			DRM_ERROR("delayed enqueue failed %d\n", ret);
+	}
+
+	if (changed) {
+		if (fb_helper->fb_poll_changed)
+			fb_helper->fb_poll_changed(fb_helper);
+	}
+}
+
+struct slow_work_ops output_poll_ops = {
+	.execute = output_poll_execute,
+};
+
+void drm_fb_helper_poll_init(struct drm_fb_helper *fb_helper)
+{
+	int ret;
+
+	ret = slow_work_register_user(THIS_MODULE);
+
+	delayed_slow_work_init(&fb_helper->output_poll_slow_work, &output_poll_ops);
+	fb_helper->poll_enabled = true;
+}
+EXPORT_SYMBOL(drm_fb_helper_poll_init);
+
+void drm_fb_helper_poll_fini(struct drm_fb_helper *fb_helper)
+{
+	delayed_slow_work_cancel(&fb_helper->output_poll_slow_work);
+	slow_work_unregister_user(THIS_MODULE);
+}
+EXPORT_SYMBOL(drm_fb_helper_poll_fini);
diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c
index 01a9d3b..867ecd5 100644
--- a/drivers/gpu/drm/radeon/radeon_fb.c
+++ b/drivers/gpu/drm/radeon/radeon_fb.c
@@ -321,18 +321,23 @@  static int radeonfb_probe(struct radeon_fbdev *rfbdev)
 	return drm_fb_helper_single_fb_probe(&rfbdev->helper, bpp_sel);
 }
 
-void radeonfb_hotplug(struct drm_device *dev)
+void radeonfb_hotplug(struct drm_device *dev, bool polled)
 {
 	struct radeon_device *rdev = dev->dev_private;
 	int max_width, max_height;
 
 	max_width = rdev->mode_info.rfbdev->rfb.base.width;
 	max_height = rdev->mode_info.rfbdev->rfb.base.height;
-	drm_helper_fb_hotplug_event(&rdev->mode_info.rfbdev->helper, max_width, max_height);
+	drm_helper_fb_hotplug_event(&rdev->mode_info.rfbdev->helper, max_width, max_height, polled);
 
 	radeonfb_probe(rdev->mode_info.rfbdev);
 }
 
+static void radeon_fb_poll_changed(struct drm_fb_helper *fb_helper)
+{
+	radeonfb_hotplug(fb_helper->dev, true);
+}
+
 static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfbdev)
 {
 	struct fb_info *info;
@@ -381,8 +386,12 @@  int radeon_fbdev_init(struct radeon_device *rdev)
 
 	drm_fb_helper_single_add_all_connectors(&rfbdev->helper);
 
+	rfbdev->helper.fb_poll_changed = radeon_fb_poll_changed;
+	drm_fb_helper_poll_init(&rfbdev->helper);
+
 	drm_fb_helper_initial_config(&rfbdev->helper);
 	radeonfb_probe(rfbdev);
+
 	return 0;
 
 }
@@ -392,6 +401,7 @@  void radeon_fbdev_fini(struct radeon_device *rdev)
 	if (!rdev->mode_info.rfbdev)
 		return;
 
+	drm_fb_helper_poll_fini(&rdev->mode_info.rfbdev->helper);
 	radeon_fbdev_destroy(rdev->ddev, rdev->mode_info.rfbdev);
 	kfree(rdev->mode_info.rfbdev);
 	rdev->mode_info.rfbdev = NULL;
diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c
index 19ed835..2580807 100644
--- a/drivers/gpu/drm/radeon/radeon_irq_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c
@@ -55,7 +55,7 @@  static void radeon_hotplug_work_func(struct work_struct *work)
 			radeon_connector_hotplug(connector);
 	}
 	/* Just fire off a uevent and let userspace tell us what to do */
-	radeonfb_hotplug(dev);
+	radeonfb_hotplug(dev, false);
 
 	drm_sysfs_hotplug_event(dev);
 }
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
index 847ddbb..f40f9bf 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -591,5 +591,5 @@  void radeon_fbdev_fini(struct radeon_device *rdev);
 void radeon_fbdev_set_suspend(struct radeon_device *rdev, int state);
 int radeon_fbdev_total_size(struct radeon_device *rdev);
 bool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj);
-void radeonfb_hotplug(struct drm_device *dev);
+void radeonfb_hotplug(struct drm_device *dev, bool polled);
 #endif
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
index 50094f9..a073d73 100644
--- a/include/drm/drm_fb_helper.h
+++ b/include/drm/drm_fb_helper.h
@@ -30,6 +30,8 @@ 
 #ifndef DRM_FB_HELPER_H
 #define DRM_FB_HELPER_H
 
+#include <linux/slow-work.h>
+
 struct drm_fb_helper_crtc {
 	uint32_t crtc_id;
 	struct drm_mode_set mode_set;
@@ -86,8 +88,12 @@  struct drm_fb_helper {
 	u32 pseudo_palette[17];
 	struct list_head kernel_fb_list;
 
+	struct delayed_slow_work output_poll_slow_work;
+	bool poll_enabled;
 	int (*fb_probe)(struct drm_fb_helper *helper,
 			struct drm_fb_helper_surface_size *sizes);
+
+	void (*fb_poll_changed)(struct drm_fb_helper *helper);
 };
 
 int drm_fb_helper_single_fb_probe(struct drm_fb_helper *helper,
@@ -118,9 +124,11 @@  void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
 
 int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info);
 
-bool drm_helper_fb_hotplug_event(struct drm_fb_helper *fb_helper, u32 max_width,
-				 u32 max_height);
+bool drm_helper_fb_hotplug_event(struct drm_fb_helper *fb_helper,
+				 u32 max_width, u32 max_height, bool polled);
 bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper);
 int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper);
 
+void drm_fb_helper_poll_init(struct drm_fb_helper *fb_helper);
+void drm_fb_helper_poll_fini(struct drm_fb_helper *fb_helper);
 #endif