diff options
author | Robert Tarasov <tutankhamen@chromium.org> | 2017-10-12 20:13:50 -0400 |
---|---|---|
committer | Alex Deucher <alexander.deucher@amd.com> | 2017-10-16 15:37:19 -0400 |
commit | 75c65ee20adebca6ac29d6f9d301ced073dd8030 (patch) | |
tree | ba34537db1dccfe10ed4d6cf246737027c1dda87 | |
parent | afdfc4c6f55f953e073f0eef9e2afa3c8cf78d7c (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.c | 106 |
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, | 20 | static 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 | |||
23 | static 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 | ||
52 | error: | 48 | static 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 | ||
58 | static int udl_get_modes(struct drm_connector *connector) | 95 | static int udl_get_modes(struct drm_connector *connector) |
@@ -84,33 +121,26 @@ static int udl_mode_valid(struct drm_connector *connector, | |||
84 | static enum drm_connector_status | 121 | static enum drm_connector_status |
85 | udl_detect(struct drm_connector *connector, bool force) | 122 | udl_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 | ||