<div dir="ltr">Fixed.</div><div class="gmail_extra"><br><div class="gmail_quote">On Thu, Oct 12, 2017 at 12:56 PM, Alex Deucher <span dir="ltr"><<a href="mailto:alexdeucher@gmail.com" target="_blank">alexdeucher@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">On Wed, Oct 11, 2017 at 4:41 PM, Robert Tarasov<br>
<<a href="mailto:tutankhamen@chromium.org">tutankhamen@chromium.org</a>> wrote:<br>
> Fixed problem with DisplayLink and DisplayLink certified adapters when they<br>
> didn't want to work if they were initialized with disconnected DVI cable. Now<br>
> udl driver checks and updates adapter's connection state every 10 seconds, as<br>
> well as retreives all the edid data blocks instead of only base one. Previous<br>
> approch could lead to improper initialization of video mode with certain<br>
> monitors.<br>
><br>
<br>
</span>Seems like this should be split into two patches:<br>
<br>
1. rework the EDID handling in the driver<br>
2. enable drm connector polling.<br>
<br>
Alex<br>
<div><div class="h5"><br>
> Signed-off-by: Robert Tarasov <<a href="mailto:tutankhamen@chromium.org">tutankhamen@chromium.org</a>><br>
> ---<br>
>  drivers/gpu/drm/udl/udl_<wbr>connector.c | 153 ++++++++++++++++++++++++------<wbr>------<br>
>  drivers/gpu/drm/udl/udl_<wbr>connector.h |  13 +++<br>
>  drivers/gpu/drm/udl/udl_drv.c       |   4 +<br>
>  drivers/gpu/drm/udl/udl_main.c      |   5 ++<br>
>  4 files changed, 125 insertions(+), 50 deletions(-)<br>
>  create mode 100644 drivers/gpu/drm/udl/udl_<wbr>connector.h<br>
><br>
> diff --git a/drivers/gpu/drm/udl/udl_<wbr>connector.c b/drivers/gpu/drm/udl/udl_<wbr>connector.c<br>
> index 9f9a49748d17..b2d9ffcc29aa 100644<br>
> --- a/drivers/gpu/drm/udl/udl_<wbr>connector.c<br>
> +++ b/drivers/gpu/drm/udl/udl_<wbr>connector.c<br>
> @@ -14,70 +14,95 @@<br>
>  #include <drm/drm_crtc.h><br>
>  #include <drm/drm_edid.h><br>
>  #include <drm/drm_crtc_helper.h><br>
> +#include "udl_connector.h"<br>
>  #include "udl_drv.h"<br>
><br>
> -/* dummy connector to just get EDID,<br>
> -   all UDL appear to have a DVI-D */<br>
> -<br>
> -static u8 *udl_get_edid(struct udl_device *udl)<br>
> +static bool udl_get_edid_block(struct udl_device *udl, int block_idx,<br>
> +                                                          u8 *buff)<br>
>  {<br>
> -       u8 *block;<br>
> -       char *rbuf;<br>
>         int ret, i;<br>
> +       u8 *read_buff;<br>
><br>
> -       block = kmalloc(EDID_LENGTH, GFP_KERNEL);<br>
> -       if (block == NULL)<br>
> -               return NULL;<br>
> -<br>
> -       rbuf = kmalloc(2, GFP_KERNEL);<br>
> -       if (rbuf == NULL)<br>
> -               goto error;<br>
> +       read_buff = kmalloc(2, GFP_KERNEL);<br>
> +       if (!read_buff)<br>
> +               return false;<br>
><br>
>         for (i = 0; i < EDID_LENGTH; i++) {<br>
> +               int bval = (i + block_idx * EDID_LENGTH) << 8;<br>
>                 ret = usb_control_msg(udl->udev,<br>
> -                                     usb_rcvctrlpipe(udl->udev, 0), (0x02),<br>
> -                                     (0x80 | (0x02 << 5)), i << 8, 0xA1, rbuf, 2,<br>
> -                                     HZ);<br>
> +                                     usb_rcvctrlpipe(udl->udev, 0),<br>
> +                                         (0x02), (0x80 | (0x02 << 5)), bval,<br>
> +                                         0xA1, read_buff, 2, HZ);<br>
>                 if (ret < 1) {<br>
>                         DRM_ERROR("Read EDID byte %d failed err %x\n", i, ret);<br>
> -                       goto error;<br>
> +                       kfree(read_buff);<br>
> +                       return false;<br>
>                 }<br>
> -               block[i] = rbuf[1];<br>
> +               buff[i] = read_buff[1];<br>
>         }<br>
><br>
> -       kfree(rbuf);<br>
> -       return block;<br>
> -<br>
> -error:<br>
> -       kfree(block);<br>
> -       kfree(rbuf);<br>
> -       return NULL;<br>
> +       kfree(read_buff);<br>
> +       return true;<br>
>  }<br>
><br>
> -static int udl_get_modes(struct drm_connector *connector)<br>
> +static bool udl_get_edid(struct udl_device *udl, u8 **result_buff,<br>
> +                        int *result_buff_size)<br>
>  {<br>
> -       struct udl_device *udl = connector->dev->dev_private;<br>
> -       struct edid *edid;<br>
> -       int ret;<br>
> -<br>
> -       edid = (struct edid *)udl_get_edid(udl);<br>
> -       if (!edid) {<br>
> -               drm_mode_connector_update_<wbr>edid_property(connector, NULL);<br>
> -               return 0;<br>
> +       int i, extensions;<br>
> +       u8 *block_buff = NULL, *buff_ptr;<br>
> +<br>
> +       block_buff = kmalloc(EDID_LENGTH, GFP_KERNEL);<br>
> +       if (block_buff == NULL)<br>
> +               return false;<br>
> +<br>
> +       if (udl_get_edid_block(udl, 0, block_buff) &&<br>
> +           memchr_inv(block_buff, 0, EDID_LENGTH)) {<br>
> +               extensions = ((struct edid *)block_buff)->extensions;<br>
> +               if (extensions > 0) {<br>
> +                       /* we have to read all extensions one by one */<br>
> +                       *result_buff_size = EDID_LENGTH * (extensions + 1);<br>
> +                       *result_buff = kmalloc(*result_buff_size, GFP_KERNEL);<br>
> +                       buff_ptr = *result_buff;<br>
> +                       if (buff_ptr == NULL) {<br>
> +                               kfree(block_buff);<br>
> +                               return false;<br>
> +                       }<br>
> +                       memcpy(buff_ptr, block_buff, EDID_LENGTH);<br>
> +                       kfree(block_buff);<br>
> +                       buff_ptr += EDID_LENGTH;<br>
> +                       for (i = 1; i < extensions; ++i) {<br>
> +                               if (udl_get_edid_block(udl, i, buff_ptr)) {<br>
> +                                       buff_ptr += EDID_LENGTH;<br>
> +                               } else {<br>
> +                                       kfree(*result_buff);<br>
> +                                       *result_buff = NULL;<br>
> +                                       return false;<br>
> +                               }<br>
> +                       }<br>
> +                       return true;<br>
> +               }<br>
> +               /* we have only base edid block */<br>
> +               *result_buff = block_buff;<br>
> +               *result_buff_size = EDID_LENGTH;<br>
> +               return true;<br>
>         }<br>
><br>
> -       /*<br>
> -        * We only read the main block, but if the monitor reports extension<br>
> -        * blocks then the drm edid code expects them to be present, so patch<br>
> -        * the extension count to 0.<br>
> -        */<br>
> -       edid->checksum += edid->extensions;<br>
> -       edid->extensions = 0;<br>
> -<br>
> -       drm_mode_connector_update_<wbr>edid_property(connector, edid);<br>
> -       ret = drm_add_edid_modes(connector, edid);<br>
> -       kfree(edid);<br>
> -       return ret;<br>
> +       kfree(block_buff);<br>
> +<br>
> +       return false;<br>
> +}<br>
> +<br>
> +static int udl_get_modes(struct drm_connector *connector)<br>
> +{<br>
> +       struct udl_drm_connector *udl_connector =<br>
> +                                       container_of(connector,<br>
> +                                       struct udl_drm_connector,<br>
> +                                       connector);<br>
> +<br>
> +       drm_mode_connector_update_<wbr>edid_property(connector, udl_connector->edid);<br>
> +       if (udl_connector->edid)<br>
> +               return drm_add_edid_modes(connector, udl_connector->edid);<br>
> +       return 0;<br>
>  }<br>
><br>
>  static int udl_mode_valid(struct drm_connector *connector,<br>
> @@ -96,8 +121,25 @@ static int udl_mode_valid(struct drm_connector *connector,<br>
>  static enum drm_connector_status<br>
>  udl_detect(struct drm_connector *connector, bool force)<br>
>  {<br>
> -       if (drm_dev_is_unplugged(<wbr>connector->dev))<br>
> +       u8 *edid_buff = NULL;<br>
> +       int edid_buff_size = 0;<br>
> +       struct udl_device *udl = connector->dev->dev_private;<br>
> +       struct udl_drm_connector *udl_connector =<br>
> +                                       container_of(connector,<br>
> +                                       struct udl_drm_connector,<br>
> +                                       connector);<br>
> +<br>
> +       /* cleanup previous edid */<br>
> +       if (udl_connector->edid != NULL) {<br>
> +               kfree(udl_connector->edid);<br>
> +               udl_connector->edid = NULL;<br>
> +       }<br>
> +<br>
> +       if (!udl_get_edid(udl, &edid_buff, &edid_buff_size))<br>
>                 return connector_status_disconnected;<br>
> +<br>
> +       udl_connector->edid = (struct edid *)edid_buff;<br>
> +<br>
>         return connector_status_connected;<br>
>  }<br>
><br>
> @@ -117,8 +159,14 @@ static int udl_connector_set_property(<wbr>struct drm_connector *connector,<br>
><br>
>  static void udl_connector_destroy(struct drm_connector *connector)<br>
>  {<br>
> +       struct udl_drm_connector *udl_connector =<br>
> +                                       container_of(connector,<br>
> +                                       struct udl_drm_connector,<br>
> +                                       connector);<br>
> +<br>
>         drm_connector_unregister(<wbr>connector);<br>
>         drm_connector_cleanup(<wbr>connector);<br>
> +       kfree(udl_connector->edid);<br>
>         kfree(connector);<br>
>  }<br>
><br>
> @@ -138,17 +186,22 @@ static const struct drm_connector_funcs udl_connector_funcs = {<br>
><br>
>  int udl_connector_init(struct drm_device *dev, struct drm_encoder *encoder)<br>
>  {<br>
> +       struct udl_drm_connector *udl_connector;<br>
>         struct drm_connector *connector;<br>
><br>
> -       connector = kzalloc(sizeof(struct drm_connector), GFP_KERNEL);<br>
> -       if (!connector)<br>
> +       udl_connector = kzalloc(sizeof(struct udl_drm_connector), GFP_KERNEL);<br>
> +       if (!udl_connector)<br>
>                 return -ENOMEM;<br>
><br>
> -       drm_connector_init(dev, connector, &udl_connector_funcs, DRM_MODE_CONNECTOR_DVII);<br>
> +       connector = &udl_connector->connector;<br>
> +       drm_connector_init(dev, connector, &udl_connector_funcs,<br>
> +                          DRM_MODE_CONNECTOR_DVII);<br>
>         drm_connector_helper_add(<wbr>connector, &udl_connector_helper_funcs);<br>
><br>
>         drm_connector_register(<wbr>connector);<br>
>         drm_mode_connector_attach_<wbr>encoder(connector, encoder);<br>
> +       connector->polled = DRM_CONNECTOR_POLL_HPD |<br>
> +               DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;<br>
><br>
>         return 0;<br>
>  }<br>
> diff --git a/drivers/gpu/drm/udl/udl_<wbr>connector.h b/drivers/gpu/drm/udl/udl_<wbr>connector.h<br>
> new file mode 100644<br>
> index 000000000000..0fb0db5c4612<br>
> --- /dev/null<br>
> +++ b/drivers/gpu/drm/udl/udl_<wbr>connector.h<br>
> @@ -0,0 +1,13 @@<br>
> +#ifndef __UDL_CONNECTOR_H__<br>
> +#define __UDL_CONNECTOR_H__<br>
> +<br>
> +#include <drm/drm_crtc.h><br>
> +<br>
> +struct udl_drm_connector {<br>
> +       struct drm_connector connector;<br>
> +       /* last udl_detect edid */<br>
> +       struct edid *edid;<br>
> +};<br>
> +<br>
> +<br>
> +#endif //__UDL_CONNECTOR_H__<br>
> diff --git a/drivers/gpu/drm/udl/udl_drv.<wbr>c b/drivers/gpu/drm/udl/udl_drv.<wbr>c<br>
> index 31421b6b586e..3c45a3064726 100644<br>
> --- a/drivers/gpu/drm/udl/udl_drv.<wbr>c<br>
> +++ b/drivers/gpu/drm/udl/udl_drv.<wbr>c<br>
> @@ -14,6 +14,9 @@<br>
>  static int udl_usb_suspend(struct usb_interface *interface,<br>
>                            pm_message_t message)<br>
>  {<br>
> +       struct drm_device *dev = usb_get_intfdata(interface);<br>
> +<br>
> +       drm_kms_helper_poll_disable(<wbr>dev);<br>
>         return 0;<br>
>  }<br>
><br>
> @@ -21,6 +24,7 @@ static int udl_usb_resume(struct usb_interface *interface)<br>
>  {<br>
>         struct drm_device *dev = usb_get_intfdata(interface);<br>
><br>
> +       drm_kms_helper_poll_enable(<wbr>dev);<br>
>         udl_modeset_restore(dev);<br>
>         return 0;<br>
>  }<br>
> diff --git a/drivers/gpu/drm/udl/udl_<wbr>main.c b/drivers/gpu/drm/udl/udl_<wbr>main.c<br>
> index 0328b2c7b210..f1ec4528a73e 100644<br>
> --- a/drivers/gpu/drm/udl/udl_<wbr>main.c<br>
> +++ b/drivers/gpu/drm/udl/udl_<wbr>main.c<br>
> @@ -11,6 +11,7 @@<br>
>   * more details.<br>
>   */<br>
>  #include <drm/drmP.h><br>
> +#include <drm/drm_crtc_helper.h><br>
>  #include "udl_drv.h"<br>
><br>
>  /* -BULK_SIZE as per usb-skeleton. Can we get full page and avoid overhead? */<br>
> @@ -350,6 +351,8 @@ int udl_driver_load(struct drm_device *dev, unsigned long flags)<br>
>         if (ret)<br>
>                 goto err_fb;<br>
><br>
> +       drm_kms_helper_poll_init(dev);<br>
> +<br>
>         return 0;<br>
>  err_fb:<br>
>         udl_fbdev_cleanup(dev);<br>
> @@ -371,6 +374,8 @@ void udl_driver_unload(struct drm_device *dev)<br>
>  {<br>
>         struct udl_device *udl = dev->dev_private;<br>
><br>
> +       drm_kms_helper_poll_fini(dev);<br>
> +<br>
>         if (udl->urbs.count)<br>
>                 udl_free_urb_list(dev);<br>
><br>
> --<br>
> 2.15.0.rc0.271.g36b669edcc-<wbr>goog<br>
><br>
</div></div>> ______________________________<wbr>_________________<br>
> dri-devel mailing list<br>
> <a href="mailto:dri-devel@lists.freedesktop.org">dri-devel@lists.freedesktop.<wbr>org</a><br>
> <a href="https://lists.freedesktop.org/mailman/listinfo/dri-devel" rel="noreferrer" target="_blank">https://lists.freedesktop.org/<wbr>mailman/listinfo/dri-devel</a><br>
</blockquote></div><br></div>