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