Commit e14cbee401cd00779a5267128371506b22c77bc9
Committed by
Dave Airlie
1 parent
b1e3a6d1c4
Exists in
master
and in
7 other branches
drm: Fix shifts which were miscalculated when converting from bitfields.
Looks like I managed to mess up most shifts when converting from bitfields. :( The patch below works on my Thinkpad T500 (as well as on my PowerBook, where the previous change worked as well, maybe out of luck...). I'd appreciate more testing and eyes looking over it though. Signed-off-by: Michel Dänzer <daenzer@vmware.com> Tested-by: Michael Pyne <mpyne@kde.org> Signed-off-by: Dave Airlie <airlied@linux.ie>
Showing 2 changed files with 25 additions and 25 deletions Inline Diff
drivers/gpu/drm/drm_edid.c
1 | /* | 1 | /* |
2 | * Copyright (c) 2006 Luc Verhaegen (quirks list) | 2 | * Copyright (c) 2006 Luc Verhaegen (quirks list) |
3 | * Copyright (c) 2007-2008 Intel Corporation | 3 | * Copyright (c) 2007-2008 Intel Corporation |
4 | * Jesse Barnes <jesse.barnes@intel.com> | 4 | * Jesse Barnes <jesse.barnes@intel.com> |
5 | * | 5 | * |
6 | * DDC probing routines (drm_ddc_read & drm_do_probe_ddc_edid) originally from | 6 | * DDC probing routines (drm_ddc_read & drm_do_probe_ddc_edid) originally from |
7 | * FB layer. | 7 | * FB layer. |
8 | * Copyright (C) 2006 Dennis Munsie <dmunsie@cecropia.com> | 8 | * Copyright (C) 2006 Dennis Munsie <dmunsie@cecropia.com> |
9 | * | 9 | * |
10 | * Permission is hereby granted, free of charge, to any person obtaining a | 10 | * Permission is hereby granted, free of charge, to any person obtaining a |
11 | * copy of this software and associated documentation files (the "Software"), | 11 | * copy of this software and associated documentation files (the "Software"), |
12 | * to deal in the Software without restriction, including without limitation | 12 | * to deal in the Software without restriction, including without limitation |
13 | * the rights to use, copy, modify, merge, publish, distribute, sub license, | 13 | * the rights to use, copy, modify, merge, publish, distribute, sub license, |
14 | * and/or sell copies of the Software, and to permit persons to whom the | 14 | * and/or sell copies of the Software, and to permit persons to whom the |
15 | * Software is furnished to do so, subject to the following conditions: | 15 | * Software is furnished to do so, subject to the following conditions: |
16 | * | 16 | * |
17 | * The above copyright notice and this permission notice (including the | 17 | * The above copyright notice and this permission notice (including the |
18 | * next paragraph) shall be included in all copies or substantial portions | 18 | * next paragraph) shall be included in all copies or substantial portions |
19 | * of the Software. | 19 | * of the Software. |
20 | * | 20 | * |
21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
22 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 22 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
23 | * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL | 23 | * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL |
24 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 24 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
25 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | 25 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
26 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | 26 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
27 | * DEALINGS IN THE SOFTWARE. | 27 | * DEALINGS IN THE SOFTWARE. |
28 | */ | 28 | */ |
29 | #include <linux/kernel.h> | 29 | #include <linux/kernel.h> |
30 | #include <linux/i2c.h> | 30 | #include <linux/i2c.h> |
31 | #include <linux/i2c-algo-bit.h> | 31 | #include <linux/i2c-algo-bit.h> |
32 | #include "drmP.h" | 32 | #include "drmP.h" |
33 | #include "drm_edid.h" | 33 | #include "drm_edid.h" |
34 | 34 | ||
35 | /* | 35 | /* |
36 | * TODO: | 36 | * TODO: |
37 | * - support EDID 1.4 (incl. CE blocks) | 37 | * - support EDID 1.4 (incl. CE blocks) |
38 | */ | 38 | */ |
39 | 39 | ||
40 | /* | 40 | /* |
41 | * EDID blocks out in the wild have a variety of bugs, try to collect | 41 | * EDID blocks out in the wild have a variety of bugs, try to collect |
42 | * them here (note that userspace may work around broken monitors first, | 42 | * them here (note that userspace may work around broken monitors first, |
43 | * but fixes should make their way here so that the kernel "just works" | 43 | * but fixes should make their way here so that the kernel "just works" |
44 | * on as many displays as possible). | 44 | * on as many displays as possible). |
45 | */ | 45 | */ |
46 | 46 | ||
47 | /* First detailed mode wrong, use largest 60Hz mode */ | 47 | /* First detailed mode wrong, use largest 60Hz mode */ |
48 | #define EDID_QUIRK_PREFER_LARGE_60 (1 << 0) | 48 | #define EDID_QUIRK_PREFER_LARGE_60 (1 << 0) |
49 | /* Reported 135MHz pixel clock is too high, needs adjustment */ | 49 | /* Reported 135MHz pixel clock is too high, needs adjustment */ |
50 | #define EDID_QUIRK_135_CLOCK_TOO_HIGH (1 << 1) | 50 | #define EDID_QUIRK_135_CLOCK_TOO_HIGH (1 << 1) |
51 | /* Prefer the largest mode at 75 Hz */ | 51 | /* Prefer the largest mode at 75 Hz */ |
52 | #define EDID_QUIRK_PREFER_LARGE_75 (1 << 2) | 52 | #define EDID_QUIRK_PREFER_LARGE_75 (1 << 2) |
53 | /* Detail timing is in cm not mm */ | 53 | /* Detail timing is in cm not mm */ |
54 | #define EDID_QUIRK_DETAILED_IN_CM (1 << 3) | 54 | #define EDID_QUIRK_DETAILED_IN_CM (1 << 3) |
55 | /* Detailed timing descriptors have bogus size values, so just take the | 55 | /* Detailed timing descriptors have bogus size values, so just take the |
56 | * maximum size and use that. | 56 | * maximum size and use that. |
57 | */ | 57 | */ |
58 | #define EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE (1 << 4) | 58 | #define EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE (1 << 4) |
59 | /* Monitor forgot to set the first detailed is preferred bit. */ | 59 | /* Monitor forgot to set the first detailed is preferred bit. */ |
60 | #define EDID_QUIRK_FIRST_DETAILED_PREFERRED (1 << 5) | 60 | #define EDID_QUIRK_FIRST_DETAILED_PREFERRED (1 << 5) |
61 | /* use +hsync +vsync for detailed mode */ | 61 | /* use +hsync +vsync for detailed mode */ |
62 | #define EDID_QUIRK_DETAILED_SYNC_PP (1 << 6) | 62 | #define EDID_QUIRK_DETAILED_SYNC_PP (1 << 6) |
63 | 63 | ||
64 | static struct edid_quirk { | 64 | static struct edid_quirk { |
65 | char *vendor; | 65 | char *vendor; |
66 | int product_id; | 66 | int product_id; |
67 | u32 quirks; | 67 | u32 quirks; |
68 | } edid_quirk_list[] = { | 68 | } edid_quirk_list[] = { |
69 | /* Acer AL1706 */ | 69 | /* Acer AL1706 */ |
70 | { "ACR", 44358, EDID_QUIRK_PREFER_LARGE_60 }, | 70 | { "ACR", 44358, EDID_QUIRK_PREFER_LARGE_60 }, |
71 | /* Acer F51 */ | 71 | /* Acer F51 */ |
72 | { "API", 0x7602, EDID_QUIRK_PREFER_LARGE_60 }, | 72 | { "API", 0x7602, EDID_QUIRK_PREFER_LARGE_60 }, |
73 | /* Unknown Acer */ | 73 | /* Unknown Acer */ |
74 | { "ACR", 2423, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, | 74 | { "ACR", 2423, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, |
75 | 75 | ||
76 | /* Belinea 10 15 55 */ | 76 | /* Belinea 10 15 55 */ |
77 | { "MAX", 1516, EDID_QUIRK_PREFER_LARGE_60 }, | 77 | { "MAX", 1516, EDID_QUIRK_PREFER_LARGE_60 }, |
78 | { "MAX", 0x77e, EDID_QUIRK_PREFER_LARGE_60 }, | 78 | { "MAX", 0x77e, EDID_QUIRK_PREFER_LARGE_60 }, |
79 | 79 | ||
80 | /* Envision Peripherals, Inc. EN-7100e */ | 80 | /* Envision Peripherals, Inc. EN-7100e */ |
81 | { "EPI", 59264, EDID_QUIRK_135_CLOCK_TOO_HIGH }, | 81 | { "EPI", 59264, EDID_QUIRK_135_CLOCK_TOO_HIGH }, |
82 | 82 | ||
83 | /* Funai Electronics PM36B */ | 83 | /* Funai Electronics PM36B */ |
84 | { "FCM", 13600, EDID_QUIRK_PREFER_LARGE_75 | | 84 | { "FCM", 13600, EDID_QUIRK_PREFER_LARGE_75 | |
85 | EDID_QUIRK_DETAILED_IN_CM }, | 85 | EDID_QUIRK_DETAILED_IN_CM }, |
86 | 86 | ||
87 | /* LG Philips LCD LP154W01-A5 */ | 87 | /* LG Philips LCD LP154W01-A5 */ |
88 | { "LPL", 0, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE }, | 88 | { "LPL", 0, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE }, |
89 | { "LPL", 0x2a00, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE }, | 89 | { "LPL", 0x2a00, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE }, |
90 | 90 | ||
91 | /* Philips 107p5 CRT */ | 91 | /* Philips 107p5 CRT */ |
92 | { "PHL", 57364, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, | 92 | { "PHL", 57364, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, |
93 | 93 | ||
94 | /* Proview AY765C */ | 94 | /* Proview AY765C */ |
95 | { "PTS", 765, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, | 95 | { "PTS", 765, EDID_QUIRK_FIRST_DETAILED_PREFERRED }, |
96 | 96 | ||
97 | /* Samsung SyncMaster 205BW. Note: irony */ | 97 | /* Samsung SyncMaster 205BW. Note: irony */ |
98 | { "SAM", 541, EDID_QUIRK_DETAILED_SYNC_PP }, | 98 | { "SAM", 541, EDID_QUIRK_DETAILED_SYNC_PP }, |
99 | /* Samsung SyncMaster 22[5-6]BW */ | 99 | /* Samsung SyncMaster 22[5-6]BW */ |
100 | { "SAM", 596, EDID_QUIRK_PREFER_LARGE_60 }, | 100 | { "SAM", 596, EDID_QUIRK_PREFER_LARGE_60 }, |
101 | { "SAM", 638, EDID_QUIRK_PREFER_LARGE_60 }, | 101 | { "SAM", 638, EDID_QUIRK_PREFER_LARGE_60 }, |
102 | }; | 102 | }; |
103 | 103 | ||
104 | 104 | ||
105 | /* Valid EDID header has these bytes */ | 105 | /* Valid EDID header has these bytes */ |
106 | static u8 edid_header[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; | 106 | static u8 edid_header[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; |
107 | 107 | ||
108 | /** | 108 | /** |
109 | * edid_is_valid - sanity check EDID data | 109 | * edid_is_valid - sanity check EDID data |
110 | * @edid: EDID data | 110 | * @edid: EDID data |
111 | * | 111 | * |
112 | * Sanity check the EDID block by looking at the header, the version number | 112 | * Sanity check the EDID block by looking at the header, the version number |
113 | * and the checksum. Return 0 if the EDID doesn't check out, or 1 if it's | 113 | * and the checksum. Return 0 if the EDID doesn't check out, or 1 if it's |
114 | * valid. | 114 | * valid. |
115 | */ | 115 | */ |
116 | static bool edid_is_valid(struct edid *edid) | 116 | static bool edid_is_valid(struct edid *edid) |
117 | { | 117 | { |
118 | int i; | 118 | int i; |
119 | u8 csum = 0; | 119 | u8 csum = 0; |
120 | u8 *raw_edid = (u8 *)edid; | 120 | u8 *raw_edid = (u8 *)edid; |
121 | 121 | ||
122 | if (memcmp(edid->header, edid_header, sizeof(edid_header))) | 122 | if (memcmp(edid->header, edid_header, sizeof(edid_header))) |
123 | goto bad; | 123 | goto bad; |
124 | if (edid->version != 1) { | 124 | if (edid->version != 1) { |
125 | DRM_ERROR("EDID has major version %d, instead of 1\n", edid->version); | 125 | DRM_ERROR("EDID has major version %d, instead of 1\n", edid->version); |
126 | goto bad; | 126 | goto bad; |
127 | } | 127 | } |
128 | if (edid->revision > 4) | 128 | if (edid->revision > 4) |
129 | DRM_DEBUG("EDID minor > 4, assuming backward compatibility\n"); | 129 | DRM_DEBUG("EDID minor > 4, assuming backward compatibility\n"); |
130 | 130 | ||
131 | for (i = 0; i < EDID_LENGTH; i++) | 131 | for (i = 0; i < EDID_LENGTH; i++) |
132 | csum += raw_edid[i]; | 132 | csum += raw_edid[i]; |
133 | if (csum) { | 133 | if (csum) { |
134 | DRM_ERROR("EDID checksum is invalid, remainder is %d\n", csum); | 134 | DRM_ERROR("EDID checksum is invalid, remainder is %d\n", csum); |
135 | goto bad; | 135 | goto bad; |
136 | } | 136 | } |
137 | 137 | ||
138 | return 1; | 138 | return 1; |
139 | 139 | ||
140 | bad: | 140 | bad: |
141 | if (raw_edid) { | 141 | if (raw_edid) { |
142 | DRM_ERROR("Raw EDID:\n"); | 142 | DRM_ERROR("Raw EDID:\n"); |
143 | print_hex_dump_bytes(KERN_ERR, DUMP_PREFIX_NONE, raw_edid, EDID_LENGTH); | 143 | print_hex_dump_bytes(KERN_ERR, DUMP_PREFIX_NONE, raw_edid, EDID_LENGTH); |
144 | printk("\n"); | 144 | printk("\n"); |
145 | } | 145 | } |
146 | return 0; | 146 | return 0; |
147 | } | 147 | } |
148 | 148 | ||
149 | /** | 149 | /** |
150 | * edid_vendor - match a string against EDID's obfuscated vendor field | 150 | * edid_vendor - match a string against EDID's obfuscated vendor field |
151 | * @edid: EDID to match | 151 | * @edid: EDID to match |
152 | * @vendor: vendor string | 152 | * @vendor: vendor string |
153 | * | 153 | * |
154 | * Returns true if @vendor is in @edid, false otherwise | 154 | * Returns true if @vendor is in @edid, false otherwise |
155 | */ | 155 | */ |
156 | static bool edid_vendor(struct edid *edid, char *vendor) | 156 | static bool edid_vendor(struct edid *edid, char *vendor) |
157 | { | 157 | { |
158 | char edid_vendor[3]; | 158 | char edid_vendor[3]; |
159 | 159 | ||
160 | edid_vendor[0] = ((edid->mfg_id[0] & 0x7c) >> 2) + '@'; | 160 | edid_vendor[0] = ((edid->mfg_id[0] & 0x7c) >> 2) + '@'; |
161 | edid_vendor[1] = (((edid->mfg_id[0] & 0x3) << 3) | | 161 | edid_vendor[1] = (((edid->mfg_id[0] & 0x3) << 3) | |
162 | ((edid->mfg_id[1] & 0xe0) >> 5)) + '@'; | 162 | ((edid->mfg_id[1] & 0xe0) >> 5)) + '@'; |
163 | edid_vendor[2] = (edid->mfg_id[1] & 0x1f) + '@'; | 163 | edid_vendor[2] = (edid->mfg_id[1] & 0x1f) + '@'; |
164 | 164 | ||
165 | return !strncmp(edid_vendor, vendor, 3); | 165 | return !strncmp(edid_vendor, vendor, 3); |
166 | } | 166 | } |
167 | 167 | ||
168 | /** | 168 | /** |
169 | * edid_get_quirks - return quirk flags for a given EDID | 169 | * edid_get_quirks - return quirk flags for a given EDID |
170 | * @edid: EDID to process | 170 | * @edid: EDID to process |
171 | * | 171 | * |
172 | * This tells subsequent routines what fixes they need to apply. | 172 | * This tells subsequent routines what fixes they need to apply. |
173 | */ | 173 | */ |
174 | static u32 edid_get_quirks(struct edid *edid) | 174 | static u32 edid_get_quirks(struct edid *edid) |
175 | { | 175 | { |
176 | struct edid_quirk *quirk; | 176 | struct edid_quirk *quirk; |
177 | int i; | 177 | int i; |
178 | 178 | ||
179 | for (i = 0; i < ARRAY_SIZE(edid_quirk_list); i++) { | 179 | for (i = 0; i < ARRAY_SIZE(edid_quirk_list); i++) { |
180 | quirk = &edid_quirk_list[i]; | 180 | quirk = &edid_quirk_list[i]; |
181 | 181 | ||
182 | if (edid_vendor(edid, quirk->vendor) && | 182 | if (edid_vendor(edid, quirk->vendor) && |
183 | (EDID_PRODUCT_ID(edid) == quirk->product_id)) | 183 | (EDID_PRODUCT_ID(edid) == quirk->product_id)) |
184 | return quirk->quirks; | 184 | return quirk->quirks; |
185 | } | 185 | } |
186 | 186 | ||
187 | return 0; | 187 | return 0; |
188 | } | 188 | } |
189 | 189 | ||
190 | #define MODE_SIZE(m) ((m)->hdisplay * (m)->vdisplay) | 190 | #define MODE_SIZE(m) ((m)->hdisplay * (m)->vdisplay) |
191 | #define MODE_REFRESH_DIFF(m,r) (abs((m)->vrefresh - target_refresh)) | 191 | #define MODE_REFRESH_DIFF(m,r) (abs((m)->vrefresh - target_refresh)) |
192 | 192 | ||
193 | 193 | ||
194 | /** | 194 | /** |
195 | * edid_fixup_preferred - set preferred modes based on quirk list | 195 | * edid_fixup_preferred - set preferred modes based on quirk list |
196 | * @connector: has mode list to fix up | 196 | * @connector: has mode list to fix up |
197 | * @quirks: quirks list | 197 | * @quirks: quirks list |
198 | * | 198 | * |
199 | * Walk the mode list for @connector, clearing the preferred status | 199 | * Walk the mode list for @connector, clearing the preferred status |
200 | * on existing modes and setting it anew for the right mode ala @quirks. | 200 | * on existing modes and setting it anew for the right mode ala @quirks. |
201 | */ | 201 | */ |
202 | static void edid_fixup_preferred(struct drm_connector *connector, | 202 | static void edid_fixup_preferred(struct drm_connector *connector, |
203 | u32 quirks) | 203 | u32 quirks) |
204 | { | 204 | { |
205 | struct drm_display_mode *t, *cur_mode, *preferred_mode; | 205 | struct drm_display_mode *t, *cur_mode, *preferred_mode; |
206 | int target_refresh = 0; | 206 | int target_refresh = 0; |
207 | 207 | ||
208 | if (list_empty(&connector->probed_modes)) | 208 | if (list_empty(&connector->probed_modes)) |
209 | return; | 209 | return; |
210 | 210 | ||
211 | if (quirks & EDID_QUIRK_PREFER_LARGE_60) | 211 | if (quirks & EDID_QUIRK_PREFER_LARGE_60) |
212 | target_refresh = 60; | 212 | target_refresh = 60; |
213 | if (quirks & EDID_QUIRK_PREFER_LARGE_75) | 213 | if (quirks & EDID_QUIRK_PREFER_LARGE_75) |
214 | target_refresh = 75; | 214 | target_refresh = 75; |
215 | 215 | ||
216 | preferred_mode = list_first_entry(&connector->probed_modes, | 216 | preferred_mode = list_first_entry(&connector->probed_modes, |
217 | struct drm_display_mode, head); | 217 | struct drm_display_mode, head); |
218 | 218 | ||
219 | list_for_each_entry_safe(cur_mode, t, &connector->probed_modes, head) { | 219 | list_for_each_entry_safe(cur_mode, t, &connector->probed_modes, head) { |
220 | cur_mode->type &= ~DRM_MODE_TYPE_PREFERRED; | 220 | cur_mode->type &= ~DRM_MODE_TYPE_PREFERRED; |
221 | 221 | ||
222 | if (cur_mode == preferred_mode) | 222 | if (cur_mode == preferred_mode) |
223 | continue; | 223 | continue; |
224 | 224 | ||
225 | /* Largest mode is preferred */ | 225 | /* Largest mode is preferred */ |
226 | if (MODE_SIZE(cur_mode) > MODE_SIZE(preferred_mode)) | 226 | if (MODE_SIZE(cur_mode) > MODE_SIZE(preferred_mode)) |
227 | preferred_mode = cur_mode; | 227 | preferred_mode = cur_mode; |
228 | 228 | ||
229 | /* At a given size, try to get closest to target refresh */ | 229 | /* At a given size, try to get closest to target refresh */ |
230 | if ((MODE_SIZE(cur_mode) == MODE_SIZE(preferred_mode)) && | 230 | if ((MODE_SIZE(cur_mode) == MODE_SIZE(preferred_mode)) && |
231 | MODE_REFRESH_DIFF(cur_mode, target_refresh) < | 231 | MODE_REFRESH_DIFF(cur_mode, target_refresh) < |
232 | MODE_REFRESH_DIFF(preferred_mode, target_refresh)) { | 232 | MODE_REFRESH_DIFF(preferred_mode, target_refresh)) { |
233 | preferred_mode = cur_mode; | 233 | preferred_mode = cur_mode; |
234 | } | 234 | } |
235 | } | 235 | } |
236 | 236 | ||
237 | preferred_mode->type |= DRM_MODE_TYPE_PREFERRED; | 237 | preferred_mode->type |= DRM_MODE_TYPE_PREFERRED; |
238 | } | 238 | } |
239 | 239 | ||
240 | /** | 240 | /** |
241 | * drm_mode_std - convert standard mode info (width, height, refresh) into mode | 241 | * drm_mode_std - convert standard mode info (width, height, refresh) into mode |
242 | * @t: standard timing params | 242 | * @t: standard timing params |
243 | * | 243 | * |
244 | * Take the standard timing params (in this case width, aspect, and refresh) | 244 | * Take the standard timing params (in this case width, aspect, and refresh) |
245 | * and convert them into a real mode using CVT. | 245 | * and convert them into a real mode using CVT. |
246 | * | 246 | * |
247 | * Punts for now, but should eventually use the FB layer's CVT based mode | 247 | * Punts for now, but should eventually use the FB layer's CVT based mode |
248 | * generation code. | 248 | * generation code. |
249 | */ | 249 | */ |
250 | struct drm_display_mode *drm_mode_std(struct drm_device *dev, | 250 | struct drm_display_mode *drm_mode_std(struct drm_device *dev, |
251 | struct std_timing *t) | 251 | struct std_timing *t) |
252 | { | 252 | { |
253 | struct drm_display_mode *mode; | 253 | struct drm_display_mode *mode; |
254 | int hsize = t->hsize * 8 + 248, vsize; | 254 | int hsize = t->hsize * 8 + 248, vsize; |
255 | unsigned aspect_ratio = (t->vfreq_aspect & EDID_TIMING_ASPECT_MASK) | 255 | unsigned aspect_ratio = (t->vfreq_aspect & EDID_TIMING_ASPECT_MASK) |
256 | >> EDID_TIMING_ASPECT_SHIFT; | 256 | >> EDID_TIMING_ASPECT_SHIFT; |
257 | 257 | ||
258 | mode = drm_mode_create(dev); | 258 | mode = drm_mode_create(dev); |
259 | if (!mode) | 259 | if (!mode) |
260 | return NULL; | 260 | return NULL; |
261 | 261 | ||
262 | if (aspect_ratio == 0) | 262 | if (aspect_ratio == 0) |
263 | vsize = (hsize * 10) / 16; | 263 | vsize = (hsize * 10) / 16; |
264 | else if (aspect_ratio == 1) | 264 | else if (aspect_ratio == 1) |
265 | vsize = (hsize * 3) / 4; | 265 | vsize = (hsize * 3) / 4; |
266 | else if (aspect_ratio == 2) | 266 | else if (aspect_ratio == 2) |
267 | vsize = (hsize * 4) / 5; | 267 | vsize = (hsize * 4) / 5; |
268 | else | 268 | else |
269 | vsize = (hsize * 9) / 16; | 269 | vsize = (hsize * 9) / 16; |
270 | 270 | ||
271 | drm_mode_set_name(mode); | 271 | drm_mode_set_name(mode); |
272 | 272 | ||
273 | return mode; | 273 | return mode; |
274 | } | 274 | } |
275 | 275 | ||
276 | /** | 276 | /** |
277 | * drm_mode_detailed - create a new mode from an EDID detailed timing section | 277 | * drm_mode_detailed - create a new mode from an EDID detailed timing section |
278 | * @dev: DRM device (needed to create new mode) | 278 | * @dev: DRM device (needed to create new mode) |
279 | * @edid: EDID block | 279 | * @edid: EDID block |
280 | * @timing: EDID detailed timing info | 280 | * @timing: EDID detailed timing info |
281 | * @quirks: quirks to apply | 281 | * @quirks: quirks to apply |
282 | * | 282 | * |
283 | * An EDID detailed timing block contains enough info for us to create and | 283 | * An EDID detailed timing block contains enough info for us to create and |
284 | * return a new struct drm_display_mode. | 284 | * return a new struct drm_display_mode. |
285 | */ | 285 | */ |
286 | static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev, | 286 | static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev, |
287 | struct edid *edid, | 287 | struct edid *edid, |
288 | struct detailed_timing *timing, | 288 | struct detailed_timing *timing, |
289 | u32 quirks) | 289 | u32 quirks) |
290 | { | 290 | { |
291 | struct drm_display_mode *mode; | 291 | struct drm_display_mode *mode; |
292 | struct detailed_pixel_timing *pt = &timing->data.pixel_data; | 292 | struct detailed_pixel_timing *pt = &timing->data.pixel_data; |
293 | unsigned hactive = (pt->hactive_hblank_hi & 0xf0) << 4 | pt->hactive_lo; | 293 | unsigned hactive = (pt->hactive_hblank_hi & 0xf0) << 4 | pt->hactive_lo; |
294 | unsigned vactive = (pt->vactive_vblank_hi & 0xf0) << 4 | pt->vactive_lo; | 294 | unsigned vactive = (pt->vactive_vblank_hi & 0xf0) << 4 | pt->vactive_lo; |
295 | unsigned hblank = (pt->hactive_hblank_hi & 0xf) << 8 | pt->hblank_lo; | 295 | unsigned hblank = (pt->hactive_hblank_hi & 0xf) << 8 | pt->hblank_lo; |
296 | unsigned vblank = (pt->vactive_vblank_hi & 0xf) << 8 | pt->vblank_lo; | 296 | unsigned vblank = (pt->vactive_vblank_hi & 0xf) << 8 | pt->vblank_lo; |
297 | unsigned hsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0x3) << 8 | pt->hsync_offset_lo; | 297 | unsigned hsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0xc0) << 2 | pt->hsync_offset_lo; |
298 | unsigned hsync_pulse_width = (pt->hsync_vsync_offset_pulse_width_hi & 0xc) << 6 | pt->hsync_pulse_width_lo; | 298 | unsigned hsync_pulse_width = (pt->hsync_vsync_offset_pulse_width_hi & 0x30) << 4 | pt->hsync_pulse_width_lo; |
299 | unsigned vsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0x30) | (pt->vsync_offset_pulse_width_lo & 0xf); | 299 | unsigned vsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0xc) >> 2 | pt->vsync_offset_pulse_width_lo >> 4; |
300 | unsigned vsync_pulse_width = (pt->hsync_vsync_offset_pulse_width_hi & 0xc0) >> 2 | pt->vsync_offset_pulse_width_lo >> 4; | 300 | unsigned vsync_pulse_width = (pt->hsync_vsync_offset_pulse_width_hi & 0x3) << 4 | (pt->vsync_offset_pulse_width_lo & 0xf); |
301 | 301 | ||
302 | /* ignore tiny modes */ | 302 | /* ignore tiny modes */ |
303 | if (hactive < 64 || vactive < 64) | 303 | if (hactive < 64 || vactive < 64) |
304 | return NULL; | 304 | return NULL; |
305 | 305 | ||
306 | if (pt->misc & DRM_EDID_PT_STEREO) { | 306 | if (pt->misc & DRM_EDID_PT_STEREO) { |
307 | printk(KERN_WARNING "stereo mode not supported\n"); | 307 | printk(KERN_WARNING "stereo mode not supported\n"); |
308 | return NULL; | 308 | return NULL; |
309 | } | 309 | } |
310 | if (!(pt->misc & DRM_EDID_PT_SEPARATE_SYNC)) { | 310 | if (!(pt->misc & DRM_EDID_PT_SEPARATE_SYNC)) { |
311 | printk(KERN_WARNING "integrated sync not supported\n"); | 311 | printk(KERN_WARNING "integrated sync not supported\n"); |
312 | return NULL; | 312 | return NULL; |
313 | } | 313 | } |
314 | 314 | ||
315 | mode = drm_mode_create(dev); | 315 | mode = drm_mode_create(dev); |
316 | if (!mode) | 316 | if (!mode) |
317 | return NULL; | 317 | return NULL; |
318 | 318 | ||
319 | mode->type = DRM_MODE_TYPE_DRIVER; | 319 | mode->type = DRM_MODE_TYPE_DRIVER; |
320 | 320 | ||
321 | if (quirks & EDID_QUIRK_135_CLOCK_TOO_HIGH) | 321 | if (quirks & EDID_QUIRK_135_CLOCK_TOO_HIGH) |
322 | timing->pixel_clock = cpu_to_le16(1088); | 322 | timing->pixel_clock = cpu_to_le16(1088); |
323 | 323 | ||
324 | mode->clock = le16_to_cpu(timing->pixel_clock) * 10; | 324 | mode->clock = le16_to_cpu(timing->pixel_clock) * 10; |
325 | 325 | ||
326 | mode->hdisplay = hactive; | 326 | mode->hdisplay = hactive; |
327 | mode->hsync_start = mode->hdisplay + hsync_offset; | 327 | mode->hsync_start = mode->hdisplay + hsync_offset; |
328 | mode->hsync_end = mode->hsync_start + hsync_pulse_width; | 328 | mode->hsync_end = mode->hsync_start + hsync_pulse_width; |
329 | mode->htotal = mode->hdisplay + hblank; | 329 | mode->htotal = mode->hdisplay + hblank; |
330 | 330 | ||
331 | mode->vdisplay = vactive; | 331 | mode->vdisplay = vactive; |
332 | mode->vsync_start = mode->vdisplay + vsync_offset; | 332 | mode->vsync_start = mode->vdisplay + vsync_offset; |
333 | mode->vsync_end = mode->vsync_start + vsync_pulse_width; | 333 | mode->vsync_end = mode->vsync_start + vsync_pulse_width; |
334 | mode->vtotal = mode->vdisplay + vblank; | 334 | mode->vtotal = mode->vdisplay + vblank; |
335 | 335 | ||
336 | drm_mode_set_name(mode); | 336 | drm_mode_set_name(mode); |
337 | 337 | ||
338 | if (pt->misc & DRM_EDID_PT_INTERLACED) | 338 | if (pt->misc & DRM_EDID_PT_INTERLACED) |
339 | mode->flags |= DRM_MODE_FLAG_INTERLACE; | 339 | mode->flags |= DRM_MODE_FLAG_INTERLACE; |
340 | 340 | ||
341 | if (quirks & EDID_QUIRK_DETAILED_SYNC_PP) { | 341 | if (quirks & EDID_QUIRK_DETAILED_SYNC_PP) { |
342 | pt->misc |= DRM_EDID_PT_HSYNC_POSITIVE | DRM_EDID_PT_VSYNC_POSITIVE; | 342 | pt->misc |= DRM_EDID_PT_HSYNC_POSITIVE | DRM_EDID_PT_VSYNC_POSITIVE; |
343 | } | 343 | } |
344 | 344 | ||
345 | mode->flags |= (pt->misc & DRM_EDID_PT_HSYNC_POSITIVE) ? | 345 | mode->flags |= (pt->misc & DRM_EDID_PT_HSYNC_POSITIVE) ? |
346 | DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC; | 346 | DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC; |
347 | mode->flags |= (pt->misc & DRM_EDID_PT_VSYNC_POSITIVE) ? | 347 | mode->flags |= (pt->misc & DRM_EDID_PT_VSYNC_POSITIVE) ? |
348 | DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC; | 348 | DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC; |
349 | 349 | ||
350 | mode->width_mm = pt->width_mm_lo | (pt->width_height_mm_hi & 0xf) << 8; | 350 | mode->width_mm = pt->width_mm_lo | (pt->width_height_mm_hi & 0xf0) << 4; |
351 | mode->height_mm = pt->height_mm_lo | (pt->width_height_mm_hi & 0xf0) << 4; | 351 | mode->height_mm = pt->height_mm_lo | (pt->width_height_mm_hi & 0xf) << 8; |
352 | 352 | ||
353 | if (quirks & EDID_QUIRK_DETAILED_IN_CM) { | 353 | if (quirks & EDID_QUIRK_DETAILED_IN_CM) { |
354 | mode->width_mm *= 10; | 354 | mode->width_mm *= 10; |
355 | mode->height_mm *= 10; | 355 | mode->height_mm *= 10; |
356 | } | 356 | } |
357 | 357 | ||
358 | if (quirks & EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE) { | 358 | if (quirks & EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE) { |
359 | mode->width_mm = edid->width_cm * 10; | 359 | mode->width_mm = edid->width_cm * 10; |
360 | mode->height_mm = edid->height_cm * 10; | 360 | mode->height_mm = edid->height_cm * 10; |
361 | } | 361 | } |
362 | 362 | ||
363 | return mode; | 363 | return mode; |
364 | } | 364 | } |
365 | 365 | ||
366 | /* | 366 | /* |
367 | * Detailed mode info for the EDID "established modes" data to use. | 367 | * Detailed mode info for the EDID "established modes" data to use. |
368 | */ | 368 | */ |
369 | static struct drm_display_mode edid_est_modes[] = { | 369 | static struct drm_display_mode edid_est_modes[] = { |
370 | { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840, | 370 | { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840, |
371 | 968, 1056, 0, 600, 601, 605, 628, 0, | 371 | 968, 1056, 0, 600, 601, 605, 628, 0, |
372 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@60Hz */ | 372 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@60Hz */ |
373 | { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824, | 373 | { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 36000, 800, 824, |
374 | 896, 1024, 0, 600, 601, 603, 625, 0, | 374 | 896, 1024, 0, 600, 601, 603, 625, 0, |
375 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@56Hz */ | 375 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@56Hz */ |
376 | { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656, | 376 | { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 656, |
377 | 720, 840, 0, 480, 481, 484, 500, 0, | 377 | 720, 840, 0, 480, 481, 484, 500, 0, |
378 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@75Hz */ | 378 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@75Hz */ |
379 | { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664, | 379 | { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 31500, 640, 664, |
380 | 704, 832, 0, 480, 489, 491, 520, 0, | 380 | 704, 832, 0, 480, 489, 491, 520, 0, |
381 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@72Hz */ | 381 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@72Hz */ |
382 | { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 30240, 640, 704, | 382 | { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 30240, 640, 704, |
383 | 768, 864, 0, 480, 483, 486, 525, 0, | 383 | 768, 864, 0, 480, 483, 486, 525, 0, |
384 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@67Hz */ | 384 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@67Hz */ |
385 | { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25200, 640, 656, | 385 | { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25200, 640, 656, |
386 | 752, 800, 0, 480, 490, 492, 525, 0, | 386 | 752, 800, 0, 480, 490, 492, 525, 0, |
387 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@60Hz */ | 387 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 640x480@60Hz */ |
388 | { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 738, | 388 | { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 35500, 720, 738, |
389 | 846, 900, 0, 400, 421, 423, 449, 0, | 389 | 846, 900, 0, 400, 421, 423, 449, 0, |
390 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 720x400@88Hz */ | 390 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 720x400@88Hz */ |
391 | { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 28320, 720, 738, | 391 | { DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 28320, 720, 738, |
392 | 846, 900, 0, 400, 412, 414, 449, 0, | 392 | 846, 900, 0, 400, 412, 414, 449, 0, |
393 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 720x400@70Hz */ | 393 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 720x400@70Hz */ |
394 | { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296, | 394 | { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 135000, 1280, 1296, |
395 | 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, | 395 | 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, |
396 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1280x1024@75Hz */ | 396 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1280x1024@75Hz */ |
397 | { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78800, 1024, 1040, | 397 | { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 78800, 1024, 1040, |
398 | 1136, 1312, 0, 768, 769, 772, 800, 0, | 398 | 1136, 1312, 0, 768, 769, 772, 800, 0, |
399 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1024x768@75Hz */ | 399 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1024x768@75Hz */ |
400 | { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048, | 400 | { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 75000, 1024, 1048, |
401 | 1184, 1328, 0, 768, 771, 777, 806, 0, | 401 | 1184, 1328, 0, 768, 771, 777, 806, 0, |
402 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@70Hz */ | 402 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@70Hz */ |
403 | { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, | 403 | { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, |
404 | 1184, 1344, 0, 768, 771, 777, 806, 0, | 404 | 1184, 1344, 0, 768, 771, 777, 806, 0, |
405 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@60Hz */ | 405 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@60Hz */ |
406 | { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER,44900, 1024, 1032, | 406 | { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER,44900, 1024, 1032, |
407 | 1208, 1264, 0, 768, 768, 776, 817, 0, | 407 | 1208, 1264, 0, 768, 768, 776, 817, 0, |
408 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_INTERLACE) }, /* 1024x768@43Hz */ | 408 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_INTERLACE) }, /* 1024x768@43Hz */ |
409 | { DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 57284, 832, 864, | 409 | { DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 57284, 832, 864, |
410 | 928, 1152, 0, 624, 625, 628, 667, 0, | 410 | 928, 1152, 0, 624, 625, 628, 667, 0, |
411 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 832x624@75Hz */ | 411 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 832x624@75Hz */ |
412 | { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816, | 412 | { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 49500, 800, 816, |
413 | 896, 1056, 0, 600, 601, 604, 625, 0, | 413 | 896, 1056, 0, 600, 601, 604, 625, 0, |
414 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@75Hz */ | 414 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@75Hz */ |
415 | { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856, | 415 | { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 50000, 800, 856, |
416 | 976, 1040, 0, 600, 637, 643, 666, 0, | 416 | 976, 1040, 0, 600, 637, 643, 666, 0, |
417 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@72Hz */ | 417 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 800x600@72Hz */ |
418 | { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216, | 418 | { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216, |
419 | 1344, 1600, 0, 864, 865, 868, 900, 0, | 419 | 1344, 1600, 0, 864, 865, 868, 900, 0, |
420 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1152x864@75Hz */ | 420 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1152x864@75Hz */ |
421 | }; | 421 | }; |
422 | 422 | ||
423 | #define EDID_EST_TIMINGS 16 | 423 | #define EDID_EST_TIMINGS 16 |
424 | #define EDID_STD_TIMINGS 8 | 424 | #define EDID_STD_TIMINGS 8 |
425 | #define EDID_DETAILED_TIMINGS 4 | 425 | #define EDID_DETAILED_TIMINGS 4 |
426 | 426 | ||
427 | /** | 427 | /** |
428 | * add_established_modes - get est. modes from EDID and add them | 428 | * add_established_modes - get est. modes from EDID and add them |
429 | * @edid: EDID block to scan | 429 | * @edid: EDID block to scan |
430 | * | 430 | * |
431 | * Each EDID block contains a bitmap of the supported "established modes" list | 431 | * Each EDID block contains a bitmap of the supported "established modes" list |
432 | * (defined above). Tease them out and add them to the global modes list. | 432 | * (defined above). Tease them out and add them to the global modes list. |
433 | */ | 433 | */ |
434 | static int add_established_modes(struct drm_connector *connector, struct edid *edid) | 434 | static int add_established_modes(struct drm_connector *connector, struct edid *edid) |
435 | { | 435 | { |
436 | struct drm_device *dev = connector->dev; | 436 | struct drm_device *dev = connector->dev; |
437 | unsigned long est_bits = edid->established_timings.t1 | | 437 | unsigned long est_bits = edid->established_timings.t1 | |
438 | (edid->established_timings.t2 << 8) | | 438 | (edid->established_timings.t2 << 8) | |
439 | ((edid->established_timings.mfg_rsvd & 0x80) << 9); | 439 | ((edid->established_timings.mfg_rsvd & 0x80) << 9); |
440 | int i, modes = 0; | 440 | int i, modes = 0; |
441 | 441 | ||
442 | for (i = 0; i <= EDID_EST_TIMINGS; i++) | 442 | for (i = 0; i <= EDID_EST_TIMINGS; i++) |
443 | if (est_bits & (1<<i)) { | 443 | if (est_bits & (1<<i)) { |
444 | struct drm_display_mode *newmode; | 444 | struct drm_display_mode *newmode; |
445 | newmode = drm_mode_duplicate(dev, &edid_est_modes[i]); | 445 | newmode = drm_mode_duplicate(dev, &edid_est_modes[i]); |
446 | if (newmode) { | 446 | if (newmode) { |
447 | drm_mode_probed_add(connector, newmode); | 447 | drm_mode_probed_add(connector, newmode); |
448 | modes++; | 448 | modes++; |
449 | } | 449 | } |
450 | } | 450 | } |
451 | 451 | ||
452 | return modes; | 452 | return modes; |
453 | } | 453 | } |
454 | 454 | ||
455 | /** | 455 | /** |
456 | * add_standard_modes - get std. modes from EDID and add them | 456 | * add_standard_modes - get std. modes from EDID and add them |
457 | * @edid: EDID block to scan | 457 | * @edid: EDID block to scan |
458 | * | 458 | * |
459 | * Standard modes can be calculated using the CVT standard. Grab them from | 459 | * Standard modes can be calculated using the CVT standard. Grab them from |
460 | * @edid, calculate them, and add them to the list. | 460 | * @edid, calculate them, and add them to the list. |
461 | */ | 461 | */ |
462 | static int add_standard_modes(struct drm_connector *connector, struct edid *edid) | 462 | static int add_standard_modes(struct drm_connector *connector, struct edid *edid) |
463 | { | 463 | { |
464 | struct drm_device *dev = connector->dev; | 464 | struct drm_device *dev = connector->dev; |
465 | int i, modes = 0; | 465 | int i, modes = 0; |
466 | 466 | ||
467 | for (i = 0; i < EDID_STD_TIMINGS; i++) { | 467 | for (i = 0; i < EDID_STD_TIMINGS; i++) { |
468 | struct std_timing *t = &edid->standard_timings[i]; | 468 | struct std_timing *t = &edid->standard_timings[i]; |
469 | struct drm_display_mode *newmode; | 469 | struct drm_display_mode *newmode; |
470 | 470 | ||
471 | /* If std timings bytes are 1, 1 it's empty */ | 471 | /* If std timings bytes are 1, 1 it's empty */ |
472 | if (t->hsize == 1 && t->vfreq_aspect == 1) | 472 | if (t->hsize == 1 && t->vfreq_aspect == 1) |
473 | continue; | 473 | continue; |
474 | 474 | ||
475 | newmode = drm_mode_std(dev, &edid->standard_timings[i]); | 475 | newmode = drm_mode_std(dev, &edid->standard_timings[i]); |
476 | if (newmode) { | 476 | if (newmode) { |
477 | drm_mode_probed_add(connector, newmode); | 477 | drm_mode_probed_add(connector, newmode); |
478 | modes++; | 478 | modes++; |
479 | } | 479 | } |
480 | } | 480 | } |
481 | 481 | ||
482 | return modes; | 482 | return modes; |
483 | } | 483 | } |
484 | 484 | ||
485 | /** | 485 | /** |
486 | * add_detailed_modes - get detailed mode info from EDID data | 486 | * add_detailed_modes - get detailed mode info from EDID data |
487 | * @connector: attached connector | 487 | * @connector: attached connector |
488 | * @edid: EDID block to scan | 488 | * @edid: EDID block to scan |
489 | * @quirks: quirks to apply | 489 | * @quirks: quirks to apply |
490 | * | 490 | * |
491 | * Some of the detailed timing sections may contain mode information. Grab | 491 | * Some of the detailed timing sections may contain mode information. Grab |
492 | * it and add it to the list. | 492 | * it and add it to the list. |
493 | */ | 493 | */ |
494 | static int add_detailed_info(struct drm_connector *connector, | 494 | static int add_detailed_info(struct drm_connector *connector, |
495 | struct edid *edid, u32 quirks) | 495 | struct edid *edid, u32 quirks) |
496 | { | 496 | { |
497 | struct drm_device *dev = connector->dev; | 497 | struct drm_device *dev = connector->dev; |
498 | int i, j, modes = 0; | 498 | int i, j, modes = 0; |
499 | 499 | ||
500 | for (i = 0; i < EDID_DETAILED_TIMINGS; i++) { | 500 | for (i = 0; i < EDID_DETAILED_TIMINGS; i++) { |
501 | struct detailed_timing *timing = &edid->detailed_timings[i]; | 501 | struct detailed_timing *timing = &edid->detailed_timings[i]; |
502 | struct detailed_non_pixel *data = &timing->data.other_data; | 502 | struct detailed_non_pixel *data = &timing->data.other_data; |
503 | struct drm_display_mode *newmode; | 503 | struct drm_display_mode *newmode; |
504 | 504 | ||
505 | /* EDID up to and including 1.2 may put monitor info here */ | 505 | /* EDID up to and including 1.2 may put monitor info here */ |
506 | if (edid->version == 1 && edid->revision < 3) | 506 | if (edid->version == 1 && edid->revision < 3) |
507 | continue; | 507 | continue; |
508 | 508 | ||
509 | /* Detailed mode timing */ | 509 | /* Detailed mode timing */ |
510 | if (timing->pixel_clock) { | 510 | if (timing->pixel_clock) { |
511 | newmode = drm_mode_detailed(dev, edid, timing, quirks); | 511 | newmode = drm_mode_detailed(dev, edid, timing, quirks); |
512 | if (!newmode) | 512 | if (!newmode) |
513 | continue; | 513 | continue; |
514 | 514 | ||
515 | /* First detailed mode is preferred */ | 515 | /* First detailed mode is preferred */ |
516 | if (i == 0 && (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING)) | 516 | if (i == 0 && (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING)) |
517 | newmode->type |= DRM_MODE_TYPE_PREFERRED; | 517 | newmode->type |= DRM_MODE_TYPE_PREFERRED; |
518 | drm_mode_probed_add(connector, newmode); | 518 | drm_mode_probed_add(connector, newmode); |
519 | 519 | ||
520 | modes++; | 520 | modes++; |
521 | continue; | 521 | continue; |
522 | } | 522 | } |
523 | 523 | ||
524 | /* Other timing or info */ | 524 | /* Other timing or info */ |
525 | switch (data->type) { | 525 | switch (data->type) { |
526 | case EDID_DETAIL_MONITOR_SERIAL: | 526 | case EDID_DETAIL_MONITOR_SERIAL: |
527 | break; | 527 | break; |
528 | case EDID_DETAIL_MONITOR_STRING: | 528 | case EDID_DETAIL_MONITOR_STRING: |
529 | break; | 529 | break; |
530 | case EDID_DETAIL_MONITOR_RANGE: | 530 | case EDID_DETAIL_MONITOR_RANGE: |
531 | /* Get monitor range data */ | 531 | /* Get monitor range data */ |
532 | break; | 532 | break; |
533 | case EDID_DETAIL_MONITOR_NAME: | 533 | case EDID_DETAIL_MONITOR_NAME: |
534 | break; | 534 | break; |
535 | case EDID_DETAIL_MONITOR_CPDATA: | 535 | case EDID_DETAIL_MONITOR_CPDATA: |
536 | break; | 536 | break; |
537 | case EDID_DETAIL_STD_MODES: | 537 | case EDID_DETAIL_STD_MODES: |
538 | /* Five modes per detailed section */ | 538 | /* Five modes per detailed section */ |
539 | for (j = 0; j < 5; i++) { | 539 | for (j = 0; j < 5; i++) { |
540 | struct std_timing *std; | 540 | struct std_timing *std; |
541 | struct drm_display_mode *newmode; | 541 | struct drm_display_mode *newmode; |
542 | 542 | ||
543 | std = &data->data.timings[j]; | 543 | std = &data->data.timings[j]; |
544 | newmode = drm_mode_std(dev, std); | 544 | newmode = drm_mode_std(dev, std); |
545 | if (newmode) { | 545 | if (newmode) { |
546 | drm_mode_probed_add(connector, newmode); | 546 | drm_mode_probed_add(connector, newmode); |
547 | modes++; | 547 | modes++; |
548 | } | 548 | } |
549 | } | 549 | } |
550 | break; | 550 | break; |
551 | default: | 551 | default: |
552 | break; | 552 | break; |
553 | } | 553 | } |
554 | } | 554 | } |
555 | 555 | ||
556 | return modes; | 556 | return modes; |
557 | } | 557 | } |
558 | 558 | ||
559 | #define DDC_ADDR 0x50 | 559 | #define DDC_ADDR 0x50 |
560 | /** | 560 | /** |
561 | * Get EDID information via I2C. | 561 | * Get EDID information via I2C. |
562 | * | 562 | * |
563 | * \param adapter : i2c device adaptor | 563 | * \param adapter : i2c device adaptor |
564 | * \param buf : EDID data buffer to be filled | 564 | * \param buf : EDID data buffer to be filled |
565 | * \param len : EDID data buffer length | 565 | * \param len : EDID data buffer length |
566 | * \return 0 on success or -1 on failure. | 566 | * \return 0 on success or -1 on failure. |
567 | * | 567 | * |
568 | * Try to fetch EDID information by calling i2c driver function. | 568 | * Try to fetch EDID information by calling i2c driver function. |
569 | */ | 569 | */ |
570 | int drm_do_probe_ddc_edid(struct i2c_adapter *adapter, | 570 | int drm_do_probe_ddc_edid(struct i2c_adapter *adapter, |
571 | unsigned char *buf, int len) | 571 | unsigned char *buf, int len) |
572 | { | 572 | { |
573 | unsigned char start = 0x0; | 573 | unsigned char start = 0x0; |
574 | struct i2c_msg msgs[] = { | 574 | struct i2c_msg msgs[] = { |
575 | { | 575 | { |
576 | .addr = DDC_ADDR, | 576 | .addr = DDC_ADDR, |
577 | .flags = 0, | 577 | .flags = 0, |
578 | .len = 1, | 578 | .len = 1, |
579 | .buf = &start, | 579 | .buf = &start, |
580 | }, { | 580 | }, { |
581 | .addr = DDC_ADDR, | 581 | .addr = DDC_ADDR, |
582 | .flags = I2C_M_RD, | 582 | .flags = I2C_M_RD, |
583 | .len = len, | 583 | .len = len, |
584 | .buf = buf, | 584 | .buf = buf, |
585 | } | 585 | } |
586 | }; | 586 | }; |
587 | 587 | ||
588 | if (i2c_transfer(adapter, msgs, 2) == 2) | 588 | if (i2c_transfer(adapter, msgs, 2) == 2) |
589 | return 0; | 589 | return 0; |
590 | 590 | ||
591 | dev_info(&adapter->dev, "unable to read EDID block.\n"); | 591 | dev_info(&adapter->dev, "unable to read EDID block.\n"); |
592 | return -1; | 592 | return -1; |
593 | } | 593 | } |
594 | EXPORT_SYMBOL(drm_do_probe_ddc_edid); | 594 | EXPORT_SYMBOL(drm_do_probe_ddc_edid); |
595 | 595 | ||
596 | static int drm_ddc_read_edid(struct drm_connector *connector, | 596 | static int drm_ddc_read_edid(struct drm_connector *connector, |
597 | struct i2c_adapter *adapter, | 597 | struct i2c_adapter *adapter, |
598 | char *buf, int len) | 598 | char *buf, int len) |
599 | { | 599 | { |
600 | int ret; | 600 | int ret; |
601 | 601 | ||
602 | ret = drm_do_probe_ddc_edid(adapter, buf, len); | 602 | ret = drm_do_probe_ddc_edid(adapter, buf, len); |
603 | if (ret != 0) { | 603 | if (ret != 0) { |
604 | dev_info(&connector->dev->pdev->dev, "%s: no EDID data\n", | 604 | dev_info(&connector->dev->pdev->dev, "%s: no EDID data\n", |
605 | drm_get_connector_name(connector)); | 605 | drm_get_connector_name(connector)); |
606 | goto end; | 606 | goto end; |
607 | } | 607 | } |
608 | if (!edid_is_valid((struct edid *)buf)) { | 608 | if (!edid_is_valid((struct edid *)buf)) { |
609 | dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n", | 609 | dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n", |
610 | drm_get_connector_name(connector)); | 610 | drm_get_connector_name(connector)); |
611 | ret = -1; | 611 | ret = -1; |
612 | } | 612 | } |
613 | end: | 613 | end: |
614 | return ret; | 614 | return ret; |
615 | } | 615 | } |
616 | 616 | ||
617 | #define MAX_EDID_EXT_NUM 4 | 617 | #define MAX_EDID_EXT_NUM 4 |
618 | /** | 618 | /** |
619 | * drm_get_edid - get EDID data, if available | 619 | * drm_get_edid - get EDID data, if available |
620 | * @connector: connector we're probing | 620 | * @connector: connector we're probing |
621 | * @adapter: i2c adapter to use for DDC | 621 | * @adapter: i2c adapter to use for DDC |
622 | * | 622 | * |
623 | * Poke the given connector's i2c channel to grab EDID data if possible. | 623 | * Poke the given connector's i2c channel to grab EDID data if possible. |
624 | * | 624 | * |
625 | * Return edid data or NULL if we couldn't find any. | 625 | * Return edid data or NULL if we couldn't find any. |
626 | */ | 626 | */ |
627 | struct edid *drm_get_edid(struct drm_connector *connector, | 627 | struct edid *drm_get_edid(struct drm_connector *connector, |
628 | struct i2c_adapter *adapter) | 628 | struct i2c_adapter *adapter) |
629 | { | 629 | { |
630 | int ret; | 630 | int ret; |
631 | struct edid *edid; | 631 | struct edid *edid; |
632 | 632 | ||
633 | edid = kmalloc(EDID_LENGTH * (MAX_EDID_EXT_NUM + 1), | 633 | edid = kmalloc(EDID_LENGTH * (MAX_EDID_EXT_NUM + 1), |
634 | GFP_KERNEL); | 634 | GFP_KERNEL); |
635 | if (edid == NULL) { | 635 | if (edid == NULL) { |
636 | dev_warn(&connector->dev->pdev->dev, | 636 | dev_warn(&connector->dev->pdev->dev, |
637 | "Failed to allocate EDID\n"); | 637 | "Failed to allocate EDID\n"); |
638 | goto end; | 638 | goto end; |
639 | } | 639 | } |
640 | 640 | ||
641 | /* Read first EDID block */ | 641 | /* Read first EDID block */ |
642 | ret = drm_ddc_read_edid(connector, adapter, | 642 | ret = drm_ddc_read_edid(connector, adapter, |
643 | (unsigned char *)edid, EDID_LENGTH); | 643 | (unsigned char *)edid, EDID_LENGTH); |
644 | if (ret != 0) | 644 | if (ret != 0) |
645 | goto clean_up; | 645 | goto clean_up; |
646 | 646 | ||
647 | /* There are EDID extensions to be read */ | 647 | /* There are EDID extensions to be read */ |
648 | if (edid->extensions != 0) { | 648 | if (edid->extensions != 0) { |
649 | int edid_ext_num = edid->extensions; | 649 | int edid_ext_num = edid->extensions; |
650 | 650 | ||
651 | if (edid_ext_num > MAX_EDID_EXT_NUM) { | 651 | if (edid_ext_num > MAX_EDID_EXT_NUM) { |
652 | dev_warn(&connector->dev->pdev->dev, | 652 | dev_warn(&connector->dev->pdev->dev, |
653 | "The number of extension(%d) is " | 653 | "The number of extension(%d) is " |
654 | "over max (%d), actually read number (%d)\n", | 654 | "over max (%d), actually read number (%d)\n", |
655 | edid_ext_num, MAX_EDID_EXT_NUM, | 655 | edid_ext_num, MAX_EDID_EXT_NUM, |
656 | MAX_EDID_EXT_NUM); | 656 | MAX_EDID_EXT_NUM); |
657 | /* Reset EDID extension number to be read */ | 657 | /* Reset EDID extension number to be read */ |
658 | edid_ext_num = MAX_EDID_EXT_NUM; | 658 | edid_ext_num = MAX_EDID_EXT_NUM; |
659 | } | 659 | } |
660 | /* Read EDID including extensions too */ | 660 | /* Read EDID including extensions too */ |
661 | ret = drm_ddc_read_edid(connector, adapter, (char *)edid, | 661 | ret = drm_ddc_read_edid(connector, adapter, (char *)edid, |
662 | EDID_LENGTH * (edid_ext_num + 1)); | 662 | EDID_LENGTH * (edid_ext_num + 1)); |
663 | if (ret != 0) | 663 | if (ret != 0) |
664 | goto clean_up; | 664 | goto clean_up; |
665 | 665 | ||
666 | } | 666 | } |
667 | 667 | ||
668 | connector->display_info.raw_edid = (char *)edid; | 668 | connector->display_info.raw_edid = (char *)edid; |
669 | goto end; | 669 | goto end; |
670 | 670 | ||
671 | clean_up: | 671 | clean_up: |
672 | kfree(edid); | 672 | kfree(edid); |
673 | edid = NULL; | 673 | edid = NULL; |
674 | end: | 674 | end: |
675 | return edid; | 675 | return edid; |
676 | 676 | ||
677 | } | 677 | } |
678 | EXPORT_SYMBOL(drm_get_edid); | 678 | EXPORT_SYMBOL(drm_get_edid); |
679 | 679 | ||
680 | #define HDMI_IDENTIFIER 0x000C03 | 680 | #define HDMI_IDENTIFIER 0x000C03 |
681 | #define VENDOR_BLOCK 0x03 | 681 | #define VENDOR_BLOCK 0x03 |
682 | /** | 682 | /** |
683 | * drm_detect_hdmi_monitor - detect whether monitor is hdmi. | 683 | * drm_detect_hdmi_monitor - detect whether monitor is hdmi. |
684 | * @edid: monitor EDID information | 684 | * @edid: monitor EDID information |
685 | * | 685 | * |
686 | * Parse the CEA extension according to CEA-861-B. | 686 | * Parse the CEA extension according to CEA-861-B. |
687 | * Return true if HDMI, false if not or unknown. | 687 | * Return true if HDMI, false if not or unknown. |
688 | */ | 688 | */ |
689 | bool drm_detect_hdmi_monitor(struct edid *edid) | 689 | bool drm_detect_hdmi_monitor(struct edid *edid) |
690 | { | 690 | { |
691 | char *edid_ext = NULL; | 691 | char *edid_ext = NULL; |
692 | int i, hdmi_id, edid_ext_num; | 692 | int i, hdmi_id, edid_ext_num; |
693 | int start_offset, end_offset; | 693 | int start_offset, end_offset; |
694 | bool is_hdmi = false; | 694 | bool is_hdmi = false; |
695 | 695 | ||
696 | /* No EDID or EDID extensions */ | 696 | /* No EDID or EDID extensions */ |
697 | if (edid == NULL || edid->extensions == 0) | 697 | if (edid == NULL || edid->extensions == 0) |
698 | goto end; | 698 | goto end; |
699 | 699 | ||
700 | /* Chose real EDID extension number */ | 700 | /* Chose real EDID extension number */ |
701 | edid_ext_num = edid->extensions > MAX_EDID_EXT_NUM ? | 701 | edid_ext_num = edid->extensions > MAX_EDID_EXT_NUM ? |
702 | MAX_EDID_EXT_NUM : edid->extensions; | 702 | MAX_EDID_EXT_NUM : edid->extensions; |
703 | 703 | ||
704 | /* Find CEA extension */ | 704 | /* Find CEA extension */ |
705 | for (i = 0; i < edid_ext_num; i++) { | 705 | for (i = 0; i < edid_ext_num; i++) { |
706 | edid_ext = (char *)edid + EDID_LENGTH * (i + 1); | 706 | edid_ext = (char *)edid + EDID_LENGTH * (i + 1); |
707 | /* This block is CEA extension */ | 707 | /* This block is CEA extension */ |
708 | if (edid_ext[0] == 0x02) | 708 | if (edid_ext[0] == 0x02) |
709 | break; | 709 | break; |
710 | } | 710 | } |
711 | 711 | ||
712 | if (i == edid_ext_num) | 712 | if (i == edid_ext_num) |
713 | goto end; | 713 | goto end; |
714 | 714 | ||
715 | /* Data block offset in CEA extension block */ | 715 | /* Data block offset in CEA extension block */ |
716 | start_offset = 4; | 716 | start_offset = 4; |
717 | end_offset = edid_ext[2]; | 717 | end_offset = edid_ext[2]; |
718 | 718 | ||
719 | /* | 719 | /* |
720 | * Because HDMI identifier is in Vendor Specific Block, | 720 | * Because HDMI identifier is in Vendor Specific Block, |
721 | * search it from all data blocks of CEA extension. | 721 | * search it from all data blocks of CEA extension. |
722 | */ | 722 | */ |
723 | for (i = start_offset; i < end_offset; | 723 | for (i = start_offset; i < end_offset; |
724 | /* Increased by data block len */ | 724 | /* Increased by data block len */ |
725 | i += ((edid_ext[i] & 0x1f) + 1)) { | 725 | i += ((edid_ext[i] & 0x1f) + 1)) { |
726 | /* Find vendor specific block */ | 726 | /* Find vendor specific block */ |
727 | if ((edid_ext[i] >> 5) == VENDOR_BLOCK) { | 727 | if ((edid_ext[i] >> 5) == VENDOR_BLOCK) { |
728 | hdmi_id = edid_ext[i + 1] | (edid_ext[i + 2] << 8) | | 728 | hdmi_id = edid_ext[i + 1] | (edid_ext[i + 2] << 8) | |
729 | edid_ext[i + 3] << 16; | 729 | edid_ext[i + 3] << 16; |
730 | /* Find HDMI identifier */ | 730 | /* Find HDMI identifier */ |
731 | if (hdmi_id == HDMI_IDENTIFIER) | 731 | if (hdmi_id == HDMI_IDENTIFIER) |
732 | is_hdmi = true; | 732 | is_hdmi = true; |
733 | break; | 733 | break; |
734 | } | 734 | } |
735 | } | 735 | } |
736 | 736 | ||
737 | end: | 737 | end: |
738 | return is_hdmi; | 738 | return is_hdmi; |
739 | } | 739 | } |
740 | EXPORT_SYMBOL(drm_detect_hdmi_monitor); | 740 | EXPORT_SYMBOL(drm_detect_hdmi_monitor); |
741 | 741 | ||
742 | /** | 742 | /** |
743 | * drm_add_edid_modes - add modes from EDID data, if available | 743 | * drm_add_edid_modes - add modes from EDID data, if available |
744 | * @connector: connector we're probing | 744 | * @connector: connector we're probing |
745 | * @edid: edid data | 745 | * @edid: edid data |
746 | * | 746 | * |
747 | * Add the specified modes to the connector's mode list. | 747 | * Add the specified modes to the connector's mode list. |
748 | * | 748 | * |
749 | * Return number of modes added or 0 if we couldn't find any. | 749 | * Return number of modes added or 0 if we couldn't find any. |
750 | */ | 750 | */ |
751 | int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) | 751 | int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) |
752 | { | 752 | { |
753 | int num_modes = 0; | 753 | int num_modes = 0; |
754 | u32 quirks; | 754 | u32 quirks; |
755 | 755 | ||
756 | if (edid == NULL) { | 756 | if (edid == NULL) { |
757 | return 0; | 757 | return 0; |
758 | } | 758 | } |
759 | if (!edid_is_valid(edid)) { | 759 | if (!edid_is_valid(edid)) { |
760 | dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n", | 760 | dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n", |
761 | drm_get_connector_name(connector)); | 761 | drm_get_connector_name(connector)); |
762 | return 0; | 762 | return 0; |
763 | } | 763 | } |
764 | 764 | ||
765 | quirks = edid_get_quirks(edid); | 765 | quirks = edid_get_quirks(edid); |
766 | 766 | ||
767 | num_modes += add_established_modes(connector, edid); | 767 | num_modes += add_established_modes(connector, edid); |
768 | num_modes += add_standard_modes(connector, edid); | 768 | num_modes += add_standard_modes(connector, edid); |
769 | num_modes += add_detailed_info(connector, edid, quirks); | 769 | num_modes += add_detailed_info(connector, edid, quirks); |
770 | 770 | ||
771 | if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75)) | 771 | if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75)) |
772 | edid_fixup_preferred(connector, quirks); | 772 | edid_fixup_preferred(connector, quirks); |
773 | 773 | ||
774 | connector->display_info.serration_vsync = (edid->input & DRM_EDID_INPUT_SERRATION_VSYNC) ? 1 : 0; | 774 | connector->display_info.serration_vsync = (edid->input & DRM_EDID_INPUT_SERRATION_VSYNC) ? 1 : 0; |
775 | connector->display_info.sync_on_green = (edid->input & DRM_EDID_INPUT_SYNC_ON_GREEN) ? 1 : 0; | 775 | connector->display_info.sync_on_green = (edid->input & DRM_EDID_INPUT_SYNC_ON_GREEN) ? 1 : 0; |
776 | connector->display_info.composite_sync = (edid->input & DRM_EDID_INPUT_COMPOSITE_SYNC) ? 1 : 0; | 776 | connector->display_info.composite_sync = (edid->input & DRM_EDID_INPUT_COMPOSITE_SYNC) ? 1 : 0; |
777 | connector->display_info.separate_syncs = (edid->input & DRM_EDID_INPUT_SEPARATE_SYNCS) ? 1 : 0; | 777 | connector->display_info.separate_syncs = (edid->input & DRM_EDID_INPUT_SEPARATE_SYNCS) ? 1 : 0; |
778 | connector->display_info.blank_to_black = (edid->input & DRM_EDID_INPUT_BLANK_TO_BLACK) ? 1 : 0; | 778 | connector->display_info.blank_to_black = (edid->input & DRM_EDID_INPUT_BLANK_TO_BLACK) ? 1 : 0; |
779 | connector->display_info.video_level = (edid->input & DRM_EDID_INPUT_VIDEO_LEVEL) >> 5; | 779 | connector->display_info.video_level = (edid->input & DRM_EDID_INPUT_VIDEO_LEVEL) >> 5; |
780 | connector->display_info.digital = (edid->input & DRM_EDID_INPUT_DIGITAL) ? 1 : 0; | 780 | connector->display_info.digital = (edid->input & DRM_EDID_INPUT_DIGITAL) ? 1 : 0; |
781 | connector->display_info.width_mm = edid->width_cm * 10; | 781 | connector->display_info.width_mm = edid->width_cm * 10; |
782 | connector->display_info.height_mm = edid->height_cm * 10; | 782 | connector->display_info.height_mm = edid->height_cm * 10; |
783 | connector->display_info.gamma = edid->gamma; | 783 | connector->display_info.gamma = edid->gamma; |
784 | connector->display_info.gtf_supported = (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF) ? 1 : 0; | 784 | connector->display_info.gtf_supported = (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF) ? 1 : 0; |
785 | connector->display_info.standard_color = (edid->features & DRM_EDID_FEATURE_STANDARD_COLOR) ? 1 : 0; | 785 | connector->display_info.standard_color = (edid->features & DRM_EDID_FEATURE_STANDARD_COLOR) ? 1 : 0; |
786 | connector->display_info.display_type = (edid->features & DRM_EDID_FEATURE_DISPLAY_TYPE) >> 3; | 786 | connector->display_info.display_type = (edid->features & DRM_EDID_FEATURE_DISPLAY_TYPE) >> 3; |
787 | connector->display_info.active_off_supported = (edid->features & DRM_EDID_FEATURE_PM_ACTIVE_OFF) ? 1 : 0; | 787 | connector->display_info.active_off_supported = (edid->features & DRM_EDID_FEATURE_PM_ACTIVE_OFF) ? 1 : 0; |
788 | connector->display_info.suspend_supported = (edid->features & DRM_EDID_FEATURE_PM_SUSPEND) ? 1 : 0; | 788 | connector->display_info.suspend_supported = (edid->features & DRM_EDID_FEATURE_PM_SUSPEND) ? 1 : 0; |
789 | connector->display_info.standby_supported = (edid->features & DRM_EDID_FEATURE_PM_STANDBY) ? 1 : 0; | 789 | connector->display_info.standby_supported = (edid->features & DRM_EDID_FEATURE_PM_STANDBY) ? 1 : 0; |
790 | connector->display_info.gamma = edid->gamma; | 790 | connector->display_info.gamma = edid->gamma; |
791 | 791 | ||
792 | return num_modes; | 792 | return num_modes; |
793 | } | 793 | } |
794 | EXPORT_SYMBOL(drm_add_edid_modes); | 794 | EXPORT_SYMBOL(drm_add_edid_modes); |
795 | 795 |
include/drm/drm_edid.h
1 | /* | 1 | /* |
2 | * Copyright © 2007-2008 Intel Corporation | 2 | * Copyright © 2007-2008 Intel Corporation |
3 | * Jesse Barnes <jesse.barnes@intel.com> | 3 | * Jesse Barnes <jesse.barnes@intel.com> |
4 | * | 4 | * |
5 | * Permission is hereby granted, free of charge, to any person obtaining a | 5 | * Permission is hereby granted, free of charge, to any person obtaining a |
6 | * copy of this software and associated documentation files (the "Software"), | 6 | * copy of this software and associated documentation files (the "Software"), |
7 | * to deal in the Software without restriction, including without limitation | 7 | * to deal in the Software without restriction, including without limitation |
8 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | 8 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
9 | * and/or sell copies of the Software, and to permit persons to whom the | 9 | * and/or sell copies of the Software, and to permit persons to whom the |
10 | * Software is furnished to do so, subject to the following conditions: | 10 | * Software is furnished to do so, subject to the following conditions: |
11 | * | 11 | * |
12 | * The above copyright notice and this permission notice shall be included in | 12 | * The above copyright notice and this permission notice shall be included in |
13 | * all copies or substantial portions of the Software. | 13 | * all copies or substantial portions of the Software. |
14 | * | 14 | * |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
18 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | 18 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR |
19 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | 19 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
20 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | 20 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
21 | * OTHER DEALINGS IN THE SOFTWARE. | 21 | * OTHER DEALINGS IN THE SOFTWARE. |
22 | */ | 22 | */ |
23 | #ifndef __DRM_EDID_H__ | 23 | #ifndef __DRM_EDID_H__ |
24 | #define __DRM_EDID_H__ | 24 | #define __DRM_EDID_H__ |
25 | 25 | ||
26 | #include <linux/types.h> | 26 | #include <linux/types.h> |
27 | 27 | ||
28 | #define EDID_LENGTH 128 | 28 | #define EDID_LENGTH 128 |
29 | #define DDC_ADDR 0x50 | 29 | #define DDC_ADDR 0x50 |
30 | 30 | ||
31 | struct est_timings { | 31 | struct est_timings { |
32 | u8 t1; | 32 | u8 t1; |
33 | u8 t2; | 33 | u8 t2; |
34 | u8 mfg_rsvd; | 34 | u8 mfg_rsvd; |
35 | } __attribute__((packed)); | 35 | } __attribute__((packed)); |
36 | 36 | ||
37 | /* 00=16:10, 01=4:3, 10=5:4, 11=16:9 */ | 37 | /* 00=16:10, 01=4:3, 10=5:4, 11=16:9 */ |
38 | #define EDID_TIMING_ASPECT_SHIFT 0 | 38 | #define EDID_TIMING_ASPECT_SHIFT 6 |
39 | #define EDID_TIMING_ASPECT_MASK (0x3 << EDID_TIMING_ASPECT_SHIFT) | 39 | #define EDID_TIMING_ASPECT_MASK (0x3 << EDID_TIMING_ASPECT_SHIFT) |
40 | 40 | ||
41 | /* need to add 60 */ | 41 | /* need to add 60 */ |
42 | #define EDID_TIMING_VFREQ_SHIFT 2 | 42 | #define EDID_TIMING_VFREQ_SHIFT 0 |
43 | #define EDID_TIMING_VFREQ_MASK (0x3f << EDID_TIMING_VFREQ_SHIFT) | 43 | #define EDID_TIMING_VFREQ_MASK (0x3f << EDID_TIMING_VFREQ_SHIFT) |
44 | 44 | ||
45 | struct std_timing { | 45 | struct std_timing { |
46 | u8 hsize; /* need to multiply by 8 then add 248 */ | 46 | u8 hsize; /* need to multiply by 8 then add 248 */ |
47 | u8 vfreq_aspect; | 47 | u8 vfreq_aspect; |
48 | } __attribute__((packed)); | 48 | } __attribute__((packed)); |
49 | 49 | ||
50 | #define DRM_EDID_PT_HSYNC_POSITIVE (1 << 6) | 50 | #define DRM_EDID_PT_HSYNC_POSITIVE (1 << 1) |
51 | #define DRM_EDID_PT_VSYNC_POSITIVE (1 << 5) | 51 | #define DRM_EDID_PT_VSYNC_POSITIVE (1 << 2) |
52 | #define DRM_EDID_PT_SEPARATE_SYNC (3 << 3) | 52 | #define DRM_EDID_PT_SEPARATE_SYNC (3 << 3) |
53 | #define DRM_EDID_PT_STEREO (1 << 2) | 53 | #define DRM_EDID_PT_STEREO (1 << 5) |
54 | #define DRM_EDID_PT_INTERLACED (1 << 1) | 54 | #define DRM_EDID_PT_INTERLACED (1 << 7) |
55 | 55 | ||
56 | /* If detailed data is pixel timing */ | 56 | /* If detailed data is pixel timing */ |
57 | struct detailed_pixel_timing { | 57 | struct detailed_pixel_timing { |
58 | u8 hactive_lo; | 58 | u8 hactive_lo; |
59 | u8 hblank_lo; | 59 | u8 hblank_lo; |
60 | u8 hactive_hblank_hi; | 60 | u8 hactive_hblank_hi; |
61 | u8 vactive_lo; | 61 | u8 vactive_lo; |
62 | u8 vblank_lo; | 62 | u8 vblank_lo; |
63 | u8 vactive_vblank_hi; | 63 | u8 vactive_vblank_hi; |
64 | u8 hsync_offset_lo; | 64 | u8 hsync_offset_lo; |
65 | u8 hsync_pulse_width_lo; | 65 | u8 hsync_pulse_width_lo; |
66 | u8 vsync_offset_pulse_width_lo; | 66 | u8 vsync_offset_pulse_width_lo; |
67 | u8 hsync_vsync_offset_pulse_width_hi; | 67 | u8 hsync_vsync_offset_pulse_width_hi; |
68 | u8 width_mm_lo; | 68 | u8 width_mm_lo; |
69 | u8 height_mm_lo; | 69 | u8 height_mm_lo; |
70 | u8 width_height_mm_hi; | 70 | u8 width_height_mm_hi; |
71 | u8 hborder; | 71 | u8 hborder; |
72 | u8 vborder; | 72 | u8 vborder; |
73 | u8 misc; | 73 | u8 misc; |
74 | } __attribute__((packed)); | 74 | } __attribute__((packed)); |
75 | 75 | ||
76 | /* If it's not pixel timing, it'll be one of the below */ | 76 | /* If it's not pixel timing, it'll be one of the below */ |
77 | struct detailed_data_string { | 77 | struct detailed_data_string { |
78 | u8 str[13]; | 78 | u8 str[13]; |
79 | } __attribute__((packed)); | 79 | } __attribute__((packed)); |
80 | 80 | ||
81 | struct detailed_data_monitor_range { | 81 | struct detailed_data_monitor_range { |
82 | u8 min_vfreq; | 82 | u8 min_vfreq; |
83 | u8 max_vfreq; | 83 | u8 max_vfreq; |
84 | u8 min_hfreq_khz; | 84 | u8 min_hfreq_khz; |
85 | u8 max_hfreq_khz; | 85 | u8 max_hfreq_khz; |
86 | u8 pixel_clock_mhz; /* need to multiply by 10 */ | 86 | u8 pixel_clock_mhz; /* need to multiply by 10 */ |
87 | __le16 sec_gtf_toggle; /* A000=use above, 20=use below */ | 87 | __le16 sec_gtf_toggle; /* A000=use above, 20=use below */ |
88 | u8 hfreq_start_khz; /* need to multiply by 2 */ | 88 | u8 hfreq_start_khz; /* need to multiply by 2 */ |
89 | u8 c; /* need to divide by 2 */ | 89 | u8 c; /* need to divide by 2 */ |
90 | __le16 m; | 90 | __le16 m; |
91 | u8 k; | 91 | u8 k; |
92 | u8 j; /* need to divide by 2 */ | 92 | u8 j; /* need to divide by 2 */ |
93 | } __attribute__((packed)); | 93 | } __attribute__((packed)); |
94 | 94 | ||
95 | struct detailed_data_wpindex { | 95 | struct detailed_data_wpindex { |
96 | u8 white_xy_lo; /* Upper 2 bits each */ | 96 | u8 white_yx_lo; /* Lower 2 bits each */ |
97 | u8 white_x_hi; | 97 | u8 white_x_hi; |
98 | u8 white_y_hi; | 98 | u8 white_y_hi; |
99 | u8 gamma; /* need to divide by 100 then add 1 */ | 99 | u8 gamma; /* need to divide by 100 then add 1 */ |
100 | } __attribute__((packed)); | 100 | } __attribute__((packed)); |
101 | 101 | ||
102 | struct detailed_data_color_point { | 102 | struct detailed_data_color_point { |
103 | u8 windex1; | 103 | u8 windex1; |
104 | u8 wpindex1[3]; | 104 | u8 wpindex1[3]; |
105 | u8 windex2; | 105 | u8 windex2; |
106 | u8 wpindex2[3]; | 106 | u8 wpindex2[3]; |
107 | } __attribute__((packed)); | 107 | } __attribute__((packed)); |
108 | 108 | ||
109 | struct detailed_non_pixel { | 109 | struct detailed_non_pixel { |
110 | u8 pad1; | 110 | u8 pad1; |
111 | u8 type; /* ff=serial, fe=string, fd=monitor range, fc=monitor name | 111 | u8 type; /* ff=serial, fe=string, fd=monitor range, fc=monitor name |
112 | fb=color point data, fa=standard timing data, | 112 | fb=color point data, fa=standard timing data, |
113 | f9=undefined, f8=mfg. reserved */ | 113 | f9=undefined, f8=mfg. reserved */ |
114 | u8 pad2; | 114 | u8 pad2; |
115 | union { | 115 | union { |
116 | struct detailed_data_string str; | 116 | struct detailed_data_string str; |
117 | struct detailed_data_monitor_range range; | 117 | struct detailed_data_monitor_range range; |
118 | struct detailed_data_wpindex color; | 118 | struct detailed_data_wpindex color; |
119 | struct std_timing timings[5]; | 119 | struct std_timing timings[5]; |
120 | } data; | 120 | } data; |
121 | } __attribute__((packed)); | 121 | } __attribute__((packed)); |
122 | 122 | ||
123 | #define EDID_DETAIL_STD_MODES 0xfa | 123 | #define EDID_DETAIL_STD_MODES 0xfa |
124 | #define EDID_DETAIL_MONITOR_CPDATA 0xfb | 124 | #define EDID_DETAIL_MONITOR_CPDATA 0xfb |
125 | #define EDID_DETAIL_MONITOR_NAME 0xfc | 125 | #define EDID_DETAIL_MONITOR_NAME 0xfc |
126 | #define EDID_DETAIL_MONITOR_RANGE 0xfd | 126 | #define EDID_DETAIL_MONITOR_RANGE 0xfd |
127 | #define EDID_DETAIL_MONITOR_STRING 0xfe | 127 | #define EDID_DETAIL_MONITOR_STRING 0xfe |
128 | #define EDID_DETAIL_MONITOR_SERIAL 0xff | 128 | #define EDID_DETAIL_MONITOR_SERIAL 0xff |
129 | 129 | ||
130 | struct detailed_timing { | 130 | struct detailed_timing { |
131 | __le16 pixel_clock; /* need to multiply by 10 KHz */ | 131 | __le16 pixel_clock; /* need to multiply by 10 KHz */ |
132 | union { | 132 | union { |
133 | struct detailed_pixel_timing pixel_data; | 133 | struct detailed_pixel_timing pixel_data; |
134 | struct detailed_non_pixel other_data; | 134 | struct detailed_non_pixel other_data; |
135 | } data; | 135 | } data; |
136 | } __attribute__((packed)); | 136 | } __attribute__((packed)); |
137 | 137 | ||
138 | #define DRM_EDID_INPUT_SERRATION_VSYNC (1 << 7) | 138 | #define DRM_EDID_INPUT_SERRATION_VSYNC (1 << 0) |
139 | #define DRM_EDID_INPUT_SYNC_ON_GREEN (1 << 5) | 139 | #define DRM_EDID_INPUT_SYNC_ON_GREEN (1 << 1) |
140 | #define DRM_EDID_INPUT_COMPOSITE_SYNC (1 << 4) | 140 | #define DRM_EDID_INPUT_COMPOSITE_SYNC (1 << 2) |
141 | #define DRM_EDID_INPUT_SEPARATE_SYNCS (1 << 3) | 141 | #define DRM_EDID_INPUT_SEPARATE_SYNCS (1 << 3) |
142 | #define DRM_EDID_INPUT_BLANK_TO_BLACK (1 << 2) | 142 | #define DRM_EDID_INPUT_BLANK_TO_BLACK (1 << 4) |
143 | #define DRM_EDID_INPUT_VIDEO_LEVEL (3 << 1) | 143 | #define DRM_EDID_INPUT_VIDEO_LEVEL (3 << 5) |
144 | #define DRM_EDID_INPUT_DIGITAL (1 << 0) /* bits above must be zero if set */ | 144 | #define DRM_EDID_INPUT_DIGITAL (1 << 7) /* bits below must be zero if set */ |
145 | 145 | ||
146 | #define DRM_EDID_FEATURE_DEFAULT_GTF (1 << 7) | 146 | #define DRM_EDID_FEATURE_DEFAULT_GTF (1 << 0) |
147 | #define DRM_EDID_FEATURE_PREFERRED_TIMING (1 << 6) | 147 | #define DRM_EDID_FEATURE_PREFERRED_TIMING (1 << 1) |
148 | #define DRM_EDID_FEATURE_STANDARD_COLOR (1 << 5) | 148 | #define DRM_EDID_FEATURE_STANDARD_COLOR (1 << 2) |
149 | #define DRM_EDID_FEATURE_DISPLAY_TYPE (3 << 3) /* 00=mono, 01=rgb, 10=non-rgb, 11=unknown */ | 149 | #define DRM_EDID_FEATURE_DISPLAY_TYPE (3 << 3) /* 00=mono, 01=rgb, 10=non-rgb, 11=unknown */ |
150 | #define DRM_EDID_FEATURE_PM_ACTIVE_OFF (1 << 2) | 150 | #define DRM_EDID_FEATURE_PM_ACTIVE_OFF (1 << 5) |
151 | #define DRM_EDID_FEATURE_PM_SUSPEND (1 << 1) | 151 | #define DRM_EDID_FEATURE_PM_SUSPEND (1 << 6) |
152 | #define DRM_EDID_FEATURE_PM_STANDBY (1 << 0) | 152 | #define DRM_EDID_FEATURE_PM_STANDBY (1 << 7) |
153 | 153 | ||
154 | struct edid { | 154 | struct edid { |
155 | u8 header[8]; | 155 | u8 header[8]; |
156 | /* Vendor & product info */ | 156 | /* Vendor & product info */ |
157 | u8 mfg_id[2]; | 157 | u8 mfg_id[2]; |
158 | u8 prod_code[2]; | 158 | u8 prod_code[2]; |
159 | u32 serial; /* FIXME: byte order */ | 159 | u32 serial; /* FIXME: byte order */ |
160 | u8 mfg_week; | 160 | u8 mfg_week; |
161 | u8 mfg_year; | 161 | u8 mfg_year; |
162 | /* EDID version */ | 162 | /* EDID version */ |
163 | u8 version; | 163 | u8 version; |
164 | u8 revision; | 164 | u8 revision; |
165 | /* Display info: */ | 165 | /* Display info: */ |
166 | u8 input; | 166 | u8 input; |
167 | u8 width_cm; | 167 | u8 width_cm; |
168 | u8 height_cm; | 168 | u8 height_cm; |
169 | u8 gamma; | 169 | u8 gamma; |
170 | u8 features; | 170 | u8 features; |
171 | /* Color characteristics */ | 171 | /* Color characteristics */ |
172 | u8 red_green_lo; | 172 | u8 red_green_lo; |
173 | u8 black_white_lo; | 173 | u8 black_white_lo; |
174 | u8 red_x; | 174 | u8 red_x; |
175 | u8 red_y; | 175 | u8 red_y; |
176 | u8 green_x; | 176 | u8 green_x; |
177 | u8 green_y; | 177 | u8 green_y; |
178 | u8 blue_x; | 178 | u8 blue_x; |
179 | u8 blue_y; | 179 | u8 blue_y; |
180 | u8 white_x; | 180 | u8 white_x; |
181 | u8 white_y; | 181 | u8 white_y; |
182 | /* Est. timings and mfg rsvd timings*/ | 182 | /* Est. timings and mfg rsvd timings*/ |
183 | struct est_timings established_timings; | 183 | struct est_timings established_timings; |
184 | /* Standard timings 1-8*/ | 184 | /* Standard timings 1-8*/ |
185 | struct std_timing standard_timings[8]; | 185 | struct std_timing standard_timings[8]; |
186 | /* Detailing timings 1-4 */ | 186 | /* Detailing timings 1-4 */ |
187 | struct detailed_timing detailed_timings[4]; | 187 | struct detailed_timing detailed_timings[4]; |
188 | /* Number of 128 byte ext. blocks */ | 188 | /* Number of 128 byte ext. blocks */ |
189 | u8 extensions; | 189 | u8 extensions; |
190 | /* Checksum */ | 190 | /* Checksum */ |
191 | u8 checksum; | 191 | u8 checksum; |
192 | } __attribute__((packed)); | 192 | } __attribute__((packed)); |
193 | 193 | ||
194 | #define EDID_PRODUCT_ID(e) ((e)->prod_code[0] | ((e)->prod_code[1] << 8)) | 194 | #define EDID_PRODUCT_ID(e) ((e)->prod_code[0] | ((e)->prod_code[1] << 8)) |
195 | 195 | ||
196 | #endif /* __DRM_EDID_H__ */ | 196 | #endif /* __DRM_EDID_H__ */ |
197 | 197 |