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