xref: /haiku/src/add-ons/kernel/drivers/graphics/intel_extreme/bios.cpp (revision 97f11716bfaa0f385eb0e28a52bf56a5023b9e99)
1 /*
2  * Copyright 2012-2014, Artem Falcon <lomka@gero.in>
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <string.h>
8 
9 #include <KernelExport.h>
10 #include <directories.h>
11 #include <stdio.h>
12 #include <driver_settings.h>
13 
14 #include "intel_extreme.h"
15 #include "driver.h"
16 
17 
18 #define TRACE_BIOS
19 #ifdef TRACE_BIOS
20 #	define TRACE(x) dprintf x
21 #else
22 #	define TRACE(x) ;
23 #endif
24 
25 
26 struct vbt_header {
27 	uint8 signature[20];
28 	uint16 version;
29 	uint16 header_size;
30 	uint16 vbt_size;
31 	uint8 vbt_checksum;
32 	uint8 reserved0;
33 	uint32 bdb_offset;
34 	uint32 aim_offset[4];
35 } __attribute__((packed));
36 
37 
38 struct bdb_header {
39 	uint8 signature[16];
40 	uint16 version;
41 	uint16 header_size;
42 	uint16 bdb_size;
43 } __attribute__((packed));
44 
45 
46 enum bdb_block_id {
47 	BDB_GENERAL_FEATURES = 1,
48 	BDB_GENERAL_DEFINITIONS,
49 	BDB_LVDS_OPTIONS = 40,
50 	BDB_LVDS_LFP_DATA_PTRS = 41,
51 	BDB_LVDS_BACKLIGHT = 43,
52 	BDB_GENERIC_DTD = 58
53 };
54 
55 
56 struct bdb_general_features {
57 	uint8 id;
58 	uint16 size;
59 
60 	uint8 panel_fitting: 2;
61 	bool flexaim: 1;
62 	bool msg_enable: 1;
63 	uint8 clear_screen: 3;
64 	bool color_flip: 1;
65 
66 	bool download_ext_vbt: 1;
67 	bool enable_ssc: 1;
68 	bool ssc_freq: 1;
69 	bool enable_lfp_on_override: 1;
70 	bool disable_ssc_ddt: 1;
71 	bool underscan_vga_timings: 1;
72 	bool display_clock_mode: 1;
73 	bool vbios_hotplug_support: 1;
74 
75 	bool disable_smooth_vision: 1;
76 	bool single_dvi: 1;
77 	bool rotate_180: 1;
78 	bool fdi_rx_polarity_inverted: 1;
79 	bool vbios_extended_mode: 1;
80 	bool copy_ilfp_dtd_to_svo_lvds_dtd: 1;
81 	bool panel_best_fit_timing: 1;
82 	bool ignore_strap_state: 1;
83 
84 	uint8 legacy_monitor_detect;
85 
86 	bool int_crt_support: 1;
87 	bool int_tv_support: 1;
88 	bool int_efp_support: 1;
89 	bool dp_ssc_enable: 1;
90 	bool dp_ssc_freq: 1;
91 	bool dp_ssc_dongle_supported: 1;
92 	uint8 rsvd11: 2;
93 
94 	uint8 tc_hpd_retry_timeout: 7;
95 	bool rsvd12: 1;
96 
97 	uint8 afc_startup_config: 2;
98 	uint8 rsvd13: 6;
99 } __attribute__((packed));
100 
101 
102 struct bdb_general_definitions {
103 	uint8 id;
104 	uint16 size;
105 	uint8 crt_ddc_gmbus_pin;
106 	uint8 dpms_bits;
107 	uint8 boot_display[2];
108 	uint8 child_device_size;
109 	uint8 devices[];
110 } __attribute__((packed));
111 
112 
113 // FIXME the struct definition for the bdb_header is not complete, so we rely
114 // on direct access with hardcoded offsets to get the timings out of it.
115 #define _PIXEL_CLOCK(x) (x[0] + (x[1] << 8)) * 10000
116 #define _H_ACTIVE(x) (x[2] + ((x[4] & 0xF0) << 4))
117 #define _H_BLANK(x) (x[3] + ((x[4] & 0x0F) << 8))
118 #define _H_SYNC_OFF(x) (x[8] + ((x[11] & 0xC0) << 2))
119 #define _H_SYNC_WIDTH(x) (x[9] + ((x[11] & 0x30) << 4))
120 #define _V_ACTIVE(x) (x[5] + ((x[7] & 0xF0) << 4))
121 #define _V_BLANK(x) (x[6] + ((x[7] & 0x0F) << 8))
122 #define _V_SYNC_OFF(x) ((x[10] >> 4) + ((x[11] & 0x0C) << 2))
123 #define _V_SYNC_WIDTH(x) ((x[10] & 0x0F) + ((x[11] & 0x03) << 4))
124 
125 #define BDB_BACKLIGHT_TYPE_NONE 0
126 #define BDB_BACKLIGHT_TYPE_PWM 2
127 
128 
129 struct lvds_bdb1 {
130 	uint8 id;
131 	uint16 size;
132 	uint8 panel_type;
133 	uint8 reserved0;
134 	uint16 caps;
135 } __attribute__((packed));
136 
137 
138 struct lvds_bdb2_entry {
139 	uint16 lfp_info_offset;
140 	uint8 lfp_info_size;
141 	uint16 lfp_edid_dtd_offset;
142 	uint8 lfp_edid_dtd_size;
143 	uint16 lfp_edid_pid_offset;
144 	uint8 lfp_edid_pid_size;
145 } __attribute__((packed));
146 
147 
148 struct lvds_bdb2 {
149 	uint8 id;
150 	uint16 size;
151 	uint8 table_size; /* followed by one or more lvds_data_ptr structs */
152 	struct lvds_bdb2_entry panels[16];
153 } __attribute__((packed));
154 
155 
156 struct lvds_bdb2_lfp_info {
157 	uint16 x_res;
158 	uint16 y_res;
159 	uint32 lvds_reg;
160 	uint32 lvds_reg_val;
161 	uint32 pp_on_reg;
162 	uint32 pp_on_reg_val;
163 	uint32 pp_off_reg;
164 	uint32 pp_off_reg_val;
165 	uint32 pp_cycle_reg;
166 	uint32 pp_cycle_reg_val;
167 	uint32 pfit_reg;
168 	uint32 pfit_reg_val;
169 	uint16 terminator;
170 } __attribute__((packed));
171 
172 
173 
174 struct generic_dtd_entry {
175 	uint32 pixel_clock;
176 	uint16 hactive;
177 	uint16 hblank;
178 	uint16 hfront_porch;
179 	uint16 hsync;
180 	uint16 vactive;
181 	uint16 vblank;
182 	uint16 vfront_porch;
183 	uint16 vsync;
184 	uint16 width_mm;
185 	uint16 height_mm;
186 
187 	uint8 rsvd_flags:6;
188 	uint8 vsync_positive_polarity:1;
189 	uint8 hsync_positive_polarity:1;
190 
191 	uint8 rsvd[3];
192 } __attribute__((packed));
193 
194 
195 struct bdb_generic_dtd {
196 	uint8 id;
197 	uint16 size;
198 	uint16 gdtd_size;
199 	struct generic_dtd_entry dtd[];
200 } __attribute__((packed));
201 
202 
203 struct bdb_lfp_backlight_data_entry {
204 	uint8 type: 2;
205 	uint8 active_low_pwm: 1;
206 	uint8 reserved1: 5;
207 	uint16 pwm_freq_hz;
208 	uint8 min_brightness; // Versions < 234
209 	uint8 reserved2;
210 	uint8 reserved3;
211 } __attribute__((packed));
212 
213 
214 struct bdb_lfp_backlight_control_method {
215 	uint8 type: 4;
216 	uint8 controller: 4;
217 } __attribute__((packed));
218 
219 
220 struct lfp_brightness_level {
221 	uint16 level;
222 	uint16 reserved;
223 } __attribute__((packed));
224 
225 
226 struct bdb_lfp_backlight_data {
227 	uint8 entry_size;
228 	struct bdb_lfp_backlight_data_entry data[16];
229 	uint8 level [16]; // Only for versions < 234
230 	struct bdb_lfp_backlight_control_method backlight_control[16];
231 	struct lfp_brightness_level brightness_level[16]; // Versions >= 234
232 	struct lfp_brightness_level brightness_min_level[16]; // Versions >= 234
233 	uint8 brightness_precision_bits[16]; // Versions >= 236
234 } __attribute__((packed));
235 
236 
237 static struct vbios {
238 	area_id			area;
239 	uint8*			memory;
ReadWordvbios240 	uint16_t ReadWord(off_t address)
241 	{
242 		return memory[address] | memory[address + 1] << 8;
243 	}
244 } vbios;
245 
246 
247 static bool
read_settings_dumpRom(void)248 read_settings_dumpRom(void)
249 {
250 	bool dumpRom = false;
251 
252 	void* settings = load_driver_settings("intel_extreme");
253 	if (settings != NULL) {
254 		dumpRom = get_driver_boolean_parameter(settings,
255 			"dump_rom", false, false);
256 
257 		unload_driver_settings(settings);
258 	}
259 	return dumpRom;
260 }
261 
262 
263 static void
dumprom(void * rom,uint32 size,intel_info & info)264 dumprom(void *rom, uint32 size, intel_info &info)
265 {
266 	int fd;
267 	uint32 cnt;
268 	char fname[64];
269 
270 	/* determine the romfile name: we need split-up per card in the system */
271 	sprintf (fname, kUserDirectory "//intel_extreme.%04x_%04x_%02x%02x%02x.rom",
272 		info.pci->vendor_id, info.pci->device_id, info.pci->bus, info.pci->device, info.pci->function);
273 
274 	fd = open (fname, O_WRONLY | O_CREAT, 0666);
275 	if (fd < 0) return;
276 
277 	/* The ROM size is a multiple of 1kb.. */
278 	for (cnt = 0; (cnt < size); cnt += 1024)
279 		write (fd, ((void *)(((uint8 *)rom) + cnt)), 1024);
280 	close (fd);
281 }
282 
283 
284 /*!	This is reimplementation, Haiku uses BIOS call and gets most current panel
285 	info, we're, otherwise, digging in VBIOS memory and parsing VBT tables to
286 	get native panel timings. This will allow to get non-updated,
287 	PROM-programmed timings info when compensation mode is off on your machine.
288 */
289 
290 // outdated: https://01.org/sites/default/files/documentation/skl_opregion_rev0p5.pdf
291 
292 #define ASLS 0xfc // ASL Storage.
293 #define OPREGION_SIGNATURE "IntelGraphicsMem"
294 #define OPREGION_ASLE_OFFSET   0x300
295 #define OPREGION_VBT_OFFSET   0x400
296 #define MBOX_ACPI (1 << 0)
297 #define MBOX_SWSCI (1 << 1)
298 #define MBOX_ASLE (1 << 2)
299 #define MBOX_ASLE_EXT (1 << 3)
300 
301 
302 struct opregion_header {
303 	uint8 signature[16];
304 	uint32 size;
305 	uint8 reserved;
306 	uint8 revision_version;
307 	uint8 minor_version;
308 	uint8 major_version;
309 	uint8 sver[32];
310 	uint8 vver[16];
311 	uint8 gver[16];
312 	uint32 mboxes;
313 	uint32 driver_model;
314 	uint32 platform_configuration;
315 	uint8 gop_version[32];
316 	uint8 rsvd[124];
317 } __attribute__((packed));
318 
319 
320 struct opregion_asle {
321 	uint32 ardy;
322 	uint32 aslc;
323 	uint32 tche;
324 	uint32 alsi;
325 	uint32 bclp;
326 	uint32 pfit;
327 	uint32 cblv;
328 	uint16 bclm[20];
329 	uint32 cpfm;
330 	uint32 epfm;
331 	uint8 plut[74];
332 	uint32 pfmb;
333 	uint32 cddv;
334 	uint32 pcft;
335 	uint32 srot;
336 	uint32 iuer;
337 	uint64 fdss;
338 	uint32 fdsp;
339 	uint32 stat;
340 	uint64 rvda;
341 	uint32 rvds;
342 	uint8 rsvd[58];
343 } __attribute__((packed));
344 
345 
346 static bool
get_bios(int * vbtOffset)347 get_bios(int* vbtOffset)
348 {
349 	STATIC_ASSERT(sizeof(opregion_header) == 0x100);
350 	STATIC_ASSERT(sizeof(opregion_asle) == 0x100);
351 
352 	intel_info &info = *gDeviceInfo[0];
353 	// first try to fetch Intel OpRegion which should be populated by the BIOS at start
354 	uint32 kVBIOSSize;
355 
356 	for (uint32 romMethod = 0; romMethod < 2; romMethod++) {
357 		switch(romMethod) {
358 			case 0:
359 			{
360 				// get OpRegion - see Intel ACPI IGD info in acpi_igd_opregion_spec_0.pdf
361 				uint64 kVBIOSAddress = get_pci_config(info.pci, ASLS, 4);
362 				if (kVBIOSAddress == 0) {
363 					TRACE((DEVICE_NAME ": ACPI OpRegion not supported!\n"));
364 					continue;
365 				}
366 				kVBIOSSize = 8 * 1024;
367 				TRACE((DEVICE_NAME ": Graphic OpRegion physical addr: 0x%" B_PRIx64
368 						"; size: 0x%" B_PRIx32 "\n", kVBIOSAddress, kVBIOSSize));
369 				vbios.area = map_physical_memory("ASLS mapping", kVBIOSAddress,
370 					kVBIOSSize, B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA, (void **)&vbios.memory);
371 				if (vbios.area < 0)
372 					continue;
373 				TRACE((DEVICE_NAME ": mapping OpRegion: 0x%" B_PRIx64 " -> %p\n",
374 					kVBIOSAddress, vbios.memory));
375 				// check if we got the OpRegion and signature
376 				if (memcmp(vbios.memory, OPREGION_SIGNATURE, 16) != 0) {
377 					TRACE((DEVICE_NAME ": OpRegion signature mismatch\n"));
378 					delete_area(vbios.area);
379 					vbios.area = -1;
380 					continue;
381 				}
382 				opregion_header* header = (opregion_header*)vbios.memory;
383 				opregion_asle* asle = (opregion_asle*)(vbios.memory + OPREGION_ASLE_OFFSET);
384 				if (header->major_version < 2 || (header->mboxes & MBOX_ASLE) == 0
385 					|| asle->rvda == 0 || asle->rvds == 0) {
386 					vbios.memory += OPREGION_VBT_OFFSET;
387 					kVBIOSSize -= OPREGION_VBT_OFFSET;
388 					break;
389 				}
390 				uint64 rvda = asle->rvda;
391 				kVBIOSSize = asle->rvds;
392 				if (header->major_version > 3 || header->minor_version >= 1) {
393 					rvda += kVBIOSAddress;
394 				}
395 				TRACE((DEVICE_NAME ": RVDA physical addr: 0x%" B_PRIx64
396 						"; size: 0x%" B_PRIx32 "\n", rvda, kVBIOSSize));
397 				delete_area(vbios.area);
398 				vbios.area = map_physical_memory("RVDA mapping", rvda,
399 					kVBIOSSize, B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA, (void **)&vbios.memory);
400 				if (vbios.area < 0)
401 					continue;
402 				break;
403 			}
404 			case 1:
405 			{
406 				uint64 kVBIOSAddress = 0xc0000;
407 				kVBIOSSize = 64 * 1024;
408 				/* !!!DANGER!!!: mapping of BIOS using legacy location as a fallback,
409 				  hence, if panel mode will be set using info from VBT this way, it will
410 				  be taken from primary card's VBIOS */
411 				vbios.area = map_physical_memory("VBIOS mapping", kVBIOSAddress,
412 					kVBIOSSize, B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA, (void**)&vbios.memory);
413 				if (vbios.area < 0)
414 					continue;
415 
416 				TRACE((DEVICE_NAME ": mapping VBIOS: 0x%" B_PRIx64 " -> %p\n",
417 					kVBIOSAddress, vbios.memory));
418 				break;
419 			}
420 		}
421 
422 		// dump ROM to file if selected in settings file
423 		if (read_settings_dumpRom())
424 			dumprom(vbios.memory, kVBIOSSize, info);
425 
426 		// scan BIOS for VBT signature
427 		*vbtOffset = kVBIOSSize;
428 		for (uint32 i = 0; i + 4 < kVBIOSSize; i += 4) {
429 			if (memcmp(vbios.memory + i, "$VBT", 4) == 0) {
430 				*vbtOffset = i;
431 				break;
432 			}
433 		}
434 
435 		if ((*vbtOffset + (uint32)sizeof(vbt_header)) >= kVBIOSSize) {
436 			TRACE((DEVICE_NAME": bad VBT offset : 0x%x\n", *vbtOffset));
437 			delete_area(vbios.area);
438 			continue;
439 		}
440 
441 		struct vbt_header* vbt = (struct vbt_header*)(vbios.memory + *vbtOffset);
442 		if (memcmp(vbt->signature, "$VBT", 4) != 0) {
443 			TRACE((DEVICE_NAME": bad VBT signature: %20s\n", vbt->signature));
444 			delete_area(vbios.area);
445 			continue;
446 		}
447 		return true;
448 	}
449 
450 	return false;
451 }
452 
453 
454 static void
sanitize_panel_timing(display_timing & timing)455 sanitize_panel_timing(display_timing& timing)
456 {
457 	bool bogus = false;
458 
459 	/* handle bogus h/vtotal values, if got such */
460 	if (timing.h_sync_end > timing.h_total) {
461 		timing.h_total = timing.h_sync_end + 1;
462 		bogus = true;
463 		TRACE((DEVICE_NAME": got bogus htotal. Fixing\n"));
464 	}
465 	if (timing.v_sync_end > timing.v_total) {
466 		timing.v_total = timing.v_sync_end + 1;
467 		bogus = true;
468 		TRACE((DEVICE_NAME": got bogus vtotal. Fixing\n"));
469 	}
470 
471 	if (bogus) {
472 		TRACE((DEVICE_NAME": adjusted LFP modeline: %" B_PRIu32 " KHz,\t"
473 			"%d %d %d %d   %d %d %d %d\n",
474 			timing.pixel_clock / (timing.h_total * timing.v_total),
475 			timing.h_display, timing.h_sync_start,
476 			timing.h_sync_end, timing.h_total,
477 			timing.v_display, timing.v_sync_start,
478 			timing.v_sync_end, timing.v_total));
479 	}
480 }
481 
482 
483 bool
parse_vbt_from_bios(struct intel_shared_info * info)484 parse_vbt_from_bios(struct intel_shared_info* info)
485 {
486 	display_timing* panelTiming = &info->panel_timing;
487 	uint16* minBrightness = &info->min_brightness;
488 
489 	int vbtOffset = 0;
490 	if (!get_bios(&vbtOffset))
491 		return false;
492 
493 	struct vbt_header* vbt = (struct vbt_header*)(vbios.memory + vbtOffset);
494 	int bdbOffset = vbtOffset + vbt->bdb_offset;
495 
496 	struct bdb_header* bdb = (struct bdb_header*)(vbios.memory + bdbOffset);
497 	if (memcmp(bdb->signature, "BIOS_DATA_BLOCK ", 16) != 0) {
498 		TRACE((DEVICE_NAME": bad BDB signature\n"));
499 		delete_area(vbios.area);
500 	}
501 	TRACE((DEVICE_NAME ": VBT signature \"%.*s\", BDB version %d\n",
502 			(int)sizeof(vbt->signature), vbt->signature, bdb->version));
503 
504 	int blockSize;
505 	int panelType = -1;
506 	bool panelTimingFound = false;
507 
508 	for (int bdbBlockOffset = bdb->header_size; bdbBlockOffset < bdb->bdb_size;
509 			bdbBlockOffset += blockSize) {
510 		int start = bdbOffset + bdbBlockOffset;
511 
512 		int id = vbios.memory[start];
513 		blockSize = vbios.ReadWord(start + 1) + 3;
514 		switch (id) {
515 			case BDB_GENERAL_FEATURES:
516 			{
517 				struct bdb_general_features* features;
518 				features = (struct bdb_general_features*)(vbios.memory + start);
519 				if (bdb->version >= 155
520 					&& (info->device_type.HasDDI()
521 						|| info->device_type.InGroup(INTEL_GROUP_VLV))) {
522 					info->internal_crt_support = features->int_crt_support;
523 					TRACE((DEVICE_NAME ": internal_crt_support: 0x%x\n",
524 						info->internal_crt_support));
525 				}
526 				break;
527 			}
528 			case BDB_GENERAL_DEFINITIONS:
529 			{
530 				info->device_config_count = 0;
531 				struct bdb_general_definitions* defs;
532 				if (bdb->version < 111)
533 					break;
534 				defs = (struct bdb_general_definitions*)(vbios.memory + start);
535 				uint8 childDeviceSize = defs->child_device_size;
536 				uint32 device_config_count = (blockSize - sizeof(*defs)) / childDeviceSize;
537 				for (uint32 i = 0; i < device_config_count; i++) {
538 					child_device_config* config =
539 						(child_device_config*)(&defs->devices[i * childDeviceSize]);
540 					if (config->device_type == 0)
541 						continue;
542 					memcpy(&info->device_configs[info->device_config_count], config,
543 						min_c(sizeof(child_device_config), childDeviceSize));
544 					TRACE((DEVICE_NAME ": found child device type: 0x%x\n", config->device_type));
545 					info->device_config_count++;
546 					if (info->device_config_count > 10)
547 						break;
548 				}
549 				break;
550 			}
551 			case BDB_LVDS_OPTIONS:
552 			{
553 				struct lvds_bdb1 *lvds1;
554 				lvds1 = (struct lvds_bdb1 *)(vbios.memory + start);
555 				panelType = lvds1->panel_type;
556 				if (panelType > 0xf) {
557 					TRACE((DEVICE_NAME ": invalid panel type %d\n", panelType));
558 					panelType = -1;
559 					break;
560 				}
561 				TRACE((DEVICE_NAME ": panel type: %d\n", panelType));
562 				break;
563 			}
564 			case BDB_LVDS_LFP_DATA_PTRS:
565 			{
566 				// First make sure we found block BDB_LVDS_OPTIONS and the panel type
567 				if (panelType == -1)
568 					break;
569 
570 				// on newer versions, check also generic DTD, use LFP panel DTD as a fallback
571 				if (bdb->version >= 229 && panelTimingFound)
572 					break;
573 
574 				struct lvds_bdb2 *lvds2;
575 				struct lvds_bdb2_lfp_info *lvds2_lfp_info;
576 
577 				lvds2 = (struct lvds_bdb2 *)(vbios.memory + start);
578 				lvds2_lfp_info = (struct lvds_bdb2_lfp_info *)
579 					(vbios.memory + bdbOffset
580 					+ lvds2->panels[panelType].lfp_info_offset);
581 				/* Show terminator: Check not done in drm i915 driver: Assuming chk not valid. */
582 				TRACE((DEVICE_NAME ": LFP info terminator %x\n", lvds2_lfp_info->terminator));
583 
584 				uint8_t* timing_data = vbios.memory + bdbOffset
585 					+ lvds2->panels[panelType].lfp_edid_dtd_offset;
586 				TRACE((DEVICE_NAME ": found LFP of size %d x %d "
587 					"in BIOS VBT tables\n",
588 					lvds2_lfp_info->x_res, lvds2_lfp_info->y_res));
589 
590 				panelTiming->pixel_clock = _PIXEL_CLOCK(timing_data) / 1000;
591 				panelTiming->h_sync_start = _H_ACTIVE(timing_data) + _H_SYNC_OFF(timing_data);
592 				panelTiming->h_sync_end = panelTiming->h_sync_start + _H_SYNC_WIDTH(timing_data);
593 				panelTiming->h_total = _H_ACTIVE(timing_data) + _H_BLANK(timing_data);
594 				panelTiming->h_display = _H_ACTIVE(timing_data);
595 				panelTiming->v_sync_start = _V_ACTIVE(timing_data) + _V_SYNC_OFF(timing_data);
596 				panelTiming->v_sync_end = panelTiming->v_sync_start + _V_SYNC_WIDTH(timing_data);
597 				panelTiming->v_total = _V_ACTIVE(timing_data) + _V_BLANK(timing_data);
598 				panelTiming->v_display = _V_ACTIVE(timing_data);
599 				panelTiming->flags = 0;
600 
601 				sanitize_panel_timing(*panelTiming);
602 
603 				panelTimingFound = true;
604 				break;
605 
606 			}
607 			case BDB_GENERIC_DTD:
608 			{
609 				// First make sure we found block BDB_LVDS_OPTIONS and the panel type
610 				if (panelType == -1)
611 					break;
612 
613 				bdb_generic_dtd* generic_dtd = (bdb_generic_dtd*)(vbios.memory + start);
614 				if (generic_dtd->gdtd_size < sizeof(bdb_generic_dtd)) {
615 					TRACE((DEVICE_NAME ": invalid gdtd_size %d\n", generic_dtd->gdtd_size));
616 					break;
617 				}
618 				int32 count = (blockSize - sizeof(bdb_generic_dtd)) / generic_dtd->gdtd_size;
619 				if (panelType >= count) {
620 					TRACE((DEVICE_NAME ": panel type not found %d in %" B_PRId32 " dtds\n",
621 						panelType, count));
622 					break;
623 				}
624 				generic_dtd_entry* dtd = &generic_dtd->dtd[panelType];
625 				TRACE((DEVICE_NAME ": pixel_clock %" B_PRId32 " "
626 					"hactive %d hfront_porch %d hsync %d hblank %d "
627 					"vactive %d vfront_porch %d vsync %d vblank %d\n",
628 						dtd->pixel_clock, dtd->hactive, dtd->hfront_porch, dtd->hsync, dtd->hblank,
629 						dtd->vactive, dtd->vfront_porch, dtd->vsync, dtd->vblank));
630 
631 				TRACE((DEVICE_NAME ": found generic dtd entry of size %d x %d "
632 					"in BIOS VBT tables\n", dtd->hactive, dtd->vactive));
633 
634 				panelTiming->pixel_clock = dtd->pixel_clock;
635 				panelTiming->h_sync_start = dtd->hactive + dtd->hfront_porch;
636 				panelTiming->h_sync_end = panelTiming->h_sync_start + dtd->hsync;
637 				panelTiming->h_total = dtd->hactive + dtd->hblank;
638 				panelTiming->h_display = dtd->hactive;
639 				panelTiming->v_sync_start = dtd->vactive + dtd->vfront_porch;
640 				panelTiming->v_sync_end = panelTiming->v_sync_start + dtd->vsync;
641 				panelTiming->v_total = dtd->vactive + dtd->vblank;
642 				panelTiming->v_display = dtd->vactive;
643 				panelTiming->flags = 0;
644 				if (dtd->hsync_positive_polarity)
645 					panelTiming->flags |= B_POSITIVE_HSYNC;
646 				if (dtd->vsync_positive_polarity)
647 					panelTiming->flags |= B_POSITIVE_VSYNC;
648 
649 				sanitize_panel_timing(*panelTiming);
650 				panelTimingFound = true;
651 				break;
652 			}
653 			case BDB_LVDS_BACKLIGHT:
654 			{
655 				TRACE((DEVICE_NAME ": found bdb lvds backlight info\n"));
656 				// First make sure we found block BDB_LVDS_OPTIONS and the panel type
657 				if (panelType == -1)
658 					break;
659 
660 				bdb_lfp_backlight_data* backlightData
661 					= (bdb_lfp_backlight_data*)(vbios.memory + start);
662 
663 				const struct bdb_lfp_backlight_data_entry* entry = &backlightData->data[panelType];
664 
665 				if (entry->type == BDB_BACKLIGHT_TYPE_PWM) {
666 					uint16 minLevel;
667 					if (bdb->version < 234) {
668 						minLevel = entry->min_brightness;
669 					} else {
670 						minLevel = backlightData->brightness_min_level[panelType].level;
671 						if (bdb->version >= 236
672 							&& backlightData->brightness_precision_bits[panelType] == 16) {
673 							TRACE((DEVICE_NAME ": divide level by 255\n"));
674 							minLevel /= 255;
675 						}
676 					}
677 
678 					*minBrightness = minLevel;
679 					TRACE((DEVICE_NAME ": display %d min brightness level is %u\n", panelType,
680 						minLevel));
681 				} else {
682 					TRACE((DEVICE_NAME ": display %d does not have PWM\n", panelType));
683 				}
684 				break;
685 			}
686 		}
687 	}
688 
689 	delete_area(vbios.area);
690 	return panelTimingFound;
691 }
692