aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRobert Tarasov <tutankhamen@chromium.org>2017-10-12 20:13:50 -0400
committerAlex Deucher <alexander.deucher@amd.com>2017-10-16 15:37:19 -0400
commit75c65ee20adebca6ac29d6f9d301ced073dd8030 (patch)
treeba34537db1dccfe10ed4d6cf246737027c1dda87
parentafdfc4c6f55f953e073f0eef9e2afa3c8cf78d7c (diff)
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 <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-2-tutankhamen@chromium.org
-rw-r--r--drivers/gpu/drm/udl/udl_connector.c106
1 files changed, 68 insertions, 38 deletions
diff --git a/drivers/gpu/drm/udl/udl_connector.c b/drivers/gpu/drm/udl/udl_connector.c
index b4d1464d2d87..c0bbd1ce15f0 100644
--- a/drivers/gpu/drm/udl/udl_connector.c
+++ b/drivers/gpu/drm/udl/udl_connector.c
@@ -17,42 +17,79 @@
17#include "udl_connector.h" 17#include "udl_connector.h"
18#include "udl_drv.h" 18#include "udl_drv.h"
19 19
20/* dummy connector to just get EDID, 20static bool udl_get_edid_block(struct udl_device *udl, int block_idx,
21 all UDL appear to have a DVI-D */ 21 u8 *buff)
22
23static u8 *udl_get_edid(struct udl_device *udl)
24{ 22{
25 u8 *block;
26 char *rbuf;
27 int ret, i; 23 int ret, i;
24 u8 *read_buff;
28 25
29 block = kmalloc(EDID_LENGTH, GFP_KERNEL); 26 read_buff = kmalloc(2, GFP_KERNEL);
30 if (block == NULL) 27 if (!read_buff)
31 return NULL; 28 return false;
32
33 rbuf = kmalloc(2, GFP_KERNEL);
34 if (rbuf == NULL)
35 goto error;
36 29
37 for (i = 0; i < EDID_LENGTH; i++) { 30 for (i = 0; i < EDID_LENGTH; i++) {
31 int bval = (i + block_idx * EDID_LENGTH) << 8;
38 ret = usb_control_msg(udl->udev, 32 ret = usb_control_msg(udl->udev,
39 usb_rcvctrlpipe(udl->udev, 0), (0x02), 33 usb_rcvctrlpipe(udl->udev, 0),
40 (0x80 | (0x02 << 5)), i << 8, 0xA1, rbuf, 2, 34 (0x02), (0x80 | (0x02 << 5)), bval,
41 HZ); 35 0xA1, read_buff, 2, HZ);
42 if (ret < 1) { 36 if (ret < 1) {
43 DRM_ERROR("Read EDID byte %d failed err %x\n", i, ret); 37 DRM_ERROR("Read EDID byte %d failed err %x\n", i, ret);
44 goto error; 38 kfree(read_buff);
39 return false;
45 } 40 }
46 block[i] = rbuf[1]; 41 buff[i] = read_buff[1];
47 } 42 }
48 43
49 kfree(rbuf); 44 kfree(read_buff);
50 return block; 45 return true;
46}
51 47
52error: 48static bool udl_get_edid(struct udl_device *udl, u8 **result_buff,
53 kfree(block); 49 int *result_buff_size)
54 kfree(rbuf); 50{
55 return NULL; 51 int i, extensions;
52 u8 *block_buff = NULL, *buff_ptr;
53
54 block_buff = kmalloc(EDID_LENGTH, GFP_KERNEL);
55 if (block_buff == NULL)
56 return false;
57
58 if (udl_get_edid_block(udl, 0, block_buff) &&
59 memchr_inv(block_buff, 0, EDID_LENGTH)) {
60 extensions = ((struct edid *)block_buff)->extensions;
61 if (extensions > 0) {
62 /* we have to read all extensions one by one */
63 *result_buff_size = EDID_LENGTH * (extensions + 1);
64 *result_buff = kmalloc(*result_buff_size, GFP_KERNEL);
65 buff_ptr = *result_buff;
66 if (buff_ptr == NULL) {
67 kfree(block_buff);
68 return false;
69 }
70 memcpy(buff_ptr, block_buff, EDID_LENGTH);
71 kfree(block_buff);
72 buff_ptr += EDID_LENGTH;
73 for (i = 1; i < extensions; ++i) {
74 if (udl_get_edid_block(udl, i, buff_ptr)) {
75 buff_ptr += EDID_LENGTH;
76 } else {
77 kfree(*result_buff);
78 *result_buff = NULL;
79 return false;
80 }
81 }
82 return true;
83 }
84 /* we have only base edid block */
85 *result_buff = block_buff;
86 *result_buff_size = EDID_LENGTH;
87 return true;
88 }
89
90 kfree(block_buff);
91
92 return false;
56} 93}
57 94
58static int udl_get_modes(struct drm_connector *connector) 95static int udl_get_modes(struct drm_connector *connector)
@@ -84,33 +121,26 @@ static int udl_mode_valid(struct drm_connector *connector,
84static enum drm_connector_status 121static enum drm_connector_status
85udl_detect(struct drm_connector *connector, bool force) 122udl_detect(struct drm_connector *connector, bool force)
86{ 123{
87 struct edid *edid; 124 u8 *edid_buff = NULL;
125 int edid_buff_size = 0;
88 struct udl_device *udl = connector->dev->dev_private; 126 struct udl_device *udl = connector->dev->dev_private;
89 struct udl_drm_connector *udl_connector = 127 struct udl_drm_connector *udl_connector =
90 container_of(connector, 128 container_of(connector,
91 struct udl_drm_connector, 129 struct udl_drm_connector,
92 connector); 130 connector);
93 131
132 /* cleanup previous edid */
94 if (udl_connector->edid != NULL) { 133 if (udl_connector->edid != NULL) {
95 kfree(udl_connector->edid); 134 kfree(udl_connector->edid);
96 udl_connector->edid = NULL; 135 udl_connector->edid = NULL;
97 } 136 }
98 137
99 edid = (struct edid *)udl_get_edid(udl);
100 if (!edid || !memchr_inv(edid, 0, EDID_LENGTH))
101 return connector_status_disconnected;
102 138
103 udl_connector->edid = edid; 139 if (!udl_get_edid(udl, &edid_buff, &edid_buff_size))
104 140 return connector_status_disconnected;
105 /*
106 * We only read the main block, but if the monitor reports extension
107 * blocks then the drm edid code expects them to be present, so patch
108 * the extension count to 0.
109 */
110 udl_connector->edid->checksum +=
111 udl_connector->edid->extensions;
112 udl_connector->edid->extensions = 0;
113 141
142 udl_connector->edid = (struct edid *)edid_buff;
143
114 return connector_status_connected; 144 return connector_status_connected;
115} 145}
116 146