From: Dave Airlie <airl...@redhat.com> The problem with using a real connector with a fake status is we have no way to tell userspace it got disconnected if something gets plugged into it, i.e. you use DVI-0 as the connector with an unknown or connected status, and it puts a 1024x768 mode on it. However when a monitor appears on that connector when we send the uevent and userspace repolls the modes it won't reset the mode on that output because the status hasn't changed sufficenetly. Unknown->connected status changes don't cause gnome to set the mode again.
Idea from Ben Skeggs to just create a fake disconnected connector, this seems to work well, xrandr gets a bit more info that needed, but gnome seems to work fine. Signed-off-by: Dave Airlie <airl...@redhat.com> --- drivers/gpu/drm/drm_crtc.c | 40 +++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_crtc_helper.c | 25 ++++++++++++++++++++-- drivers/gpu/drm/drm_fb_helper.c | 2 + include/drm/drm_crtc.h | 4 +++ 4 files changed, 68 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 994d23b..3b880ca 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -499,6 +499,7 @@ void drm_connector_cleanup(struct drm_connector *connector) drm_mode_object_put(dev, &connector->base); list_del(&connector->head); mutex_unlock(&dev->mode_config.mutex); + connector->dev = NULL; } EXPORT_SYMBOL(drm_connector_cleanup); @@ -1560,6 +1561,11 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, goto out; } + if (out_id == dev->mode_config.disconnected_connector.base.id) { + ret = 0; + goto out; + } + obj = drm_mode_object_find(dev, out_id, DRM_MODE_OBJECT_CONNECTOR); if (!obj) { @@ -2655,3 +2661,37 @@ out: mutex_unlock(&dev->mode_config.mutex); return ret; } + +static void drm_connector_disconnected_dpms(struct drm_connector *connector, int mode) +{ + return; +} + +static enum drm_connector_status drm_connector_disconnected_detect(struct drm_connector *connector) +{ + return connector_status_disconnected; +} + +static int drm_connector_disconnected_fill_modes(struct drm_connector *connector, u32 max_width, u32 max_height) +{ + return 0; +} + +static struct drm_connector_funcs drm_disconnected_funcs = { + .dpms = drm_connector_disconnected_dpms, + .detect = drm_connector_disconnected_detect, + .fill_modes = drm_connector_disconnected_fill_modes, + .destroy = drm_connector_cleanup, +}; + +int drm_mode_add_disconnected_connector(struct drm_device *dev) +{ + if (dev->mode_config.disconnected_connector.dev == NULL) { + drm_connector_init(dev, &dev->mode_config.disconnected_connector, + &drm_disconnected_funcs, + DRM_MODE_CONNECTOR_Unknown); + dev->mode_config.disconnected_connector.status = connector_status_disconnected; + } + return 0; +} +EXPORT_SYMBOL(drm_mode_add_disconnected_connector); diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index ebb7a0c..665febb 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -819,11 +819,20 @@ static void output_poll_execute(struct slow_work *work) enum drm_connector_status old_status, status; bool repoll = false, changed = false; int ret; + bool connected = false; list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - /* if this is HPD or polled don't check it - TV out for instance */ - if (!connector->polled) + + /* skip the special disconnected connector */ + if (&dev->mode_config.disconnected_connector == connector) + continue; + /* if this is HPD or polled don't check it - + TV out for instance */ + if (!connector->polled) { + if (connector->status == connector_status_connected) + connected = true; continue; + } else if (connector->polled & (DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT)) repoll = true; @@ -832,8 +841,10 @@ static void output_poll_execute(struct slow_work *work) skip it */ if (old_status == connector_status_connected && !(connector->polled & DRM_CONNECTOR_POLL_DISCONNECT) && - !(connector->polled & DRM_CONNECTOR_POLL_HPD)) + !(connector->polled & DRM_CONNECTOR_POLL_HPD)) { + connected = true; continue; + } status = connector->funcs->detect(connector); if (old_status != status) @@ -843,6 +854,14 @@ static void output_poll_execute(struct slow_work *work) connected = true; } + /* if we have the disconnected connector */ + if (dev->mode_config.disconnected_connector.dev != NULL) { + /* if nothing connected at all we need to force disconnected connector to connected */ + if (!connected) + dev->mode_config.disconnected_connector.status = connector_status_connected; + else + dev->mode_config.disconnected_connector.status = connector_status_disconnected; + } if (changed) { /* send a uevent + call fbdev */ drm_sysfs_hotplug_event(dev); diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index d198b82..f7eb3f0 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -1283,6 +1283,8 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel) */ if (count == 0) { printk(KERN_INFO "No connectors reported connected with modes\n"); + /* create fake connector to hang disconnected X operation off */ + drm_mode_add_disconnected_connector(dev); } drm_setup_crtcs(fb_helper); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 2e4bf92..5dd3aad 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -625,6 +625,8 @@ struct drm_mode_config { struct drm_property *scaling_mode_property; struct drm_property *dithering_mode_property; struct drm_property *dirty_info_property; + + struct drm_connector disconnected_connector; }; #define obj_to_crtc(x) container_of(x, struct drm_crtc, base) @@ -804,4 +806,6 @@ extern int drm_add_modes_noedid(struct drm_connector *connector, int hdisplay, int vdisplay); extern bool drm_edid_is_valid(struct edid *edid); + +extern int drm_mode_add_disconnected_connector(struct drm_device *dev); #endif /* __DRM_CRTC_H__ */ -- 1.7.0.1 ------------------------------------------------------------------------------ -- _______________________________________________ Dri-devel mailing list Dri-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/dri-devel