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