Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/udl: Fixed problem with UDL adpater reconnection

Fixed problem with DisplayLink and DisplayLink certified adapers in drm/udl
driver when adapter doesn't want to work if it was initialized with
disconnected DVI cable by enabling drm connectot polling and updating
current connector's state.

Signed-off-by: Robert Tarasov <tutankhamen@chromium.org>
Reviewed-by: Alex Deucher <alexander.deucher@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20171013001350.172155-1-tutankhamen@chromium.org

authored by

Robert Tarasov and committed by
Alex Deucher
afdfc4c6 a52ff2a5

+71 -25
+49 -25
drivers/gpu/drm/udl/udl_connector.c
··· 14 14 #include <drm/drm_crtc.h> 15 15 #include <drm/drm_edid.h> 16 16 #include <drm/drm_crtc_helper.h> 17 + #include "udl_connector.h" 17 18 #include "udl_drv.h" 18 19 19 20 /* dummy connector to just get EDID, ··· 57 56 58 57 static int udl_get_modes(struct drm_connector *connector) 59 58 { 60 - struct udl_device *udl = connector->dev->dev_private; 61 - struct edid *edid; 62 - int ret; 59 + struct udl_drm_connector *udl_connector = 60 + container_of(connector, 61 + struct udl_drm_connector, 62 + connector); 63 63 64 - edid = (struct edid *)udl_get_edid(udl); 65 - if (!edid) { 66 - drm_mode_connector_update_edid_property(connector, NULL); 67 - return 0; 68 - } 69 - 70 - /* 71 - * We only read the main block, but if the monitor reports extension 72 - * blocks then the drm edid code expects them to be present, so patch 73 - * the extension count to 0. 74 - */ 75 - edid->checksum += edid->extensions; 76 - edid->extensions = 0; 77 - 78 - drm_mode_connector_update_edid_property(connector, edid); 79 - ret = drm_add_edid_modes(connector, edid); 80 - kfree(edid); 81 - return ret; 64 + drm_mode_connector_update_edid_property(connector, udl_connector->edid); 65 + if (udl_connector->edid) 66 + return drm_add_edid_modes(connector, udl_connector->edid); 67 + return 0; 82 68 } 83 69 84 70 static int udl_mode_valid(struct drm_connector *connector, ··· 84 96 static enum drm_connector_status 85 97 udl_detect(struct drm_connector *connector, bool force) 86 98 { 87 - if (drm_dev_is_unplugged(connector->dev)) 99 + struct edid *edid; 100 + struct udl_device *udl = connector->dev->dev_private; 101 + struct udl_drm_connector *udl_connector = 102 + container_of(connector, 103 + struct udl_drm_connector, 104 + connector); 105 + 106 + if (udl_connector->edid != NULL) { 107 + kfree(udl_connector->edid); 108 + udl_connector->edid = NULL; 109 + } 110 + 111 + edid = (struct edid *)udl_get_edid(udl); 112 + if (!edid || !memchr_inv(edid, 0, EDID_LENGTH)) 88 113 return connector_status_disconnected; 114 + 115 + udl_connector->edid = edid; 116 + 117 + /* 118 + * We only read the main block, but if the monitor reports extension 119 + * blocks then the drm edid code expects them to be present, so patch 120 + * the extension count to 0. 121 + */ 122 + udl_connector->edid->checksum += 123 + udl_connector->edid->extensions; 124 + udl_connector->edid->extensions = 0; 125 + 89 126 return connector_status_connected; 90 127 } 91 128 ··· 130 117 131 118 static void udl_connector_destroy(struct drm_connector *connector) 132 119 { 120 + struct udl_drm_connector *udl_connector = 121 + container_of(connector, 122 + struct udl_drm_connector, 123 + connector); 124 + 133 125 drm_connector_unregister(connector); 134 126 drm_connector_cleanup(connector); 127 + kfree(udl_connector->edid); 135 128 kfree(connector); 136 129 } 137 130 ··· 157 138 158 139 int udl_connector_init(struct drm_device *dev, struct drm_encoder *encoder) 159 140 { 141 + struct udl_drm_connector *udl_connector; 160 142 struct drm_connector *connector; 161 143 162 - connector = kzalloc(sizeof(struct drm_connector), GFP_KERNEL); 163 - if (!connector) 144 + udl_connector = kzalloc(sizeof(struct udl_drm_connector), GFP_KERNEL); 145 + if (!udl_connector) 164 146 return -ENOMEM; 165 147 166 - drm_connector_init(dev, connector, &udl_connector_funcs, DRM_MODE_CONNECTOR_DVII); 148 + connector = &udl_connector->connector; 149 + drm_connector_init(dev, connector, &udl_connector_funcs, 150 + DRM_MODE_CONNECTOR_DVII); 167 151 drm_connector_helper_add(connector, &udl_connector_helper_funcs); 168 152 169 153 drm_connector_register(connector); 170 154 drm_mode_connector_attach_encoder(connector, encoder); 155 + connector->polled = DRM_CONNECTOR_POLL_HPD | 156 + DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; 171 157 172 158 return 0; 173 159 }
+13
drivers/gpu/drm/udl/udl_connector.h
··· 1 + #ifndef __UDL_CONNECTOR_H__ 2 + #define __UDL_CONNECTOR_H__ 3 + 4 + #include <drm/drm_crtc.h> 5 + 6 + struct udl_drm_connector { 7 + struct drm_connector connector; 8 + /* last udl_detect edid */ 9 + struct edid *edid; 10 + }; 11 + 12 + 13 + #endif //__UDL_CONNECTOR_H__
+4
drivers/gpu/drm/udl/udl_drv.c
··· 14 14 static int udl_usb_suspend(struct usb_interface *interface, 15 15 pm_message_t message) 16 16 { 17 + struct drm_device *dev = usb_get_intfdata(interface); 18 + 19 + drm_kms_helper_poll_disable(dev); 17 20 return 0; 18 21 } 19 22 ··· 24 21 { 25 22 struct drm_device *dev = usb_get_intfdata(interface); 26 23 24 + drm_kms_helper_poll_enable(dev); 27 25 udl_modeset_restore(dev); 28 26 return 0; 29 27 }
+5
drivers/gpu/drm/udl/udl_main.c
··· 11 11 * more details. 12 12 */ 13 13 #include <drm/drmP.h> 14 + #include <drm/drm_crtc_helper.h> 14 15 #include "udl_drv.h" 15 16 16 17 /* -BULK_SIZE as per usb-skeleton. Can we get full page and avoid overhead? */ ··· 351 350 if (ret) 352 351 goto err_fb; 353 352 353 + drm_kms_helper_poll_init(dev); 354 + 354 355 return 0; 355 356 err_fb: 356 357 udl_fbdev_cleanup(dev); ··· 373 370 void udl_driver_unload(struct drm_device *dev) 374 371 { 375 372 struct udl_device *udl = dev->dev_private; 373 + 374 + drm_kms_helper_poll_fini(dev); 376 375 377 376 if (udl->urbs.count) 378 377 udl_free_urb_list(dev);