[PATCH] drm/udl: Refactor edid retreiving in UDL driver (v2)
Now drm/udl driver uses drm_do_get_edid() function to retreive and validate all blocks of EDID data. Old approach had insufficient validation routine and had problems with retreiving of extra blocks Signed-off-by: Robert Tarasov --- drivers/gpu/drm/udl/udl_connector.c | 72 + 1 file changed, 11 insertions(+), 61 deletions(-) diff --git a/drivers/gpu/drm/udl/udl_connector.c b/drivers/gpu/drm/udl/udl_connector.c index c3dc1fd20cb4..c7f8ac2cdbe5 100644 --- a/drivers/gpu/drm/udl/udl_connector.c +++ b/drivers/gpu/drm/udl/udl_connector.c @@ -17,18 +17,19 @@ #include "udl_connector.h" #include "udl_drv.h" -static bool udl_get_edid_block(struct udl_device *udl, int block_idx, - u8 *buff) +static int udl_get_edid_block(void *data, u8 *buf, unsigned int block, + size_t len) { int ret, i; u8 *read_buff; + struct udl_device *udl = data; read_buff = kmalloc(2, GFP_KERNEL); if (!read_buff) - return false; + return -1; - for (i = 0; i < EDID_LENGTH; i++) { - int bval = (i + block_idx * EDID_LENGTH) << 8; + for (i = 0; i < len; i++) { + int bval = (i + block * EDID_LENGTH) << 8; ret = usb_control_msg(udl->udev, usb_rcvctrlpipe(udl->udev, 0), (0x02), (0x80 | (0x02 << 5)), bval, @@ -36,60 +37,13 @@ static bool udl_get_edid_block(struct udl_device *udl, int block_idx, if (ret < 1) { DRM_ERROR("Read EDID byte %d failed err %x\n", i, ret); kfree(read_buff); - return false; + return -1; } - buff[i] = read_buff[1]; + buf[i] = read_buff[1]; } kfree(read_buff); - return true; -} - -static bool udl_get_edid(struct udl_device *udl, u8 **result_buff, -int *result_buff_size) -{ - int i, extensions; - u8 *block_buff = NULL, *buff_ptr; - - block_buff = kmalloc(EDID_LENGTH, GFP_KERNEL); - if (block_buff == NULL) - return false; - - if (udl_get_edid_block(udl, 0, block_buff) && - memchr_inv(block_buff, 0, EDID_LENGTH)) { - extensions = ((struct edid *)block_buff)->extensions; - if (extensions > 0) { - /* we have to read all extensions one by one */ - *result_buff_size = EDID_LENGTH * (extensions + 1); - *result_buff = kmalloc(*result_buff_size, GFP_KERNEL); - buff_ptr = *result_buff; - if (buff_ptr == NULL) { - kfree(block_buff); - return false; - } - memcpy(buff_ptr, block_buff, EDID_LENGTH); - kfree(block_buff); - buff_ptr += EDID_LENGTH; - for (i = 1; i < extensions; ++i) { - if (udl_get_edid_block(udl, i, buff_ptr)) { - buff_ptr += EDID_LENGTH; - } else { - kfree(*result_buff); - *result_buff = NULL; - return false; - } - } - return true; - } - /* we have only base edid block */ - *result_buff = block_buff; - *result_buff_size = EDID_LENGTH; - return true; - } - - kfree(block_buff); - - return false; + return 0; } static int udl_get_modes(struct drm_connector *connector) @@ -121,8 +75,6 @@ static int udl_mode_valid(struct drm_connector *connector, static enum drm_connector_status udl_detect(struct drm_connector *connector, bool force) { - u8 *edid_buff = NULL; - int edid_buff_size = 0; struct udl_device *udl = connector->dev->dev_private; struct udl_drm_connector *udl_connector = container_of(connector, @@ -135,12 +87,10 @@ udl_detect(struct drm_connector *connector, bool force) udl_connector->edid = NULL; } - - if (!udl_get_edid(udl, _buff, _buff_size)) + udl_connector->edid = drm_do_get_edid(connector, udl_get_edid_block, udl); + if (!udl_connector->edid) return connector_status_disconnected; - udl_connector->edid = (struct edid *)edid_buff; - return connector_status_connected; } -- 2.21.0.360.g471c308f928-goog __
[PATCH] drm/udl: Refactor edid retreiving in UDL driver
Now drm/udl driver uses drm_do_get_edid() function to retreive and validate all blocks of EDID data. Old approach had insufficient validation routine and had problems with retreiving of extra blocks Signed-off-by: Robert Tarasov --- drivers/gpu/drm/udl/udl_connector.c | 72 + 1 file changed, 11 insertions(+), 61 deletions(-) diff --git a/drivers/gpu/drm/udl/udl_connector.c b/drivers/gpu/drm/udl/udl_connector.c index c3dc1fd20cb4..c7f8ac2cdbe5 100644 --- a/drivers/gpu/drm/udl/udl_connector.c +++ b/drivers/gpu/drm/udl/udl_connector.c @@ -17,18 +17,19 @@ #include "udl_connector.h" #include "udl_drv.h" -static bool udl_get_edid_block(struct udl_device *udl, int block_idx, - u8 *buff) +static int udl_get_edid_block(void *data, u8 *buf, unsigned int block, + size_t len) { int ret, i; u8 *read_buff; + struct udl_device *udl = data; read_buff = kmalloc(2, GFP_KERNEL); if (!read_buff) - return false; + return -1; - for (i = 0; i < EDID_LENGTH; i++) { - int bval = (i + block_idx * EDID_LENGTH) << 8; + for (i = 0; i < len; i++) { + int bval = (i + block * EDID_LENGTH) << 8; ret = usb_control_msg(udl->udev, usb_rcvctrlpipe(udl->udev, 0), (0x02), (0x80 | (0x02 << 5)), bval, @@ -36,60 +37,13 @@ static bool udl_get_edid_block(struct udl_device *udl, int block_idx, if (ret < 1) { DRM_ERROR("Read EDID byte %d failed err %x\n", i, ret); kfree(read_buff); - return false; + return -1; } - buff[i] = read_buff[1]; + buf[i] = read_buff[1]; } kfree(read_buff); - return true; -} - -static bool udl_get_edid(struct udl_device *udl, u8 **result_buff, -int *result_buff_size) -{ - int i, extensions; - u8 *block_buff = NULL, *buff_ptr; - - block_buff = kmalloc(EDID_LENGTH, GFP_KERNEL); - if (block_buff == NULL) - return false; - - if (udl_get_edid_block(udl, 0, block_buff) && - memchr_inv(block_buff, 0, EDID_LENGTH)) { - extensions = ((struct edid *)block_buff)->extensions; - if (extensions > 0) { - /* we have to read all extensions one by one */ - *result_buff_size = EDID_LENGTH * (extensions + 1); - *result_buff = kmalloc(*result_buff_size, GFP_KERNEL); - buff_ptr = *result_buff; - if (buff_ptr == NULL) { - kfree(block_buff); - return false; - } - memcpy(buff_ptr, block_buff, EDID_LENGTH); - kfree(block_buff); - buff_ptr += EDID_LENGTH; - for (i = 1; i < extensions; ++i) { - if (udl_get_edid_block(udl, i, buff_ptr)) { - buff_ptr += EDID_LENGTH; - } else { - kfree(*result_buff); - *result_buff = NULL; - return false; - } - } - return true; - } - /* we have only base edid block */ - *result_buff = block_buff; - *result_buff_size = EDID_LENGTH; - return true; - } - - kfree(block_buff); - - return false; + return 0; } static int udl_get_modes(struct drm_connector *connector) @@ -121,8 +75,6 @@ static int udl_mode_valid(struct drm_connector *connector, static enum drm_connector_status udl_detect(struct drm_connector *connector, bool force) { - u8 *edid_buff = NULL; - int edid_buff_size = 0; struct udl_device *udl = connector->dev->dev_private; struct udl_drm_connector *udl_connector = container_of(connector, @@ -135,12 +87,10 @@ udl_detect(struct drm_connector *connector, bool force) udl_connector->edid = NULL; } - - if (!udl_get_edid(udl, _buff, _buff_size)) + udl_connector->edid = drm_do_get_edid(connector, udl_get_edid_block, udl); + if (!udl_connector->edid) return connector_status_disconnected; - udl_connector->edid = (struct edid *)edid_buff; - return connector_status_connected; } -- 2.21.0.360.g471c308f928-goog __
Re: [PATCH] drm/udl: Refactor edid retreiving in UDL driver
On Wed, Mar 13, 2019 at 2:26 AM Jani Nikula wrote: > You'll also get support for debugfs and firmware loader EDID override > mechanisms for free. > Yep :) > Signed-off-by missing! > Fixed. read_buff = kmalloc(2, GFP_KERNEL); > > A follow-up cleanup might be to switch to using "u8 read_buff[2];" > instead of kmallocing it. > > I don't claim to understand how the usb stuff works, but otherwise the > patch looks good to me. Nice refactoring! > > "The buffer passed to usb_control_msg may end up in scatter-gather list, and may thus not be on the stack. Having it on the stack usually works on x86, but not on other archs. " ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH] drm/udl: Bugfix in EDID reading routine
I've already submitted the new one. On Tue, Mar 12, 2019 at 1:02 AM Jani Nikula wrote: > On Mon, 11 Mar 2019, Robert Tarasov wrote: > > Yes, you're right. Will prepare another patch which will use > > drm_do_get_edid() > > Oh, you might want to have this patch first with cc: stable and all > that, and do the cleanup afterwards. > > BR, > Jani. > > > > > > On Mon, Mar 11, 2019 at 4:33 AM Jani Nikula > > > wrote: > > > >> On Fri, 08 Mar 2019, Robert Tarasov wrote: > >> > Fixed bug with reading of last EDID extra block in drm/udl driver. > >> > Previouse approach read all the blocks except the last one. > >> > > >> > Signed-off-by: Robert Tarasov > >> > --- > >> > drivers/gpu/drm/udl/udl_connector.c | 2 +- > >> > 1 file changed, 1 insertion(+), 1 deletion(-) > >> > > >> > diff --git a/drivers/gpu/drm/udl/udl_connector.c > >> b/drivers/gpu/drm/udl/udl_connector.c > >> > index c3dc1fd20cb4..68b221b9a01f 100644 > >> > --- a/drivers/gpu/drm/udl/udl_connector.c > >> > +++ b/drivers/gpu/drm/udl/udl_connector.c > >> > @@ -70,7 +70,7 @@ static bool udl_get_edid(struct udl_device *udl, u8 > >> **result_buff, > >> > memcpy(buff_ptr, block_buff, EDID_LENGTH); > >> > kfree(block_buff); > >> > buff_ptr += EDID_LENGTH; > >> > - for (i = 1; i < extensions; ++i) { > >> > + for (i = 1; i <= extensions; ++i) { > >> > if (udl_get_edid_block(udl, i, > buff_ptr)) { > >> > buff_ptr += EDID_LENGTH; > >> > } else { > >> > >> Ugh. Why doesn't udl use drm_do_get_edid()? > >> > >> BR, > >> Jani. > >> > >> > >> -- > >> Jani Nikula, Intel Open Source Graphics Center > >> > > -- > Jani Nikula, Intel Open Source Graphics Center > ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH] drm/udl: Refactor edid retreiving in UDL driver
Now drm/udl driver uses drm_do_get_edid() function to retreive and validate all blocks of EDID data. Old approach had insufficient validation routine and had problems with retreiving of extra blocks --- drivers/gpu/drm/udl/udl_connector.c | 72 + 1 file changed, 11 insertions(+), 61 deletions(-) diff --git a/drivers/gpu/drm/udl/udl_connector.c b/drivers/gpu/drm/udl/udl_connector.c index c3dc1fd20cb4..c7f8ac2cdbe5 100644 --- a/drivers/gpu/drm/udl/udl_connector.c +++ b/drivers/gpu/drm/udl/udl_connector.c @@ -17,18 +17,19 @@ #include "udl_connector.h" #include "udl_drv.h" -static bool udl_get_edid_block(struct udl_device *udl, int block_idx, - u8 *buff) +static int udl_get_edid_block(void *data, u8 *buf, unsigned int block, + size_t len) { int ret, i; u8 *read_buff; + struct udl_device *udl = data; read_buff = kmalloc(2, GFP_KERNEL); if (!read_buff) - return false; + return -1; - for (i = 0; i < EDID_LENGTH; i++) { - int bval = (i + block_idx * EDID_LENGTH) << 8; + for (i = 0; i < len; i++) { + int bval = (i + block * EDID_LENGTH) << 8; ret = usb_control_msg(udl->udev, usb_rcvctrlpipe(udl->udev, 0), (0x02), (0x80 | (0x02 << 5)), bval, @@ -36,60 +37,13 @@ static bool udl_get_edid_block(struct udl_device *udl, int block_idx, if (ret < 1) { DRM_ERROR("Read EDID byte %d failed err %x\n", i, ret); kfree(read_buff); - return false; + return -1; } - buff[i] = read_buff[1]; + buf[i] = read_buff[1]; } kfree(read_buff); - return true; -} - -static bool udl_get_edid(struct udl_device *udl, u8 **result_buff, -int *result_buff_size) -{ - int i, extensions; - u8 *block_buff = NULL, *buff_ptr; - - block_buff = kmalloc(EDID_LENGTH, GFP_KERNEL); - if (block_buff == NULL) - return false; - - if (udl_get_edid_block(udl, 0, block_buff) && - memchr_inv(block_buff, 0, EDID_LENGTH)) { - extensions = ((struct edid *)block_buff)->extensions; - if (extensions > 0) { - /* we have to read all extensions one by one */ - *result_buff_size = EDID_LENGTH * (extensions + 1); - *result_buff = kmalloc(*result_buff_size, GFP_KERNEL); - buff_ptr = *result_buff; - if (buff_ptr == NULL) { - kfree(block_buff); - return false; - } - memcpy(buff_ptr, block_buff, EDID_LENGTH); - kfree(block_buff); - buff_ptr += EDID_LENGTH; - for (i = 1; i < extensions; ++i) { - if (udl_get_edid_block(udl, i, buff_ptr)) { - buff_ptr += EDID_LENGTH; - } else { - kfree(*result_buff); - *result_buff = NULL; - return false; - } - } - return true; - } - /* we have only base edid block */ - *result_buff = block_buff; - *result_buff_size = EDID_LENGTH; - return true; - } - - kfree(block_buff); - - return false; + return 0; } static int udl_get_modes(struct drm_connector *connector) @@ -121,8 +75,6 @@ static int udl_mode_valid(struct drm_connector *connector, static enum drm_connector_status udl_detect(struct drm_connector *connector, bool force) { - u8 *edid_buff = NULL; - int edid_buff_size = 0; struct udl_device *udl = connector->dev->dev_private; struct udl_drm_connector *udl_connector = container_of(connector, @@ -135,12 +87,10 @@ udl_detect(struct drm_connector *connector, bool force) udl_connector->edid = NULL; } - - if (!udl_get_edid(udl, _buff, _buff_size)) + udl_connector->edid = drm_do_get_edid(connector, udl_get_edid_block, udl); + if (!udl_connector->edid) return connector_status_disconnected; - udl_connector->edid = (struct edid *)edid_buff; - return connector_status_connected; } -- 2.21.0.360.g471c308f928-goog ___ dri-devel mailing list dri-devel@lists.freedesktop.org
Re: [PATCH] drm/udl: Bugfix in EDID reading routine
Yes, you're right. Will prepare another patch which will use drm_do_get_edid() On Mon, Mar 11, 2019 at 4:33 AM Jani Nikula wrote: > On Fri, 08 Mar 2019, Robert Tarasov wrote: > > Fixed bug with reading of last EDID extra block in drm/udl driver. > > Previouse approach read all the blocks except the last one. > > > > Signed-off-by: Robert Tarasov > > --- > > drivers/gpu/drm/udl/udl_connector.c | 2 +- > > 1 file changed, 1 insertion(+), 1 deletion(-) > > > > diff --git a/drivers/gpu/drm/udl/udl_connector.c > b/drivers/gpu/drm/udl/udl_connector.c > > index c3dc1fd20cb4..68b221b9a01f 100644 > > --- a/drivers/gpu/drm/udl/udl_connector.c > > +++ b/drivers/gpu/drm/udl/udl_connector.c > > @@ -70,7 +70,7 @@ static bool udl_get_edid(struct udl_device *udl, u8 > **result_buff, > > memcpy(buff_ptr, block_buff, EDID_LENGTH); > > kfree(block_buff); > > buff_ptr += EDID_LENGTH; > > - for (i = 1; i < extensions; ++i) { > > + for (i = 1; i <= extensions; ++i) { > > if (udl_get_edid_block(udl, i, buff_ptr)) { > > buff_ptr += EDID_LENGTH; > > } else { > > Ugh. Why doesn't udl use drm_do_get_edid()? > > BR, > Jani. > > > -- > Jani Nikula, Intel Open Source Graphics Center > ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH] drm/udl: Cut >165 MHz modes for DVI
Filter out all modes with clock higher than 165 MHz for DVI connector in drm/udl driver. Signed-off-by: Robert Tarasov --- drivers/gpu/drm/udl/udl_connector.c | 8 1 file changed, 8 insertions(+) diff --git a/drivers/gpu/drm/udl/udl_connector.c b/drivers/gpu/drm/udl/udl_connector.c index 68b221b9a01f..3b9be500b9ae 100644 --- a/drivers/gpu/drm/udl/udl_connector.c +++ b/drivers/gpu/drm/udl/udl_connector.c @@ -109,6 +109,14 @@ static int udl_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { struct udl_device *udl = connector->dev->dev_private; + int con_type = connector->connector_type; + + if ((con_type == DRM_MODE_CONNECTOR_DVII || +con_type == DRM_MODE_CONNECTOR_DVID || +con_type == DRM_MODE_CONNECTOR_DVIA) && + mode->clock > 165000) + return MODE_CLOCK_HIGH; + if (!udl->sku_pixel_limit) return 0; -- 2.21.0.360.g471c308f928-goog ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH] drm/udl: Bugfix in EDID reading routine
Fixed bug with reading of last EDID extra block in drm/udl driver. Previouse approach read all the blocks except the last one. Signed-off-by: Robert Tarasov --- drivers/gpu/drm/udl/udl_connector.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/udl/udl_connector.c b/drivers/gpu/drm/udl/udl_connector.c index c3dc1fd20cb4..68b221b9a01f 100644 --- a/drivers/gpu/drm/udl/udl_connector.c +++ b/drivers/gpu/drm/udl/udl_connector.c @@ -70,7 +70,7 @@ static bool udl_get_edid(struct udl_device *udl, u8 **result_buff, memcpy(buff_ptr, block_buff, EDID_LENGTH); kfree(block_buff); buff_ptr += EDID_LENGTH; - for (i = 1; i < extensions; ++i) { + for (i = 1; i <= extensions; ++i) { if (udl_get_edid_block(udl, i, buff_ptr)) { buff_ptr += EDID_LENGTH; } else { -- 2.21.0.360.g471c308f928-goog ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
Re: [PATCH] drm/udl: Fixed problem with UDL adpater reconnection
Fixed. On Thu, Oct 12, 2017 at 12:56 PM, Alex Deucher <alexdeuc...@gmail.com> wrote: > On Wed, Oct 11, 2017 at 4:41 PM, Robert Tarasov > <tutankha...@chromium.org> wrote: > > Fixed problem with DisplayLink and DisplayLink certified adapters when > they > > didn't want to work if they were initialized with disconnected DVI > cable. Now > > udl driver checks and updates adapter's connection state every 10 > seconds, as > > well as retreives all the edid data blocks instead of only base one. > Previous > > approch could lead to improper initialization of video mode with certain > > monitors. > > > > Seems like this should be split into two patches: > > 1. rework the EDID handling in the driver > 2. enable drm connector polling. > > Alex > > > Signed-off-by: Robert Tarasov <tutankha...@chromium.org> > > --- > > drivers/gpu/drm/udl/udl_connector.c | 153 > > > drivers/gpu/drm/udl/udl_connector.h | 13 +++ > > drivers/gpu/drm/udl/udl_drv.c | 4 + > > drivers/gpu/drm/udl/udl_main.c | 5 ++ > > 4 files changed, 125 insertions(+), 50 deletions(-) > > create mode 100644 drivers/gpu/drm/udl/udl_connector.h > > > > diff --git a/drivers/gpu/drm/udl/udl_connector.c > b/drivers/gpu/drm/udl/udl_connector.c > > index 9f9a49748d17..b2d9ffcc29aa 100644 > > --- a/drivers/gpu/drm/udl/udl_connector.c > > +++ b/drivers/gpu/drm/udl/udl_connector.c > > @@ -14,70 +14,95 @@ > > #include > > #include > > #include > > +#include "udl_connector.h" > > #include "udl_drv.h" > > > > -/* dummy connector to just get EDID, > > - all UDL appear to have a DVI-D */ > > - > > -static u8 *udl_get_edid(struct udl_device *udl) > > +static bool udl_get_edid_block(struct udl_device *udl, int block_idx, > > + u8 *buff) > > { > > - u8 *block; > > - char *rbuf; > > int ret, i; > > + u8 *read_buff; > > > > - block = kmalloc(EDID_LENGTH, GFP_KERNEL); > > - if (block == NULL) > > - return NULL; > > - > > - rbuf = kmalloc(2, GFP_KERNEL); > > - if (rbuf == NULL) > > - goto error; > > + read_buff = kmalloc(2, GFP_KERNEL); > > + if (!read_buff) > > + return false; > > > > for (i = 0; i < EDID_LENGTH; i++) { > > + int bval = (i + block_idx * EDID_LENGTH) << 8; > > ret = usb_control_msg(udl->udev, > > - usb_rcvctrlpipe(udl->udev, 0), > (0x02), > > - (0x80 | (0x02 << 5)), i << 8, > 0xA1, rbuf, 2, > > - HZ); > > + usb_rcvctrlpipe(udl->udev, 0), > > + (0x02), (0x80 | (0x02 << 5)), > bval, > > + 0xA1, read_buff, 2, HZ); > > if (ret < 1) { > > DRM_ERROR("Read EDID byte %d failed err %x\n", > i, ret); > > - goto error; > > + kfree(read_buff); > > + return false; > > } > > - block[i] = rbuf[1]; > > + buff[i] = read_buff[1]; > > } > > > > - kfree(rbuf); > > - return block; > > - > > -error: > > - kfree(block); > > - kfree(rbuf); > > - return NULL; > > + kfree(read_buff); > > + return true; > > } > > > > -static int udl_get_modes(struct drm_connector *connector) > > +static bool udl_get_edid(struct udl_device *udl, u8 **result_buff, > > +int *result_buff_size) > > { > > - struct udl_device *udl = connector->dev->dev_private; > > - struct edid *edid; > > - int ret; > > - > > - edid = (struct edid *)udl_get_edid(udl); > > - if (!edid) { > > - drm_mode_connector_update_edid_property(connector, > NULL); > > - return 0; > > + int i, extensions; > > + u8 *block_buff = NULL, *buff_ptr; > > + > > + block_buff = kmalloc(EDID_LENGTH, GFP_KERNEL); > > + if (block_buff == NULL) > > + return false; > > + > > + if (udl_get_edi
[PATCH 2/2] drm/udl: Reading all edid blocks in DRM/UDL driver
Now DRM/UDL driver retreives all edid data blocks instead of only base one. Previous approch could lead to improper initialization of video mode with certain monitors. Signed-off-by: Robert Tarasov <tutankha...@chromium.org> --- drivers/gpu/drm/udl/udl_connector.c | 106 +++- 1 file changed, 68 insertions(+), 38 deletions(-) diff --git a/drivers/gpu/drm/udl/udl_connector.c b/drivers/gpu/drm/udl/udl_connector.c index 6a9250ac8f29..c3dc1fd20cb4 100644 --- a/drivers/gpu/drm/udl/udl_connector.c +++ b/drivers/gpu/drm/udl/udl_connector.c @@ -17,42 +17,79 @@ #include "udl_connector.h" #include "udl_drv.h" -/* dummy connector to just get EDID, - all UDL appear to have a DVI-D */ - -static u8 *udl_get_edid(struct udl_device *udl) +static bool udl_get_edid_block(struct udl_device *udl, int block_idx, + u8 *buff) { - u8 *block; - char *rbuf; int ret, i; + u8 *read_buff; - block = kmalloc(EDID_LENGTH, GFP_KERNEL); - if (block == NULL) - return NULL; - - rbuf = kmalloc(2, GFP_KERNEL); - if (rbuf == NULL) - goto error; + read_buff = kmalloc(2, GFP_KERNEL); + if (!read_buff) + return false; for (i = 0; i < EDID_LENGTH; i++) { + int bval = (i + block_idx * EDID_LENGTH) << 8; ret = usb_control_msg(udl->udev, - usb_rcvctrlpipe(udl->udev, 0), (0x02), - (0x80 | (0x02 << 5)), i << 8, 0xA1, rbuf, 2, - HZ); + usb_rcvctrlpipe(udl->udev, 0), + (0x02), (0x80 | (0x02 << 5)), bval, + 0xA1, read_buff, 2, HZ); if (ret < 1) { DRM_ERROR("Read EDID byte %d failed err %x\n", i, ret); - goto error; + kfree(read_buff); + return false; } - block[i] = rbuf[1]; + buff[i] = read_buff[1]; } - kfree(rbuf); - return block; + kfree(read_buff); + return true; +} -error: - kfree(block); - kfree(rbuf); - return NULL; +static bool udl_get_edid(struct udl_device *udl, u8 **result_buff, +int *result_buff_size) +{ + int i, extensions; + u8 *block_buff = NULL, *buff_ptr; + + block_buff = kmalloc(EDID_LENGTH, GFP_KERNEL); + if (block_buff == NULL) + return false; + + if (udl_get_edid_block(udl, 0, block_buff) && + memchr_inv(block_buff, 0, EDID_LENGTH)) { + extensions = ((struct edid *)block_buff)->extensions; + if (extensions > 0) { + /* we have to read all extensions one by one */ + *result_buff_size = EDID_LENGTH * (extensions + 1); + *result_buff = kmalloc(*result_buff_size, GFP_KERNEL); + buff_ptr = *result_buff; + if (buff_ptr == NULL) { + kfree(block_buff); + return false; + } + memcpy(buff_ptr, block_buff, EDID_LENGTH); + kfree(block_buff); + buff_ptr += EDID_LENGTH; + for (i = 1; i < extensions; ++i) { + if (udl_get_edid_block(udl, i, buff_ptr)) { + buff_ptr += EDID_LENGTH; + } else { + kfree(*result_buff); + *result_buff = NULL; + return false; + } + } + return true; + } + /* we have only base edid block */ + *result_buff = block_buff; + *result_buff_size = EDID_LENGTH; + return true; + } + + kfree(block_buff); + + return false; } static int udl_get_modes(struct drm_connector *connector) @@ -84,33 +121,26 @@ static int udl_mode_valid(struct drm_connector *connector, static enum drm_connector_status udl_detect(struct drm_connector *connector, bool force) { - struct edid *edid; + u8 *edid_buff = NULL; + int edid_buff_size = 0; struct udl_device *udl = connector->dev->dev_private; struct udl_drm_connector *udl_connector = container_of(connector, struct udl_drm_connector, connector); +
[PATCH 1/2] 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 <tutankha...@chromium.org> --- drivers/gpu/drm/udl/udl_connector.c | 76 - drivers/gpu/drm/udl/udl_connector.h | 13 +++ drivers/gpu/drm/udl/udl_drv.c | 4 ++ drivers/gpu/drm/udl/udl_main.c | 5 +++ 4 files changed, 72 insertions(+), 26 deletions(-) create mode 100644 drivers/gpu/drm/udl/udl_connector.h diff --git a/drivers/gpu/drm/udl/udl_connector.c b/drivers/gpu/drm/udl/udl_connector.c index 091ca81658eb..6a9250ac8f29 100644 --- a/drivers/gpu/drm/udl/udl_connector.c +++ b/drivers/gpu/drm/udl/udl_connector.c @@ -14,6 +14,7 @@ #include #include #include +#include "udl_connector.h" #include "udl_drv.h" /* dummy connector to just get EDID, @@ -56,28 +57,15 @@ static u8 *udl_get_edid(struct udl_device *udl) static int udl_get_modes(struct drm_connector *connector) { - struct udl_device *udl = connector->dev->dev_private; - struct edid *edid; - int ret; - - edid = (struct edid *)udl_get_edid(udl); - if (!edid) { - drm_mode_connector_update_edid_property(connector, NULL); - return 0; - } - - /* -* We only read the main block, but if the monitor reports extension -* blocks then the drm edid code expects them to be present, so patch -* the extension count to 0. -*/ - edid->checksum += edid->extensions; - edid->extensions = 0; - - drm_mode_connector_update_edid_property(connector, edid); - ret = drm_add_edid_modes(connector, edid); - kfree(edid); - return ret; + struct udl_drm_connector *udl_connector = + container_of(connector, + struct udl_drm_connector, + connector); + + drm_mode_connector_update_edid_property(connector, udl_connector->edid); + if (udl_connector->edid) + return drm_add_edid_modes(connector, udl_connector->edid); + return 0; } static int udl_mode_valid(struct drm_connector *connector, @@ -96,8 +84,33 @@ static int udl_mode_valid(struct drm_connector *connector, static enum drm_connector_status udl_detect(struct drm_connector *connector, bool force) { - if (drm_dev_is_unplugged(connector->dev)) + struct edid *edid; + struct udl_device *udl = connector->dev->dev_private; + struct udl_drm_connector *udl_connector = + container_of(connector, + struct udl_drm_connector, + connector); + + if (udl_connector->edid != NULL) { + kfree(udl_connector->edid); + udl_connector->edid = NULL; + } + + edid = (struct edid *)udl_get_edid(udl); + if (!edid || !memchr_inv(edid, 0, EDID_LENGTH)) return connector_status_disconnected; + + udl_connector->edid = edid; + + /* +* We only read the main block, but if the monitor reports extension +* blocks then the drm edid code expects them to be present, so patch +* the extension count to 0. +*/ + udl_connector->edid->checksum += + udl_connector->edid->extensions; + udl_connector->edid->extensions = 0; + return connector_status_connected; } @@ -117,8 +130,14 @@ static int udl_connector_set_property(struct drm_connector *connector, static void udl_connector_destroy(struct drm_connector *connector) { + struct udl_drm_connector *udl_connector = + container_of(connector, + struct udl_drm_connector, + connector); + drm_connector_unregister(connector); drm_connector_cleanup(connector); + kfree(udl_connector->edid); kfree(connector); } @@ -138,17 +157,22 @@ static const struct drm_connector_funcs udl_connector_funcs = { int udl_connector_init(struct drm_device *dev, struct drm_encoder *encoder) { + struct udl_drm_connector *udl_connector; struct drm_connector *connector; - connector = kzalloc(sizeof(struct drm_connector), GFP_KERNEL); - if (!connector) + udl_connector = kzalloc(sizeof(struct udl_drm_connector), GFP_KERNEL); + if (!udl_connector) return -ENOMEM; - drm_connector_init(dev, connector, _connector_funcs, DRM_MODE_CONNECTOR_DVII); + connector = _connector->connector; + drm_connector_init(dev, connector, _connector_funcs,
[PATCH] drm/udl: Fixed problem with UDL adpater reconnection
Fixed problem with DisplayLink and DisplayLink certified adapters when they didn't want to work if they were initialized with disconnected DVI cable. Now udl driver checks and updates adapter's connection state every 10 seconds, as well as retreives all the edid data blocks instead of only base one. Previous approch could lead to improper initialization of video mode with certain monitors. Signed-off-by: Robert Tarasov <tutankha...@chromium.org> --- drivers/gpu/drm/udl/udl_connector.c | 153 drivers/gpu/drm/udl/udl_connector.h | 13 +++ drivers/gpu/drm/udl/udl_drv.c | 4 + drivers/gpu/drm/udl/udl_main.c | 5 ++ 4 files changed, 125 insertions(+), 50 deletions(-) create mode 100644 drivers/gpu/drm/udl/udl_connector.h diff --git a/drivers/gpu/drm/udl/udl_connector.c b/drivers/gpu/drm/udl/udl_connector.c index 9f9a49748d17..b2d9ffcc29aa 100644 --- a/drivers/gpu/drm/udl/udl_connector.c +++ b/drivers/gpu/drm/udl/udl_connector.c @@ -14,70 +14,95 @@ #include #include #include +#include "udl_connector.h" #include "udl_drv.h" -/* dummy connector to just get EDID, - all UDL appear to have a DVI-D */ - -static u8 *udl_get_edid(struct udl_device *udl) +static bool udl_get_edid_block(struct udl_device *udl, int block_idx, + u8 *buff) { - u8 *block; - char *rbuf; int ret, i; + u8 *read_buff; - block = kmalloc(EDID_LENGTH, GFP_KERNEL); - if (block == NULL) - return NULL; - - rbuf = kmalloc(2, GFP_KERNEL); - if (rbuf == NULL) - goto error; + read_buff = kmalloc(2, GFP_KERNEL); + if (!read_buff) + return false; for (i = 0; i < EDID_LENGTH; i++) { + int bval = (i + block_idx * EDID_LENGTH) << 8; ret = usb_control_msg(udl->udev, - usb_rcvctrlpipe(udl->udev, 0), (0x02), - (0x80 | (0x02 << 5)), i << 8, 0xA1, rbuf, 2, - HZ); + usb_rcvctrlpipe(udl->udev, 0), + (0x02), (0x80 | (0x02 << 5)), bval, + 0xA1, read_buff, 2, HZ); if (ret < 1) { DRM_ERROR("Read EDID byte %d failed err %x\n", i, ret); - goto error; + kfree(read_buff); + return false; } - block[i] = rbuf[1]; + buff[i] = read_buff[1]; } - kfree(rbuf); - return block; - -error: - kfree(block); - kfree(rbuf); - return NULL; + kfree(read_buff); + return true; } -static int udl_get_modes(struct drm_connector *connector) +static bool udl_get_edid(struct udl_device *udl, u8 **result_buff, +int *result_buff_size) { - struct udl_device *udl = connector->dev->dev_private; - struct edid *edid; - int ret; - - edid = (struct edid *)udl_get_edid(udl); - if (!edid) { - drm_mode_connector_update_edid_property(connector, NULL); - return 0; + int i, extensions; + u8 *block_buff = NULL, *buff_ptr; + + block_buff = kmalloc(EDID_LENGTH, GFP_KERNEL); + if (block_buff == NULL) + return false; + + if (udl_get_edid_block(udl, 0, block_buff) && + memchr_inv(block_buff, 0, EDID_LENGTH)) { + extensions = ((struct edid *)block_buff)->extensions; + if (extensions > 0) { + /* we have to read all extensions one by one */ + *result_buff_size = EDID_LENGTH * (extensions + 1); + *result_buff = kmalloc(*result_buff_size, GFP_KERNEL); + buff_ptr = *result_buff; + if (buff_ptr == NULL) { + kfree(block_buff); + return false; + } + memcpy(buff_ptr, block_buff, EDID_LENGTH); + kfree(block_buff); + buff_ptr += EDID_LENGTH; + for (i = 1; i < extensions; ++i) { + if (udl_get_edid_block(udl, i, buff_ptr)) { + buff_ptr += EDID_LENGTH; + } else { + kfree(*result_buff); + *result_buff = NULL; + return false; + } + } + return true; + } + /* we have only base edid block */ +