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

Reply via email to