@@ -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.
@@ -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);
@@ -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;
@@ -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);
}
@@ -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
@@ -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