]> git.ipfire.org Git - ipfire-2.x.git/blob - src/hwinfo/src/hd/monitor.c
Signierten GPG-Schluessel importiert.
[ipfire-2.x.git] / src / hwinfo / src / hd / monitor.c
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