Commit 5c4426a7 authored by Dave Airlie's avatar Dave Airlie

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: default avatarDave Airlie <airlied@redhat.com>
parent 19b4b445
...@@ -23,6 +23,7 @@ config DRM_KMS_HELPER ...@@ -23,6 +23,7 @@ config DRM_KMS_HELPER
depends on DRM depends on DRM
select FB select FB
select FRAMEBUFFER_CONSOLE if !EMBEDDED select FRAMEBUFFER_CONSOLE if !EMBEDDED
select SLOW_WORK
help help
FB and CRTC helpers for KMS drivers. FB and CRTC helpers for KMS drivers.
......
...@@ -1308,9 +1308,14 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper) ...@@ -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. * we shouldn't end up with no modes here.
*/ */
if (count == 0) if (count == 0) {
printk(KERN_INFO "No connectors reported connected with modes\n"); 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); drm_setup_crtcs(fb_helper);
return 0; return 0;
...@@ -1318,15 +1323,80 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper) ...@@ -1318,15 +1323,80 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper)
EXPORT_SYMBOL(drm_fb_helper_initial_config); EXPORT_SYMBOL(drm_fb_helper_initial_config);
bool drm_helper_fb_hotplug_event(struct drm_fb_helper *fb_helper, 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_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); 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); drm_setup_crtcs(fb_helper);
return true; return true;
} }
EXPORT_SYMBOL(drm_helper_fb_hotplug_event); 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) ...@@ -321,18 +321,23 @@ static int radeonfb_probe(struct radeon_fbdev *rfbdev)
return drm_fb_helper_single_fb_probe(&rfbdev->helper, bpp_sel); 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; struct radeon_device *rdev = dev->dev_private;
int max_width, max_height; int max_width, max_height;
max_width = rdev->mode_info.rfbdev->rfb.base.width; max_width = rdev->mode_info.rfbdev->rfb.base.width;
max_height = rdev->mode_info.rfbdev->rfb.base.height; 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); 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) static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfbdev)
{ {
struct fb_info *info; struct fb_info *info;
...@@ -381,8 +386,12 @@ int radeon_fbdev_init(struct radeon_device *rdev) ...@@ -381,8 +386,12 @@ int radeon_fbdev_init(struct radeon_device *rdev)
drm_fb_helper_single_add_all_connectors(&rfbdev->helper); 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); drm_fb_helper_initial_config(&rfbdev->helper);
radeonfb_probe(rfbdev); radeonfb_probe(rfbdev);
return 0; return 0;
} }
...@@ -392,6 +401,7 @@ void radeon_fbdev_fini(struct radeon_device *rdev) ...@@ -392,6 +401,7 @@ void radeon_fbdev_fini(struct radeon_device *rdev)
if (!rdev->mode_info.rfbdev) if (!rdev->mode_info.rfbdev)
return; return;
drm_fb_helper_poll_fini(&rdev->mode_info.rfbdev->helper);
radeon_fbdev_destroy(rdev->ddev, rdev->mode_info.rfbdev); radeon_fbdev_destroy(rdev->ddev, rdev->mode_info.rfbdev);
kfree(rdev->mode_info.rfbdev); kfree(rdev->mode_info.rfbdev);
rdev->mode_info.rfbdev = NULL; rdev->mode_info.rfbdev = NULL;
......
...@@ -55,7 +55,7 @@ static void radeon_hotplug_work_func(struct work_struct *work) ...@@ -55,7 +55,7 @@ static void radeon_hotplug_work_func(struct work_struct *work)
radeon_connector_hotplug(connector); radeon_connector_hotplug(connector);
} }
/* Just fire off a uevent and let userspace tell us what to do */ /* 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); drm_sysfs_hotplug_event(dev);
} }
......
...@@ -585,5 +585,5 @@ void radeon_fbdev_fini(struct radeon_device *rdev); ...@@ -585,5 +585,5 @@ void radeon_fbdev_fini(struct radeon_device *rdev);
void radeon_fbdev_set_suspend(struct radeon_device *rdev, int state); void radeon_fbdev_set_suspend(struct radeon_device *rdev, int state);
int radeon_fbdev_total_size(struct radeon_device *rdev); int radeon_fbdev_total_size(struct radeon_device *rdev);
bool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj); 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 #endif
...@@ -30,6 +30,8 @@ ...@@ -30,6 +30,8 @@
#ifndef DRM_FB_HELPER_H #ifndef DRM_FB_HELPER_H
#define DRM_FB_HELPER_H #define DRM_FB_HELPER_H
#include <linux/slow-work.h>
struct drm_fb_helper_crtc { struct drm_fb_helper_crtc {
uint32_t crtc_id; uint32_t crtc_id;
struct drm_mode_set mode_set; struct drm_mode_set mode_set;
...@@ -86,8 +88,12 @@ struct drm_fb_helper { ...@@ -86,8 +88,12 @@ struct drm_fb_helper {
u32 pseudo_palette[17]; u32 pseudo_palette[17];
struct list_head kernel_fb_list; 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, int (*fb_probe)(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes); 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, 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, ...@@ -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); 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, bool drm_helper_fb_hotplug_event(struct drm_fb_helper *fb_helper,
u32 max_height); u32 max_width, u32 max_height, bool polled);
bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper); 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); 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 #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment