]>
Commit | Line | Data |
---|---|---|
a6316ce4 MT |
1 | #include <stdio.h> |
2 | #include <stdlib.h> | |
3 | #include <string.h> | |
4 | ||
5 | #include "hd.h" | |
6 | #include "hd_int.h" | |
7 | #include "hddb.h" | |
8 | #include "monitor.h" | |
9 | ||
10 | /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
11 | * monitor info | |
12 | * | |
13 | * Read the info out of the 'SuSE=' entry in /proc/cmdline. It contains | |
14 | * (among others) info from the EDID record got by our syslinux extension. | |
15 | * | |
16 | * We will try to look up our monitor id in the id file to get additional | |
17 | * info. | |
18 | * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
19 | */ | |
20 | ||
21 | #ifdef __PPC__ | |
22 | static void add_old_mac_monitor(hd_data_t *hd_data); | |
23 | static void add_monitor(hd_data_t *hd_data, devtree_t *dt); | |
24 | #endif | |
25 | static int chk_edid_info(hd_data_t *hd_data, unsigned char *edid); | |
26 | #if !defined(__PPC__) | |
27 | static void add_lcd_info(hd_data_t *hd_data, hd_t *hd, bios_info_t *bt); | |
28 | #endif | |
29 | static void add_edid_info(hd_data_t *hd_data, hd_t *hd, unsigned char *edid); | |
30 | static void add_monitor_res(hd_t *hd, unsigned x, unsigned y, unsigned hz, unsigned il); | |
31 | static void fix_edid_info(hd_data_t *hd_data, unsigned char *edid); | |
32 | ||
33 | #if !defined(__PPC__) | |
34 | void hd_scan_monitor(hd_data_t *hd_data) | |
35 | { | |
36 | hd_t *hd; | |
37 | int i, j, k; | |
38 | char *s, *s0, *s1, *se, m[8], *t; | |
39 | unsigned u; | |
40 | hd_res_t *res; | |
41 | monitor_info_t *mi = NULL; | |
42 | bios_info_t *bt; | |
43 | ||
44 | if(!hd_probe_feature(hd_data, pr_monitor)) return; | |
45 | ||
46 | hd_data->module = mod_monitor; | |
47 | ||
48 | /* some clean-up */ | |
49 | remove_hd_entries(hd_data); | |
50 | ||
51 | PROGRESS(1, 0, "ddc"); | |
52 | ||
53 | for(hd = hd_data->hd; hd; hd = hd->next) { | |
54 | if(hd->base_class.id == bc_internal && hd->sub_class.id == sc_int_bios) break; | |
55 | } | |
56 | ||
57 | /* first, see if we got the full edid record from bios */ | |
58 | bt = NULL; | |
59 | ||
60 | #if 0 | |
61 | /* for testing: LIBHD_EDID points to a file with valid edid record */ | |
62 | { | |
63 | char *s = getenv("LIBHD_EDID"); | |
64 | unsigned char edid[0x80]; | |
65 | FILE *f; | |
66 | ||
67 | if(s && (f = fopen(s, "r"))) { | |
68 | if(fread(edid, sizeof edid, 1, f) == 1) { | |
69 | hd = add_hd_entry(hd_data, __LINE__, 0); | |
70 | hd->base_class.id = bc_monitor; | |
71 | add_edid_info(hd_data, hd, edid); | |
72 | } | |
73 | fclose(f); | |
74 | return; | |
75 | } | |
76 | } | |
77 | #endif | |
78 | ||
79 | if( | |
80 | hd && | |
81 | hd->detail && | |
82 | hd->detail->type == hd_detail_bios && | |
83 | (bt = hd->detail->bios.data) && | |
84 | bt->vbe.ok | |
85 | ) { | |
86 | if(chk_edid_info(hd_data, bt->vbe.ddc)) { | |
87 | hd = add_hd_entry(hd_data, __LINE__, 0); | |
88 | hd->base_class.id = bc_monitor; | |
89 | ||
90 | hd_set_hw_class(hd, hw_vbe); | |
91 | ||
92 | add_edid_info(hd_data, hd, bt->vbe.ddc); | |
93 | ||
94 | return; | |
95 | } | |
96 | } | |
97 | ||
98 | /* Maybe a LCD panel? */ | |
99 | if(bt && bt->lcd.width) { | |
100 | hd = add_hd_entry(hd_data, __LINE__, 0); | |
101 | hd->base_class.id = bc_monitor; | |
102 | hd->sub_class.id = sc_mon_lcd; | |
103 | ||
104 | hd_set_hw_class(hd, hw_vbe); | |
105 | ||
106 | add_lcd_info(hd_data, hd, bt); | |
107 | ||
108 | return; | |
109 | } | |
110 | ||
111 | /* Maybe we have hidden edid info here? */ | |
112 | if(!(s = s0 = t = get_cmd_param(hd_data, 0))) return; /* no :-( */ | |
113 | ||
114 | s = strsep(&t, "^"); | |
115 | ||
116 | se = s + strlen(s); | |
117 | ||
118 | if(se - s < 7 + 2 * 4) { | |
119 | free_mem(s0); | |
120 | return; | |
121 | } | |
122 | ||
123 | /* Ok, we've got it. Now we split the fields. */ | |
124 | ||
125 | memcpy(m, s, 7); m[7] = 0; s += 7; | |
126 | ||
127 | hd = add_hd_entry(hd_data, __LINE__, 0); | |
128 | ||
129 | hd->base_class.id = bc_monitor; | |
130 | hd->vendor.id = name2eisa_id(m); | |
131 | if(sscanf(m + 3, "%x", &u) == 1) hd->device.id = MAKE_ID(TAG_EISA, u); | |
132 | if((u = device_class(hd_data, hd->vendor.id, hd->device.id))) { | |
133 | if((u >> 8) == bc_monitor) hd->sub_class.id = u & 0xff; | |
134 | } | |
135 | ||
136 | i = hex(s, 2); j = hex(s + 2, 2); s += 4; | |
137 | if(i > 0 && j > 0) { | |
138 | res = add_res_entry(&hd->res, new_mem(sizeof *res)); | |
139 | res->size.type = res_size; | |
140 | res->size.unit = size_unit_cm; | |
141 | res->size.val1 = i; /* width */ | |
142 | res->size.val2 = j; /* height */ | |
143 | } | |
144 | ||
145 | i = hex(s, 2); s+= 2; | |
146 | if(i & (1 << 7)) add_monitor_res(hd, 720, 400, 70, 0); | |
147 | if(i & (1 << 6)) add_monitor_res(hd, 720, 400, 88, 0); | |
148 | if(i & (1 << 5)) add_monitor_res(hd, 640, 480, 60, 0); | |
149 | if(i & (1 << 4)) add_monitor_res(hd, 640, 480, 67, 0); | |
150 | if(i & (1 << 3)) add_monitor_res(hd, 640, 480, 72, 0); | |
151 | if(i & (1 << 2)) add_monitor_res(hd, 640, 480, 75, 0); | |
152 | if(i & (1 << 1)) add_monitor_res(hd, 800, 600, 56, 0); | |
153 | if(i & (1 << 0)) add_monitor_res(hd, 800, 600, 60, 0); | |
154 | ||
155 | i = hex(s, 2); s+= 2; | |
156 | if(i & (1 << 7)) add_monitor_res(hd, 800, 600, 72, 0); | |
157 | if(i & (1 << 6)) add_monitor_res(hd, 800, 600, 75, 0); | |
158 | if(i & (1 << 5)) add_monitor_res(hd, 832, 624, 75, 0); | |
159 | if(i & (1 << 4)) add_monitor_res(hd, 1024, 768, 87, 1); | |
160 | if(i & (1 << 3)) add_monitor_res(hd, 1024, 768, 60, 0); | |
161 | if(i & (1 << 2)) add_monitor_res(hd, 1024, 768, 70, 0); | |
162 | if(i & (1 << 1)) add_monitor_res(hd, 1024, 768, 75, 0); | |
163 | if(i & (1 << 0)) add_monitor_res(hd, 1280, 1024, 75, 0); | |
164 | ||
165 | if(((se - s) & 1) || se - s > 8 * 4 + 2) { | |
166 | ADD2LOG(" ddc oops: %d bytes left?\n", (int) (se - s)); | |
167 | free_mem(s0); | |
168 | return; | |
169 | } | |
170 | ||
171 | while(s + 4 <= se) { | |
172 | i = (hex(s, 2) + 31) * 8; j = hex(s + 2, 2); s += 4; | |
173 | k = 0; | |
174 | switch((j >> 6) & 3) { | |
175 | case 1: k = (i * 3) / 4; break; | |
176 | case 2: k = (i * 4) / 5; break; | |
177 | case 3: k = (i * 9) / 16; break; | |
178 | } | |
179 | if(k) add_monitor_res(hd, i, k, (j & 0x3f) + 60, 0); | |
180 | } | |
181 | ||
182 | u = 0; | |
183 | if(se - s == 2) u = hex(s, 2) + 1990; | |
184 | ||
185 | if(u || t) { | |
186 | mi = new_mem(sizeof *mi); | |
187 | if(u) mi->manu_year = u; | |
188 | while((s = strsep(&t, "^"))) { | |
189 | for(s1 = s; *s1++; ) if(*s1 == '_') *s1 = ' '; | |
190 | switch(*s) { | |
191 | case '0': | |
192 | if(!mi->name && s[1]) mi->name = canon_str(s + 1, strlen(s + 1)); | |
193 | break; | |
194 | case '1': | |
195 | u = 0; | |
196 | if(strlen(s) == 9) { | |
197 | i = hex(s + 1, 2); | |
198 | j = hex(s + 3, 2); | |
199 | if(i > j || !i) u = 1; | |
200 | mi->min_vsync = i; | |
201 | mi->max_vsync = j; | |
202 | i = hex(s + 5, 2); | |
203 | j = hex(s + 7, 2); | |
204 | if(i > j || !i) u = 1; | |
205 | mi->min_hsync = i; | |
206 | mi->max_hsync = j; | |
207 | } | |
208 | else { | |
209 | u = 1; | |
210 | } | |
211 | if(u) { | |
212 | mi->min_vsync = mi->max_vsync = mi->min_hsync = mi->max_hsync = 0; | |
213 | ADD2LOG(" ddc oops: invalid freq data\n"); | |
214 | } | |
215 | break; | |
216 | case '2': | |
217 | if(!mi->vendor && s[1]) mi->vendor = canon_str(s + 1, strlen(s + 1)); | |
218 | break; | |
219 | case '3': | |
220 | if(!mi->serial && s[1]) mi->serial = canon_str(s + 1, strlen(s + 1)); | |
221 | break; | |
222 | default: | |
223 | ADD2LOG(" ddc oops: invalid tag 0x%02x\n", *s); | |
224 | } | |
225 | } | |
226 | } | |
227 | ||
228 | if(mi) { | |
229 | hd->detail = new_mem(sizeof *hd->detail); | |
230 | hd->detail->type = hd_detail_monitor; | |
231 | hd->detail->monitor.data = mi; | |
232 | ||
233 | hd->serial = new_str(mi->serial); | |
234 | ||
235 | #if 0 | |
236 | // ########### FIXME | |
237 | if( | |
238 | mi->vendor && | |
239 | ID_VALUE(hd->vendor.id) && | |
240 | !hd_vendor_name(hd_data, hd->vendor.id) | |
241 | ) { | |
242 | add_vendor_name(hd_data, hd->vend, mi->vendor); | |
243 | } | |
244 | #endif | |
245 | ||
246 | #if 0 | |
247 | // ########### FIXME | |
248 | if( | |
249 | mi->name && | |
250 | (ID_VALUE(hd->vendor.id) || ID_VALUE(hd->device.id)) && | |
251 | !hd_device_name(hd_data, hd->vend, hd->device.id) | |
252 | ) { | |
253 | add_device_name(hd_data, hd->vend, hd->dev, mi->name); | |
254 | } | |
255 | #endif | |
256 | ||
257 | if(hd_data->debug) { | |
258 | ADD2LOG("----- DDC info -----\n"); | |
259 | if(mi->vendor) { | |
260 | ADD2LOG(" vendor: \"%s\"\n", mi->vendor); | |
261 | } | |
262 | if(mi->name) { | |
263 | ADD2LOG(" model: \"%s\"\n", mi->name); | |
264 | } | |
265 | if(mi->serial) { | |
266 | ADD2LOG(" serial: \"%s\"\n", mi->serial); | |
267 | } | |
268 | if(mi->min_hsync) { | |
269 | ADD2LOG(" hsync: %u-%u kHz\n", mi->min_hsync, mi->max_hsync); | |
270 | } | |
271 | if(mi->min_vsync) { | |
272 | ADD2LOG(" vsync: %u-%u Hz\n", mi->min_vsync, mi->max_vsync); | |
273 | } | |
274 | if(mi->manu_year) { | |
275 | ADD2LOG(" manu. year: %u\n", mi->manu_year); | |
276 | } | |
277 | ADD2LOG("----- DDC info end -----\n"); | |
278 | } | |
279 | } | |
280 | ||
281 | free_mem(s0); | |
282 | } | |
283 | #endif /* !defined(__PPC__) */ | |
284 | ||
285 | #if defined(__PPC__) | |
286 | void hd_scan_monitor(hd_data_t *hd_data) | |
287 | { | |
288 | devtree_t *dt; | |
289 | int found; | |
290 | ||
291 | if(!hd_probe_feature(hd_data, pr_monitor)) return; | |
292 | ||
293 | hd_data->module = mod_monitor; | |
294 | ||
295 | /* some clean-up */ | |
296 | remove_hd_entries(hd_data); | |
297 | ||
298 | PROGRESS(1, 0, "prom"); | |
299 | ||
300 | found = 0; | |
301 | for(dt = hd_data->devtree; dt; dt = dt->next) { | |
302 | if(dt->edid) { | |
303 | add_monitor(hd_data, dt); | |
304 | found = 1; | |
305 | } | |
306 | } | |
307 | ||
308 | if(!found) { | |
309 | add_old_mac_monitor(hd_data); | |
310 | } | |
311 | } | |
312 | ||
313 | void add_old_mac_monitor(hd_data_t *hd_data) | |
314 | { | |
315 | hd_t *hd; | |
316 | unsigned u1, u2; | |
317 | str_list_t *sl; | |
318 | static struct { | |
319 | unsigned width, height, vfreq, interlaced; | |
320 | } mode_list[20] = { | |
321 | { 512, 384, 60, 1 }, | |
322 | { 512, 384, 60, 0 }, | |
323 | { 640, 480, 50, 1 }, | |
324 | { 640, 480, 60, 1 }, | |
325 | { 640, 480, 60, 0 }, | |
326 | { 640, 480, 67, 0 }, | |
327 | { 640, 870, 75, 0 }, | |
328 | { 768, 576, 50, 1 }, | |
329 | { 800, 600, 56, 0 }, | |
330 | { 800, 600, 60, 0 }, | |
331 | { 800, 600, 72, 0 }, | |
332 | { 800, 600, 75, 0 }, | |
333 | { 832, 624, 75, 0 }, | |
334 | { 1024, 768, 60, 0 }, | |
335 | { 1024, 768, 70, 0 }, | |
336 | { 1024, 768, 75, 0 }, | |
337 | { 1024, 768, 75, 0 }, | |
338 | { 1152, 870, 75, 0 }, | |
339 | { 1280, 960, 75, 0 }, | |
340 | { 1280, 1024, 75, 0 } | |
341 | }; | |
342 | ||
343 | for(sl = hd_data->klog; sl; sl = sl->next) { | |
344 | if(sscanf(sl->str, "<%*d>Monitor sense value = %i, using video mode %i", &u1, &u2) == 2) { | |
345 | u2--; | |
346 | hd = add_hd_entry(hd_data, __LINE__, 0); | |
347 | hd->base_class.id = bc_monitor; | |
348 | ||
349 | hd->vendor.id = MAKE_ID(TAG_SPECIAL, 0x0401); | |
350 | hd->device.id = MAKE_ID(TAG_SPECIAL, (u1 & 0xfff) + 0x1000); | |
351 | ||
352 | if((u1 = hd_display_adapter(hd_data))) { | |
353 | hd->attached_to = u1; | |
354 | } | |
355 | ||
356 | if(u2 < sizeof mode_list / sizeof *mode_list) { | |
357 | add_monitor_res(hd, mode_list[u2].width, mode_list[u2].height, mode_list[u2].vfreq, mode_list[u2].interlaced); | |
358 | } | |
359 | ||
360 | break; | |
361 | } | |
362 | } | |
363 | ||
364 | } | |
365 | ||
366 | void add_monitor(hd_data_t *hd_data, devtree_t *dt) | |
367 | { | |
368 | hd_t *hd, *hd2; | |
369 | unsigned char *edid = dt->edid; | |
370 | ||
371 | if(!chk_edid_info(hd_data, edid)) return; | |
372 | ||
373 | hd = add_hd_entry(hd_data, __LINE__, 0); | |
374 | ||
375 | hd->base_class.id = bc_monitor; | |
376 | ||
377 | for(hd2 = hd_data->hd; hd2; hd2 = hd2->next) { | |
378 | if( | |
379 | hd2->detail && | |
380 | hd2->detail->type == hd_detail_devtree && | |
381 | hd2->detail->devtree.data == dt | |
382 | ) { | |
383 | hd->attached_to = hd2->idx; | |
384 | break; | |
385 | } | |
386 | } | |
387 | ||
388 | add_edid_info(hd_data, hd, edid); | |
389 | } | |
390 | ||
391 | #endif /* defined(__PPC__) */ | |
392 | ||
393 | /* do some checks to ensure we got a reasonable block */ | |
394 | int chk_edid_info(hd_data_t *hd_data, unsigned char *edid) | |
395 | { | |
396 | // no vendor or model info | |
397 | if(!(edid[0x08] || edid[0x09] || edid[0x0a] || edid[0x0b])) return 0; | |
398 | ||
399 | // no edid version or revision | |
400 | if(!(edid[0x12] || edid[0x13])) return 0; | |
401 | ||
402 | return 1; | |
403 | } | |
404 | ||
405 | #if !defined(__PPC__) | |
406 | void add_lcd_info(hd_data_t *hd_data, hd_t *hd, bios_info_t *bt) | |
407 | { | |
408 | monitor_info_t *mi = NULL; | |
409 | ||
410 | hd->vendor.name = new_str(bt->lcd.vendor); | |
411 | hd->device.name = new_str(bt->lcd.name); | |
412 | ||
413 | add_monitor_res(hd, bt->lcd.width, bt->lcd.height, 60, 0); | |
414 | ||
415 | mi = new_mem(sizeof *mi); | |
416 | hd->detail = new_mem(sizeof *hd->detail); | |
417 | hd->detail->type = hd_detail_monitor; | |
418 | hd->detail->monitor.data = mi; | |
419 | ||
420 | mi->min_vsync = 50; | |
421 | mi->min_hsync = 31; | |
422 | mi->max_vsync = 75; | |
423 | mi->max_hsync = (mi->max_vsync * bt->lcd.height * 12) / 10000; | |
424 | } | |
425 | #endif | |
426 | ||
427 | void add_edid_info(hd_data_t *hd_data, hd_t *hd, unsigned char *edid) | |
428 | { | |
429 | hd_res_t *res; | |
430 | monitor_info_t *mi = NULL; | |
431 | int i; | |
432 | unsigned u, u1, u2; | |
433 | ||
434 | fix_edid_info(hd_data, edid); | |
435 | ||
436 | u = (edid[8] << 8) + edid[9]; | |
437 | hd->vendor.id = MAKE_ID(TAG_EISA, u); | |
438 | u = (edid[0xb] << 8) + edid[0xa]; | |
439 | hd->device.id = MAKE_ID(TAG_EISA, u); | |
440 | if((u = device_class(hd_data, hd->vendor.id, hd->device.id))) { | |
441 | if((u >> 8) == bc_monitor) hd->sub_class.id = u & 0xff; | |
442 | } | |
443 | ||
444 | if(edid[0x15] > 0 && edid[0x16] > 0) { | |
445 | res = add_res_entry(&hd->res, new_mem(sizeof *res)); | |
446 | res->size.type = res_size; | |
447 | res->size.unit = size_unit_cm; | |
448 | res->size.val1 = edid[0x15]; /* width */ | |
449 | res->size.val2 = edid[0x16]; /* height */ | |
450 | } | |
451 | ||
452 | u = edid[0x23]; | |
453 | if(u & (1 << 7)) add_monitor_res(hd, 720, 400, 70, 0); | |
454 | if(u & (1 << 6)) add_monitor_res(hd, 720, 400, 88, 0); | |
455 | if(u & (1 << 5)) add_monitor_res(hd, 640, 480, 60, 0); | |
456 | if(u & (1 << 4)) add_monitor_res(hd, 640, 480, 67, 0); | |
457 | if(u & (1 << 3)) add_monitor_res(hd, 640, 480, 72, 0); | |
458 | if(u & (1 << 2)) add_monitor_res(hd, 640, 480, 75, 0); | |
459 | if(u & (1 << 1)) add_monitor_res(hd, 800, 600, 56, 0); | |
460 | if(u & (1 << 0)) add_monitor_res(hd, 800, 600, 60, 0); | |
461 | ||
462 | u = edid[0x24]; | |
463 | if(u & (1 << 7)) add_monitor_res(hd, 800, 600, 72, 0); | |
464 | if(u & (1 << 6)) add_monitor_res(hd, 800, 600, 75, 0); | |
465 | if(u & (1 << 5)) add_monitor_res(hd, 832, 624, 75, 0); | |
466 | if(u & (1 << 4)) add_monitor_res(hd, 1024, 768, 87, 1); | |
467 | if(u & (1 << 3)) add_monitor_res(hd, 1024, 768, 60, 0); | |
468 | if(u & (1 << 2)) add_monitor_res(hd, 1024, 768, 70, 0); | |
469 | if(u & (1 << 1)) add_monitor_res(hd, 1024, 768, 75, 0); | |
470 | if(u & (1 << 0)) add_monitor_res(hd, 1280, 1024, 75, 0); | |
471 | ||
472 | for(i = 0; i < 4; i++) { | |
473 | u1 = (edid[0x26 + 2 * i] + 31) * 8; | |
474 | u2 = edid[0x27 + 2 * i]; | |
475 | u = 0; | |
476 | switch((u2 >> 6) & 3) { | |
477 | case 1: u = (u1 * 3) / 4; break; | |
478 | case 2: u = (u1 * 4) / 5; break; | |
479 | case 3: u = (u1 * 9) / 16; break; | |
480 | } | |
481 | if(u) add_monitor_res(hd, u1, u, (u2 & 0x3f) + 60, 0); | |
482 | } | |
483 | ||
484 | mi = new_mem(sizeof *mi); | |
485 | mi->manu_year = 1990 + edid[0x11]; | |
486 | ||
487 | for(i = 0x36; i < 0x36 + 4 * 0x12; i += 0x12) { | |
488 | if(!(edid[i] || edid[i + 1] || edid[i + 2])) { | |
489 | switch(edid[i + 3]) { | |
490 | case 0xfc: | |
491 | if(edid[i + 5]) { | |
492 | /* the name entry is splitted some times */ | |
493 | str_printf(&mi->name, -1, "%s%s", mi->name ? " " : "", canon_str(edid + i + 5, 0xd)); | |
494 | } | |
495 | break; | |
496 | ||
497 | case 0xfd: | |
498 | u = 0; | |
499 | u1 = edid[i + 5]; | |
500 | u2 = edid[i + 6]; | |
501 | if(u1 > u2 || !u1) u = 1; | |
502 | mi->min_vsync = u1; | |
503 | mi->max_vsync = u2; | |
504 | u1 = edid[i + 7]; | |
505 | u2 = edid[i + 8]; | |
506 | if(u1 > u2 || !u1) u = 1; | |
507 | mi->min_hsync = u1; | |
508 | mi->max_hsync = u2; | |
509 | if(u) { | |
510 | mi->min_vsync = mi->max_vsync = mi->min_hsync = mi->max_hsync = 0; | |
511 | ADD2LOG(" ddc oops: invalid freq data\n"); | |
512 | } | |
513 | break; | |
514 | ||
515 | case 0xfe: | |
516 | if(!mi->vendor && edid[i + 5]) mi->vendor = canon_str(edid + i + 5, 0xd); | |
517 | break; | |
518 | ||
519 | case 0xff: | |
520 | if(!mi->serial && edid[i + 5]) mi->serial = canon_str(edid + i + 5, 0xd); | |
521 | break; | |
522 | ||
523 | default: | |
524 | ADD2LOG(" ddc oops: invalid tag 0x%02x\n", edid[i + 3]); | |
525 | } | |
526 | } | |
527 | } | |
528 | ||
529 | if(mi) { | |
530 | hd->detail = new_mem(sizeof *hd->detail); | |
531 | hd->detail->type = hd_detail_monitor; | |
532 | hd->detail->monitor.data = mi; | |
533 | ||
534 | hd->serial = new_str(mi->serial); | |
535 | hd->vendor.name = new_str(mi->vendor); | |
536 | hd->device.name = new_str(mi->name); | |
537 | ||
538 | if(hd_data->debug) { | |
539 | ADD2LOG("----- DDC info -----\n"); | |
540 | if(mi->vendor) { | |
541 | ADD2LOG(" vendor: \"%s\"\n", mi->vendor); | |
542 | } | |
543 | if(mi->name) { | |
544 | ADD2LOG(" model: \"%s\"\n", mi->name); | |
545 | } | |
546 | if(mi->serial) { | |
547 | ADD2LOG(" serial: \"%s\"\n", mi->serial); | |
548 | } | |
549 | if(mi->min_hsync) { | |
550 | ADD2LOG(" hsync: %u-%u kHz\n", mi->min_hsync, mi->max_hsync); | |
551 | } | |
552 | if(mi->min_vsync) { | |
553 | ADD2LOG(" vsync: %u-%u Hz\n", mi->min_vsync, mi->max_vsync); | |
554 | } | |
555 | if(mi->manu_year) { | |
556 | ADD2LOG(" manu. year: %u\n", mi->manu_year); | |
557 | } | |
558 | ADD2LOG("----- DDC info end -----\n"); | |
559 | } | |
560 | } | |
561 | } | |
562 | ||
563 | void add_monitor_res(hd_t *hd, unsigned width, unsigned height, unsigned vfreq, unsigned il) | |
564 | { | |
565 | hd_res_t *res; | |
566 | ||
567 | res = add_res_entry(&hd->res, new_mem(sizeof *res)); | |
568 | res->monitor.type = res_monitor; | |
569 | res->monitor.width = width; | |
570 | res->monitor.height = height; | |
571 | res->monitor.vfreq = vfreq; | |
572 | res->monitor.interlaced = il; | |
573 | } | |
574 | ||
575 | /* | |
576 | * This looks evil, but some Mac displays really lie at us. | |
577 | */ | |
578 | void fix_edid_info(hd_data_t *hd_data, unsigned char *edid) | |
579 | { | |
580 | unsigned vend, dev; | |
581 | unsigned timing; | |
582 | int fix = 0; | |
583 | ||
584 | vend = (edid[8] << 8) + edid[9]; | |
585 | dev = (edid[0xb] << 8) + edid[0xa]; | |
586 | ||
587 | timing = (edid[0x24] << 8) + edid[0x23]; | |
588 | ||
589 | /* APP9214: Apple Studio Display */ | |
590 | if(vend == 0x0610 && dev == 0x9214 && timing == 0x0800) { | |
591 | timing = 0x1000; | |
592 | fix = 1; | |
593 | } | |
594 | ||
595 | if(fix) { | |
596 | edid[0x23] = timing & 0xff; | |
597 | edid[0x24] = (timing >> 8) & 0xff; | |
598 | } | |
599 | } | |
600 |