blob: e2ac7100a8872dc3aabcff6e96fbe6f2ac6ba86d [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Tom Wai-Hong Tambaebbf52012-12-05 14:46:39 +00002/*
3 * Copyright (c) 2012 The Chromium OS Authors.
4 *
5 * (C) Copyright 2010
6 * Petr Stetiar <ynezz@true.cz>
7 *
Tom Wai-Hong Tambaebbf52012-12-05 14:46:39 +00008 * Contains stolen code from ddcprobe project which is:
9 * Copyright (C) Nalin Dahyabhai <bigfun@pobox.com>
Tom Wai-Hong Tambaebbf52012-12-05 14:46:39 +000010 */
11
Tom Wai-Hong Tambaebbf52012-12-05 14:46:39 +000012#include <edid.h>
Hans de Goede6e354e42014-11-24 13:47:13 +010013#include <errno.h>
Simon Glasseda77322015-04-14 21:03:37 -060014#include <fdtdec.h>
Simon Glass0f2af882020-05-10 11:40:05 -060015#include <log.h>
Tom Wai-Hong Tambaebbf52012-12-05 14:46:39 +000016#include <linux/ctype.h>
17#include <linux/string.h>
18
Jonas Schwöbelcdd74b32025-03-03 13:31:07 +020019#define TIMING(c, ha, hfp, hbp, hsl, va, vfp, vbp, vsl, f) \
20 .pixelclock = { (c), (c), (c) }, \
21 .hactive = { (ha), (ha), (ha) }, \
22 .hfront_porch = { (hfp), (hfp), (hfp) }, \
23 .hback_porch = { (hbp), (hbp), (hbp) }, \
24 .hsync_len = { (hsl), (hsl), (hsl) }, \
25 .vactive = { (va), (va), (va) }, \
26 .vfront_porch = { (vfp), (vfp), (vfp) }, \
27 .vback_porch = { (vbp), (vbp), (vbp) }, \
28 .vsync_len = { (vsl), (vsl), (vsl) }, \
29 .flags = (f)
30
31static const struct display_timing dmt_timings[] = {
32 { TIMING(31500000, 640, 32, 64, 96, 350, 32, 3, 60,
33 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) },
34 { TIMING(31500000, 640, 32, 64, 96, 400, 1, 3, 41,
35 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) },
36 { TIMING(35500000, 720, 36, 72, 108, 400, 1, 3, 42,
37 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) },
38 { TIMING(25175000, 640, 16, 96, 48, 480, 10, 2, 33,
39 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW) },
40 { TIMING(31500000, 640, 24, 40, 128, 480, 9, 3, 28,
41 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW) },
42 { TIMING(31500000, 640, 16, 64, 120, 480, 1, 3, 16,
43 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW) },
44 { TIMING(36000000, 640, 56, 56, 80, 480, 1, 3, 25,
45 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW) },
46 { TIMING(36000000, 800, 24, 72, 128, 600, 1, 2, 22,
47 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) },
48 { TIMING(40000000, 800, 40, 128, 88, 600, 1, 4, 23,
49 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) },
50 { TIMING(50000000, 800, 56, 120, 64, 600, 37, 6, 23,
51 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) },
52 { TIMING(49500000, 800, 16, 80, 160, 600, 1, 3, 21,
53 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) },
54 { TIMING(56250000, 800, 32, 64, 152, 600, 1, 3, 27,
55 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) },
56 { TIMING(73250000, 800, 48, 32, 80, 600, 3, 4, 29,
57 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) },
58 { TIMING(33750000, 848, 16, 112, 112, 480, 6, 8, 23,
59 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) },
60 { TIMING(44900000, 1024, 8, 176, 56, 768, 0, 8, 41,
61 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) },
62 { TIMING(65000000, 1024, 24, 136, 160, 768, 3, 6, 29,
63 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW) },
64 { TIMING(75000000, 1024, 24, 136, 144, 768, 3, 6, 29,
65 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW) },
66 { TIMING(78750000, 1024, 16, 96, 176, 768, 1, 3, 28,
67 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) },
68 { TIMING(94500000, 1024, 48, 96, 208, 768, 1, 3, 36,
69 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) },
70 { TIMING(115500000, 1024, 48, 32, 80, 768, 3, 4, 38,
71 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) },
72 { TIMING(108000000, 1152, 64, 128, 256, 864, 1, 3, 32,
73 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) },
74 { TIMING(74250000, 1280, 110, 40, 220, 720, 5, 5, 20,
75 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) },
76 { TIMING(68250000, 1280, 48, 32, 80, 768, 3, 7, 12,
77 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) },
78 { TIMING(79500000, 1280, 64, 128, 192, 768, 3, 7, 20,
79 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) },
80 { TIMING(102250000, 1280, 80, 128, 208, 768, 3, 7, 27,
81 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) },
82 { TIMING(117500000, 1280, 80, 136, 216, 768, 3, 7, 31,
83 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) },
84 { TIMING(140250000, 1280, 48, 32, 80, 768, 3, 7, 35,
85 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) },
86 { TIMING(71000000, 1280, 48, 32, 80, 800, 3, 6, 14,
87 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) },
88 { TIMING(83500000, 1280, 72, 128, 200, 800, 3, 6, 22,
89 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) },
90 { TIMING(106500000, 1280, 80, 128, 208, 800, 3, 6, 29,
91 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) },
92 { TIMING(122500000, 1280, 80, 136, 216, 800, 3, 6, 34,
93 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) },
94 { TIMING(146250000, 1280, 48, 32, 80, 800, 3, 6, 38,
95 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) },
96 { TIMING(108000000, 1280, 96, 112, 312, 960, 1, 3, 36,
97 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) },
98 { TIMING(148500000, 1280, 64, 160, 224, 960, 1, 3, 47,
99 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) },
100 { TIMING(175500000, 1280, 48, 32, 80, 960, 3, 4, 50,
101 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) },
102 { TIMING(108000000, 1280, 48, 112, 248, 1024, 1, 3, 38,
103 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) },
104 { TIMING(135000000, 1280, 16, 144, 248, 1024, 1, 3, 38,
105 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) },
106 { TIMING(157500000, 1280, 64, 160, 224, 1024, 1, 3, 44,
107 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) },
108 { TIMING(187250000, 1280, 48, 32, 80, 1024, 3, 7, 50,
109 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) },
110 { TIMING(85500000, 1360, 64, 112, 256, 768, 3, 6, 18,
111 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) },
112 { TIMING(148250000, 1360, 48, 32, 80, 768, 3, 5, 37,
113 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) },
114 { TIMING(85500000, 1366, 70, 143, 213, 768, 3, 3, 24,
115 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) },
116 { TIMING(72000000, 1366, 14, 56, 64, 768, 1, 3, 28,
117 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) },
118 { TIMING(101000000, 1400, 48, 32, 80, 1050, 3, 4, 23,
119 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) },
120 { TIMING(121750000, 1400, 88, 144, 232, 1050, 3, 4, 32,
121 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) },
122 { TIMING(156000000, 1400, 104, 144, 248, 1050, 3, 4, 42,
123 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) },
124 { TIMING(179500000, 1400, 104, 152, 256, 1050, 3, 4, 48,
125 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) },
126 { TIMING(208000000, 1400, 48, 32, 80, 1050, 3, 4, 55,
127 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) },
128 { TIMING(88750000, 1440, 48, 32, 80, 900, 3, 6, 17,
129 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) },
130 { TIMING(106500000, 1440, 80, 152, 232, 900, 3, 6, 25,
131 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) },
132 { TIMING(136750000, 1440, 96, 152, 248, 900, 3, 6, 33,
133 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) },
134 { TIMING(157000000, 1440, 104, 152, 256, 900, 3, 6, 39,
135 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) },
136 { TIMING(182750000, 1440, 48, 32, 80, 900, 3, 6, 44,
137 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) },
138 { TIMING(108000000, 1600, 24, 80, 96, 900, 1, 3, 96,
139 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) },
140 { TIMING(162000000, 1600, 64, 192, 304, 1200, 1, 3, 46,
141 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) },
142 { TIMING(175500000, 1600, 64, 192, 304, 1200, 1, 3, 46,
143 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) },
144 { TIMING(189000000, 1600, 64, 192, 304, 1200, 1, 3, 46,
145 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) },
146 { TIMING(202500000, 1600, 64, 192, 304, 1200, 1, 3, 46,
147 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) },
148 { TIMING(229500000, 1600, 64, 192, 304, 1200, 1, 3, 46,
149 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) },
150 { TIMING(268250000, 1600, 48, 32, 80, 1200, 3, 4, 64,
151 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) },
152 { TIMING(119000000, 1680, 48, 32, 80, 1050, 3, 6, 21,
153 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) },
154 { TIMING(146250000, 1680, 104, 176, 280, 1050, 3, 6, 30,
155 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) },
156 { TIMING(187000000, 1680, 120, 176, 296, 1050, 3, 6, 40,
157 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) },
158 { TIMING(214750000, 1680, 128, 176, 304, 1050, 3, 6, 46,
159 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) },
160 { TIMING(245500000, 1680, 48, 32, 80, 1050, 3, 6, 53,
161 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) },
162 { TIMING(204750000, 1792, 128, 200, 328, 1344, 1, 3, 46,
163 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) },
164 { TIMING(261000000, 1792, 96, 216, 352, 1344, 1, 3, 69,
165 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) },
166 { TIMING(333250000, 1792, 48, 32, 80, 1344, 3, 4, 72,
167 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) },
168 { TIMING(218250000, 1856, 96, 224, 352, 1392, 1, 3, 43,
169 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) },
170 { TIMING(288000000, 1856, 128, 224, 352, 1392, 1, 3, 104,
171 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) },
172 { TIMING(356500000, 1856, 48, 32, 80, 1392, 3, 4, 75,
173 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) },
174 { TIMING(148500000, 1920, 88, 44, 148, 1080, 4, 5, 36,
175 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_LOW) },
176 { TIMING(154000000, 1920, 48, 32, 80, 1200, 3, 6, 26,
177 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) },
178 { TIMING(193250000, 1920, 136, 200, 336, 1200, 3, 6, 36,
179 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) },
180 { TIMING(245250000, 1920, 136, 208, 344, 1200, 3, 6, 46,
181 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) },
182 { TIMING(281250000, 1920, 144, 208, 352, 1200, 3, 6, 53,
183 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) },
184 { TIMING(317000000, 1920, 48, 32, 80, 1200, 3, 6, 62,
185 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) },
186 { TIMING(234000000, 1920, 128, 208, 344, 1440, 1, 3, 56,
187 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) },
188 { TIMING(297000000, 1920, 144, 224, 352, 1440, 1, 3, 56,
189 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) },
190 { TIMING(380500000, 1920, 48, 32, 80, 1440, 3, 4, 78,
191 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) },
192 { TIMING(162000000, 2048, 26, 80, 96, 1152, 1, 3, 44,
193 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_HIGH) },
194 { TIMING(268500000, 2560, 48, 32, 80, 1600, 3, 6, 37,
195 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) },
196 { TIMING(348500000, 2560, 192, 280, 472, 1600, 3, 6, 49,
197 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) },
198 { TIMING(443250000, 2560, 208, 280, 488, 1600, 3, 6, 63,
199 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) },
200 { TIMING(505250000, 2560, 208, 280, 488, 1600, 3, 6, 73,
201 DISPLAY_FLAGS_HSYNC_LOW | DISPLAY_FLAGS_VSYNC_HIGH) },
202 { TIMING(552750000, 2560, 48, 32, 80, 1600, 3, 6, 85,
203 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) },
204 { TIMING(556744000, 4096, 8, 32, 40, 2160, 48, 8, 6,
205 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) },
206 { TIMING(556188000, 4096, 8, 32, 40, 2160, 48, 8, 6,
207 DISPLAY_FLAGS_HSYNC_HIGH | DISPLAY_FLAGS_VSYNC_LOW) },
208};
209
Tom Wai-Hong Tambaebbf52012-12-05 14:46:39 +0000210int edid_check_info(struct edid1_info *edid_info)
211{
212 if ((edid_info == NULL) || (edid_info->version == 0))
213 return -1;
214
215 if (memcmp(edid_info->header, "\x0\xff\xff\xff\xff\xff\xff\x0", 8))
216 return -1;
217
218 if (edid_info->version == 0xff && edid_info->revision == 0xff)
219 return -1;
220
221 return 0;
222}
223
Hans de Goede6e354e42014-11-24 13:47:13 +0100224int edid_check_checksum(u8 *edid_block)
225{
226 u8 checksum = 0;
227 int i;
228
229 for (i = 0; i < 128; i++)
230 checksum += edid_block[i];
231
232 return (checksum == 0) ? 0 : -EINVAL;
233}
234
Tom Wai-Hong Tambaebbf52012-12-05 14:46:39 +0000235int edid_get_ranges(struct edid1_info *edid, unsigned int *hmin,
236 unsigned int *hmax, unsigned int *vmin,
237 unsigned int *vmax)
238{
239 int i;
240 struct edid_monitor_descriptor *monitor;
241
242 *hmin = *hmax = *vmin = *vmax = 0;
243 if (edid_check_info(edid))
244 return -1;
245
246 for (i = 0; i < ARRAY_SIZE(edid->monitor_details.descriptor); i++) {
247 monitor = &edid->monitor_details.descriptor[i];
248 if (monitor->type == EDID_MONITOR_DESCRIPTOR_RANGE) {
249 *hmin = monitor->data.range_data.horizontal_min;
250 *hmax = monitor->data.range_data.horizontal_max;
251 *vmin = monitor->data.range_data.vertical_min;
252 *vmax = monitor->data.range_data.vertical_max;
253 return 0;
254 }
255 }
256 return -1;
257}
258
Simon Glasseda77322015-04-14 21:03:37 -0600259/* Set all parts of a timing entry to the same value */
260static void set_entry(struct timing_entry *entry, u32 value)
261{
262 entry->min = value;
263 entry->typ = value;
264 entry->max = value;
265}
266
267/**
268 * decode_timing() - Decoding an 18-byte detailed timing record
269 *
270 * @buf: Pointer to EDID detailed timing record
271 * @timing: Place to put timing
272 */
273static void decode_timing(u8 *buf, struct display_timing *timing)
274{
275 uint x_mm, y_mm;
276 unsigned int ha, hbl, hso, hspw, hborder;
277 unsigned int va, vbl, vso, vspw, vborder;
Jernej Skrabec837595d2017-04-29 14:43:35 +0200278 struct edid_detailed_timing *t = (struct edid_detailed_timing *)buf;
Simon Glasseda77322015-04-14 21:03:37 -0600279
280 /* Edid contains pixel clock in terms of 10KHz */
281 set_entry(&timing->pixelclock, (buf[0] + (buf[1] << 8)) * 10000);
282 x_mm = (buf[12] + ((buf[14] & 0xf0) << 4));
283 y_mm = (buf[13] + ((buf[14] & 0x0f) << 8));
284 ha = (buf[2] + ((buf[4] & 0xf0) << 4));
285 hbl = (buf[3] + ((buf[4] & 0x0f) << 8));
286 hso = (buf[8] + ((buf[11] & 0xc0) << 2));
287 hspw = (buf[9] + ((buf[11] & 0x30) << 4));
288 hborder = buf[15];
289 va = (buf[5] + ((buf[7] & 0xf0) << 4));
290 vbl = (buf[6] + ((buf[7] & 0x0f) << 8));
291 vso = ((buf[10] >> 4) + ((buf[11] & 0x0c) << 2));
292 vspw = ((buf[10] & 0x0f) + ((buf[11] & 0x03) << 4));
293 vborder = buf[16];
294
295 set_entry(&timing->hactive, ha);
296 set_entry(&timing->hfront_porch, hso);
297 set_entry(&timing->hback_porch, hbl - hso - hspw);
298 set_entry(&timing->hsync_len, hspw);
299
300 set_entry(&timing->vactive, va);
301 set_entry(&timing->vfront_porch, vso);
302 set_entry(&timing->vback_porch, vbl - vso - vspw);
303 set_entry(&timing->vsync_len, vspw);
304
Jernej Skrabec837595d2017-04-29 14:43:35 +0200305 timing->flags = 0;
306 if (EDID_DETAILED_TIMING_FLAG_HSYNC_POLARITY(*t))
307 timing->flags |= DISPLAY_FLAGS_HSYNC_HIGH;
308 else
309 timing->flags |= DISPLAY_FLAGS_HSYNC_LOW;
310 if (EDID_DETAILED_TIMING_FLAG_VSYNC_POLARITY(*t))
311 timing->flags |= DISPLAY_FLAGS_VSYNC_HIGH;
312 else
313 timing->flags |= DISPLAY_FLAGS_VSYNC_LOW;
314
315 if (EDID_DETAILED_TIMING_FLAG_INTERLACED(*t))
316 timing->flags = DISPLAY_FLAGS_INTERLACED;
317
Simon Glasseda77322015-04-14 21:03:37 -0600318 debug("Detailed mode clock %u Hz, %d mm x %d mm\n"
319 " %04x %04x %04x %04x hborder %x\n"
320 " %04x %04x %04x %04x vborder %x\n",
321 timing->pixelclock.typ,
322 x_mm, y_mm,
323 ha, ha + hso, ha + hso + hspw,
324 ha + hbl, hborder,
325 va, va + vso, va + vso + vspw,
326 va + vbl, vborder);
327}
328
Jernej Skrabec55884e52017-04-29 14:43:36 +0200329/**
330 * Check if HDMI vendor specific data block is present in CEA block
331 * @param info CEA extension block
Heinrich Schuchardt47b4c022022-01-19 18:05:50 +0100332 * Return: true if block is found
Jernej Skrabec55884e52017-04-29 14:43:36 +0200333 */
334static bool cea_is_hdmi_vsdb_present(struct edid_cea861_info *info)
335{
336 u8 end, i = 0;
337
338 /* check for end of data block */
339 end = info->dtd_offset;
340 if (end == 0)
Simon Glassb0e04282017-06-07 10:28:39 -0600341 end = sizeof(info->data);
342 if (end < 4 || end > sizeof(info->data))
Jernej Skrabec55884e52017-04-29 14:43:36 +0200343 return false;
344 end -= 4;
345
346 while (i < end) {
347 /* Look for vendor specific data block of appropriate size */
348 if ((EDID_CEA861_DB_TYPE(*info, i) == EDID_CEA861_DB_VENDOR) &&
349 (EDID_CEA861_DB_LEN(*info, i) >= 5)) {
350 u8 *db = &info->data[i + 1];
351 u32 oui = db[0] | (db[1] << 8) | (db[2] << 16);
352
353 if (oui == HDMI_IEEE_OUI)
354 return true;
355 }
356 i += EDID_CEA861_DB_LEN(*info, i) + 1;
357 }
358
359 return false;
360}
361
Jonas Schwöbelcdd74b32025-03-03 13:31:07 +0200362static bool edid_find_valid_detailed_timing(void *buf, int count,
363 struct display_timing *timing,
364 bool (*mode_valid)(void *priv,
365 const struct display_timing *timing),
366 void *mode_valid_priv)
Jernej Skrabec89efafe2021-04-22 01:14:28 +0100367{
368 struct edid_detailed_timing *t = buf;
369 bool found = false;
370 int i;
371
372 for (i = 0; i < count && !found; i++, t++)
373 if (EDID_DETAILED_TIMING_PIXEL_CLOCK(*t) != 0) {
374 decode_timing((u8 *)t, timing);
375 if (mode_valid)
376 found = mode_valid(mode_valid_priv,
377 timing);
378 else
379 found = true;
380 }
Jonas Schwöbelcdd74b32025-03-03 13:31:07 +0200381
382 return found;
383}
384
385static bool edid_get_standard_timing(struct edid1_info *edid, int i, unsigned int *x,
386 unsigned int *y, unsigned int *freq)
387{
388 unsigned int aspect = 10000;
389 unsigned char xres, vfreq;
390
391 xres = EDID1_INFO_STANDARD_TIMING_XRESOLUTION(*edid, i);
392 vfreq = EDID1_INFO_STANDARD_TIMING_VFREQ(*edid, i);
393 if (xres != vfreq || (xres != 0 && xres != 1) ||
394 (vfreq != 0 && vfreq != 1)) {
395 switch (EDID1_INFO_STANDARD_TIMING_ASPECT(*edid, i)) {
396 case ASPECT_625: // 16:10
397 aspect = 6250;
398 break;
399 case ASPECT_75: // 4:3
400 aspect = 7500;
401 break;
402 case ASPECT_8: // 5:4
403 aspect = 8000;
404 break;
405 case ASPECT_5625: // 16:9
406 aspect = 5625;
407 break;
408 }
409
410 *x = (xres + 31) * 8;
411 *y = *x * aspect / 10000;
412 *freq = (vfreq & 0x3f) + 60;
413
414 return true;
415 }
416
417 return false;
418}
419
420static bool edid_find_valid_standard_timing(struct edid1_info *buf,
421 struct display_timing *timing,
422 bool (*mode_valid)(void *priv,
423 const struct display_timing *timing),
424 void *mode_valid_priv)
425{
426 unsigned int x, y, freq;
427 bool found = false;
428 int i, k;
429
430 for (i = 0; i < ARRAY_SIZE(buf->standard_timings); i++) {
431 if (!edid_get_standard_timing(buf, i, &x, &y, &freq))
432 continue;
433
434 for (k = 0; k < ARRAY_SIZE(dmt_timings); k++) {
435 const struct display_timing *dt = &dmt_timings[k];
436
437 if (dt->hactive.typ == x && dt->vactive.typ == y) {
438 found = mode_valid(mode_valid_priv, dt);
439 if (found) {
440 memcpy(timing, dt, sizeof(*timing));
441 return true;
442 }
443 }
444 }
445 }
Jernej Skrabec89efafe2021-04-22 01:14:28 +0100446
447 return found;
448}
449
Neil Armstrong99fd44e2019-07-04 15:52:06 +0200450int edid_get_timing_validate(u8 *buf, int buf_size,
451 struct display_timing *timing,
452 int *panel_bits_per_colourp,
453 bool (*mode_valid)(void *priv,
454 const struct display_timing *timing),
455 void *mode_valid_priv)
Simon Glasseda77322015-04-14 21:03:37 -0600456{
457 struct edid1_info *edid = (struct edid1_info *)buf;
Jernej Skrabec89efafe2021-04-22 01:14:28 +0100458 bool found;
Simon Glasseda77322015-04-14 21:03:37 -0600459
460 if (buf_size < sizeof(*edid) || edid_check_info(edid)) {
461 debug("%s: Invalid buffer\n", __func__);
462 return -EINVAL;
463 }
464
Jernej Skrabec37d3bc62021-04-22 01:14:27 +0100465 if (!EDID1_INFO_VIDEO_INPUT_DIGITAL(*edid)) {
466 debug("%s: Not a digital display\n", __func__);
467 return -ENOSYS;
468 }
469
Simon Glasseda77322015-04-14 21:03:37 -0600470 if (!EDID1_INFO_FEATURE_PREFERRED_TIMING_MODE(*edid)) {
471 debug("%s: No preferred timing\n", __func__);
472 return -ENOENT;
473 }
474
Jernej Skrabec89efafe2021-04-22 01:14:28 +0100475 /* Look for detailed timing in base EDID */
Jonas Schwöbelcdd74b32025-03-03 13:31:07 +0200476 found = edid_find_valid_detailed_timing(edid->monitor_details.descriptor, 4,
477 timing, mode_valid, mode_valid_priv);
Jernej Skrabec04f173e2021-04-22 01:14:29 +0100478
479 /* Look for detailed timing in CTA-861 Extension Block */
480 if (!found && edid->extension_flag && buf_size >= EDID_EXT_SIZE) {
481 struct edid_cea861_info *info =
482 (struct edid_cea861_info *)(buf + sizeof(*edid));
483
484 if (info->extension_tag == EDID_CEA861_EXTENSION_TAG) {
485 int count = EDID_CEA861_DTD_COUNT(*info);
486 int offset = info->dtd_offset;
487 int size = count * sizeof(struct edid_detailed_timing);
488
489 if (offset >= 4 && offset + size < EDID_SIZE)
Jonas Schwöbelcdd74b32025-03-03 13:31:07 +0200490 found = edid_find_valid_detailed_timing(
Jernej Skrabec04f173e2021-04-22 01:14:29 +0100491 (u8 *)info + offset, count, timing,
492 mode_valid, mode_valid_priv);
493 }
494 }
495
Jonas Schwöbelcdd74b32025-03-03 13:31:07 +0200496 /* Look for timing in Standard Timings */
497 if (!found)
498 found = edid_find_valid_standard_timing(edid, timing, mode_valid,
499 mode_valid_priv);
500
Jernej Skrabec89efafe2021-04-22 01:14:28 +0100501 if (!found)
Simon Glasseda77322015-04-14 21:03:37 -0600502 return -EINVAL;
503
Simon Glasseda77322015-04-14 21:03:37 -0600504 if (edid->version != 1 || edid->revision < 4) {
505 debug("%s: EDID version %d.%d does not have required info\n",
506 __func__, edid->version, edid->revision);
507 *panel_bits_per_colourp = -1;
508 } else {
509 *panel_bits_per_colourp =
510 ((edid->video_input_definition & 0x70) >> 3) + 4;
511 }
512
Jernej Skrabec55884e52017-04-29 14:43:36 +0200513 timing->hdmi_monitor = false;
514 if (edid->extension_flag && (buf_size >= EDID_EXT_SIZE)) {
515 struct edid_cea861_info *info =
516 (struct edid_cea861_info *)(buf + sizeof(*edid));
517
518 if (info->extension_tag == EDID_CEA861_EXTENSION_TAG)
519 timing->hdmi_monitor = cea_is_hdmi_vsdb_present(info);
520 }
521
Simon Glasseda77322015-04-14 21:03:37 -0600522 return 0;
523}
524
Neil Armstrong99fd44e2019-07-04 15:52:06 +0200525int edid_get_timing(u8 *buf, int buf_size, struct display_timing *timing,
526 int *panel_bits_per_colourp)
527{
528 return edid_get_timing_validate(buf, buf_size, timing,
529 panel_bits_per_colourp, NULL, NULL);
530}
531
Tom Wai-Hong Tambaebbf52012-12-05 14:46:39 +0000532/**
533 * Snip the tailing whitespace/return of a string.
534 *
535 * @param string The string to be snipped
Heinrich Schuchardt47b4c022022-01-19 18:05:50 +0100536 * Return: the snipped string
Tom Wai-Hong Tambaebbf52012-12-05 14:46:39 +0000537 */
538static char *snip(char *string)
539{
540 char *s;
541
542 /*
543 * This is always a 13 character buffer
544 * and it's not always terminated.
545 */
546 string[12] = '\0';
547 s = &string[strlen(string) - 1];
548
549 while (s >= string && (isspace(*s) || *s == '\n' || *s == '\r' ||
550 *s == '\0'))
551 *(s--) = '\0';
552
553 return string;
554}
555
556/**
557 * Print an EDID monitor descriptor block
558 *
559 * @param monitor The EDID monitor descriptor block
560 * @have_timing Modifies to 1 if the desciptor contains timing info
561 */
562static void edid_print_dtd(struct edid_monitor_descriptor *monitor,
563 unsigned int *have_timing)
564{
565 unsigned char *bytes = (unsigned char *)monitor;
566 struct edid_detailed_timing *timing =
567 (struct edid_detailed_timing *)monitor;
568
569 if (bytes[0] == 0 && bytes[1] == 0) {
570 if (monitor->type == EDID_MONITOR_DESCRIPTOR_SERIAL)
571 printf("Monitor serial number: %s\n",
572 snip(monitor->data.string));
573 else if (monitor->type == EDID_MONITOR_DESCRIPTOR_ASCII)
574 printf("Monitor ID: %s\n",
575 snip(monitor->data.string));
576 else if (monitor->type == EDID_MONITOR_DESCRIPTOR_NAME)
577 printf("Monitor name: %s\n",
578 snip(monitor->data.string));
579 else if (monitor->type == EDID_MONITOR_DESCRIPTOR_RANGE)
580 printf("Monitor range limits, horizontal sync: "
581 "%d-%d kHz, vertical refresh: "
582 "%d-%d Hz, max pixel clock: "
583 "%d MHz\n",
584 monitor->data.range_data.horizontal_min,
585 monitor->data.range_data.horizontal_max,
586 monitor->data.range_data.vertical_min,
587 monitor->data.range_data.vertical_max,
588 monitor->data.range_data.pixel_clock_max * 10);
589 } else {
590 uint32_t pixclock, h_active, h_blanking, v_active, v_blanking;
591 uint32_t h_total, v_total, vfreq;
592
593 pixclock = EDID_DETAILED_TIMING_PIXEL_CLOCK(*timing);
594 h_active = EDID_DETAILED_TIMING_HORIZONTAL_ACTIVE(*timing);
595 h_blanking = EDID_DETAILED_TIMING_HORIZONTAL_BLANKING(*timing);
596 v_active = EDID_DETAILED_TIMING_VERTICAL_ACTIVE(*timing);
597 v_blanking = EDID_DETAILED_TIMING_VERTICAL_BLANKING(*timing);
598
599 h_total = h_active + h_blanking;
600 v_total = v_active + v_blanking;
Jernej Skrabec6dcd5a52017-05-23 23:05:30 +0200601 if (v_total > 0 && h_total > 0)
Tom Wai-Hong Tambaebbf52012-12-05 14:46:39 +0000602 vfreq = pixclock / (v_total * h_total);
603 else
604 vfreq = 1; /* Error case */
605 printf("\t%dx%d\%c\t%d Hz (detailed)\n", h_active,
606 v_active, h_active > 1000 ? ' ' : '\t', vfreq);
607 *have_timing = 1;
608 }
609}
610
611/**
612 * Get the manufacturer name from an EDID info.
613 *
614 * @param edid_info The EDID info to be printed
615 * @param name Returns the string of the manufacturer name
616 */
617static void edid_get_manufacturer_name(struct edid1_info *edid, char *name)
618{
619 name[0] = EDID1_INFO_MANUFACTURER_NAME_CHAR1(*edid) + 'A' - 1;
620 name[1] = EDID1_INFO_MANUFACTURER_NAME_CHAR2(*edid) + 'A' - 1;
621 name[2] = EDID1_INFO_MANUFACTURER_NAME_CHAR3(*edid) + 'A' - 1;
622 name[3] = '\0';
623}
624
625void edid_print_info(struct edid1_info *edid_info)
626{
627 int i;
628 char manufacturer[4];
629 unsigned int have_timing = 0;
630 uint32_t serial_number;
631
632 if (edid_check_info(edid_info)) {
633 printf("Not a valid EDID\n");
634 return;
635 }
636
637 printf("EDID version: %d.%d\n",
638 edid_info->version, edid_info->revision);
639
640 printf("Product ID code: %04x\n", EDID1_INFO_PRODUCT_CODE(*edid_info));
641
642 edid_get_manufacturer_name(edid_info, manufacturer);
643 printf("Manufacturer: %s\n", manufacturer);
644
645 serial_number = EDID1_INFO_SERIAL_NUMBER(*edid_info);
646 if (serial_number != 0xffffffff) {
647 if (strcmp(manufacturer, "MAG") == 0)
648 serial_number -= 0x7000000;
649 if (strcmp(manufacturer, "OQI") == 0)
650 serial_number -= 456150000;
651 if (strcmp(manufacturer, "VSC") == 0)
652 serial_number -= 640000000;
653 }
654 printf("Serial number: %08x\n", serial_number);
655 printf("Manufactured in week: %d year: %d\n",
656 edid_info->week, edid_info->year + 1990);
657
658 printf("Video input definition: %svoltage level %d%s%s%s%s%s\n",
659 EDID1_INFO_VIDEO_INPUT_DIGITAL(*edid_info) ?
660 "digital signal, " : "analog signal, ",
661 EDID1_INFO_VIDEO_INPUT_VOLTAGE_LEVEL(*edid_info),
662 EDID1_INFO_VIDEO_INPUT_BLANK_TO_BLACK(*edid_info) ?
663 ", blank to black" : "",
664 EDID1_INFO_VIDEO_INPUT_SEPARATE_SYNC(*edid_info) ?
665 ", separate sync" : "",
666 EDID1_INFO_VIDEO_INPUT_COMPOSITE_SYNC(*edid_info) ?
667 ", composite sync" : "",
668 EDID1_INFO_VIDEO_INPUT_SYNC_ON_GREEN(*edid_info) ?
669 ", sync on green" : "",
670 EDID1_INFO_VIDEO_INPUT_SERRATION_V(*edid_info) ?
671 ", serration v" : "");
672
673 printf("Monitor is %s\n",
674 EDID1_INFO_FEATURE_RGB(*edid_info) ? "RGB" : "non-RGB");
675
676 printf("Maximum visible display size: %d cm x %d cm\n",
677 edid_info->max_size_horizontal,
678 edid_info->max_size_vertical);
679
680 printf("Power management features: %s%s, %s%s, %s%s\n",
681 EDID1_INFO_FEATURE_ACTIVE_OFF(*edid_info) ?
682 "" : "no ", "active off",
683 EDID1_INFO_FEATURE_SUSPEND(*edid_info) ? "" : "no ", "suspend",
684 EDID1_INFO_FEATURE_STANDBY(*edid_info) ? "" : "no ", "standby");
685
686 printf("Estabilished timings:\n");
687 if (EDID1_INFO_ESTABLISHED_TIMING_720X400_70(*edid_info))
688 printf("\t720x400\t\t70 Hz (VGA 640x400, IBM)\n");
689 if (EDID1_INFO_ESTABLISHED_TIMING_720X400_88(*edid_info))
690 printf("\t720x400\t\t88 Hz (XGA2)\n");
691 if (EDID1_INFO_ESTABLISHED_TIMING_640X480_60(*edid_info))
692 printf("\t640x480\t\t60 Hz (VGA)\n");
693 if (EDID1_INFO_ESTABLISHED_TIMING_640X480_67(*edid_info))
694 printf("\t640x480\t\t67 Hz (Mac II, Apple)\n");
695 if (EDID1_INFO_ESTABLISHED_TIMING_640X480_72(*edid_info))
696 printf("\t640x480\t\t72 Hz (VESA)\n");
697 if (EDID1_INFO_ESTABLISHED_TIMING_640X480_75(*edid_info))
698 printf("\t640x480\t\t75 Hz (VESA)\n");
699 if (EDID1_INFO_ESTABLISHED_TIMING_800X600_56(*edid_info))
700 printf("\t800x600\t\t56 Hz (VESA)\n");
701 if (EDID1_INFO_ESTABLISHED_TIMING_800X600_60(*edid_info))
702 printf("\t800x600\t\t60 Hz (VESA)\n");
703 if (EDID1_INFO_ESTABLISHED_TIMING_800X600_72(*edid_info))
704 printf("\t800x600\t\t72 Hz (VESA)\n");
705 if (EDID1_INFO_ESTABLISHED_TIMING_800X600_75(*edid_info))
706 printf("\t800x600\t\t75 Hz (VESA)\n");
707 if (EDID1_INFO_ESTABLISHED_TIMING_832X624_75(*edid_info))
708 printf("\t832x624\t\t75 Hz (Mac II)\n");
709 if (EDID1_INFO_ESTABLISHED_TIMING_1024X768_87I(*edid_info))
710 printf("\t1024x768\t87 Hz Interlaced (8514A)\n");
711 if (EDID1_INFO_ESTABLISHED_TIMING_1024X768_60(*edid_info))
712 printf("\t1024x768\t60 Hz (VESA)\n");
713 if (EDID1_INFO_ESTABLISHED_TIMING_1024X768_70(*edid_info))
714 printf("\t1024x768\t70 Hz (VESA)\n");
715 if (EDID1_INFO_ESTABLISHED_TIMING_1024X768_75(*edid_info))
716 printf("\t1024x768\t75 Hz (VESA)\n");
717 if (EDID1_INFO_ESTABLISHED_TIMING_1280X1024_75(*edid_info))
718 printf("\t1280x1024\t75 (VESA)\n");
719 if (EDID1_INFO_ESTABLISHED_TIMING_1152X870_75(*edid_info))
720 printf("\t1152x870\t75 (Mac II)\n");
721
722 /* Standard timings. */
723 printf("Standard timings:\n");
724 for (i = 0; i < ARRAY_SIZE(edid_info->standard_timings); i++) {
Jonas Schwöbelcdd74b32025-03-03 13:31:07 +0200725 unsigned int x, y, freq;
Tom Wai-Hong Tambaebbf52012-12-05 14:46:39 +0000726
Jonas Schwöbelcdd74b32025-03-03 13:31:07 +0200727 if (edid_get_standard_timing(edid_info, i, &x, &y, &freq)) {
Tom Wai-Hong Tambaebbf52012-12-05 14:46:39 +0000728 printf("\t%dx%d%c\t%d Hz\n", x, y,
Jonas Schwöbelcdd74b32025-03-03 13:31:07 +0200729 x > 1000 ? ' ' : '\t', freq);
Tom Wai-Hong Tambaebbf52012-12-05 14:46:39 +0000730 have_timing = 1;
731 }
732 }
733
734 /* Detailed timing information. */
735 for (i = 0; i < ARRAY_SIZE(edid_info->monitor_details.descriptor);
736 i++) {
737 edid_print_dtd(&edid_info->monitor_details.descriptor[i],
738 &have_timing);
739 }
740
741 if (!have_timing)
742 printf("\tNone\n");
743}