From 5c4426a782bc9509573fc7958a786ebd14fafdf3 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Tue, 30 Mar 2010 05:34:17 +0000 Subject: drm/kms/fb: add polling support for when nothing is connected. When we are running in a headless environment we have no idea what output the user might plug in later, we only have hotplug detect from the digital outputs. So if we detect no connected outputs at initialisation, start a slow work operation to poll every 5 seconds for an output. this is only hooked up for radeon so far, on hw where we have full hotplug detection there is no need for this. Signed-off-by: Dave Airlie --- drivers/gpu/drm/Kconfig | 1 + drivers/gpu/drm/drm_fb_helper.c | 82 ++++++++++++++++++++++++++++++--- drivers/gpu/drm/radeon/radeon_fb.c | 14 +++++- drivers/gpu/drm/radeon/radeon_irq_kms.c | 2 +- drivers/gpu/drm/radeon/radeon_mode.h | 2 +- 5 files changed, 91 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 305c59003963..be5aa7d5206b 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 6374e9b75d45..3312092f2c7e 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -1308,9 +1308,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; @@ -1318,15 +1323,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 7275b2e09444..7913e50fe501 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 e7afd80a3d69..a95907aa7eae 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 165f6025a3bc..4a086c09e117 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -585,5 +585,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 -- cgit v1.2.3