xref: /haiku/src/add-ons/kernel/drivers/graphics/vesa/vesa.cpp (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
1 /*
2  * Copyright 2005-2009, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "vesa_private.h"
8 #include "vesa.h"
9 
10 #include <boot_item.h>
11 #include <driver_settings.h>
12 #include <frame_buffer_console.h>
13 #include <util/kernel_cpp.h>
14 #include <vm/vm.h>
15 
16 #include "driver.h"
17 #include "utility.h"
18 #include "vesa_info.h"
19 
20 
21 #define LINEAR_ADDRESS(segment, offset) \
22 	        (((addr_t)(segment) << 4) + (addr_t)(offset))
23 #define SEGMENTED_TO_LINEAR(segmented) \
24 	        LINEAR_ADDRESS((addr_t)(segmented) >> 16, (addr_t)(segmented) & 0xffff)
25 
26 
27 bios_module_info* sBIOSModule;
28 
29 
30 /*!	Loads the BIOS module and sets up a state for it. The BIOS module is only
31 	loaded when we need it, as it is quite a large module.
32 */
33 status_t
34 vbe_call_prepare(bios_state** state)
35 {
36 	status_t status;
37 
38 	status = get_module(B_BIOS_MODULE_NAME, (module_info**)&sBIOSModule);
39 	if (status != B_OK) {
40 		dprintf(DEVICE_NAME ": failed to get BIOS module: %s\n",
41 			strerror(status));
42 		return status;
43 	}
44 
45 	status = sBIOSModule->prepare(state);
46 	if (status != B_OK) {
47 		dprintf(DEVICE_NAME ": failed to prepare BIOS state: %s\n",
48 			strerror(status));
49 		put_module(B_BIOS_MODULE_NAME);
50 	}
51 
52 	return status;
53 }
54 
55 
56 void
57 vbe_call_finish(bios_state* state)
58 {
59 	sBIOSModule->finish(state);
60 	put_module(B_BIOS_MODULE_NAME);
61 }
62 
63 
64 static status_t
65 find_graphics_card(addr_t frameBuffer, addr_t& base, size_t& size)
66 {
67 	// TODO: when we port this over to the new driver API, this mechanism can be
68 	// used to find the right device_node
69 	pci_module_info* pci;
70 	if (get_module(B_PCI_MODULE_NAME, (module_info**)&pci) != B_OK)
71 		return B_ERROR;
72 
73 	pci_info info;
74 	for (int32 index = 0; pci->get_nth_pci_info(index, &info) == B_OK; index++) {
75 		if (info.class_base != PCI_display)
76 			continue;
77 
78 		// check PCI BARs
79 		for (uint32 i = 0; i < 6; i++) {
80 			if (info.u.h0.base_registers[i] <= frameBuffer
81 				&& info.u.h0.base_registers[i] + info.u.h0.base_register_sizes[i]
82 					> frameBuffer) {
83 				// found it!
84 				base = info.u.h0.base_registers[i];
85 				size = info.u.h0.base_register_sizes[i];
86 
87 				put_module(B_PCI_MODULE_NAME);
88 				return B_OK;
89 			}
90 		}
91 	}
92 
93 	put_module(B_PCI_MODULE_NAME);
94 	return B_ENTRY_NOT_FOUND;
95 }
96 
97 
98 uint32
99 get_color_space_for_depth(uint32 depth)
100 {
101 	switch (depth) {
102 		case 1:
103 			return B_GRAY1;
104 		case 4:
105 			return B_GRAY8;
106 				// the app_server is smart enough to translate this to VGA mode
107 		case 8:
108 			return B_CMAP8;
109 		case 15:
110 			return B_RGB15;
111 		case 16:
112 			return B_RGB16;
113 		case 24:
114 			return B_RGB24;
115 		case 32:
116 			return B_RGB32;
117 	}
118 
119 	return 0;
120 }
121 
122 
123 status_t
124 vbe_get_mode_info(bios_state* state, uint16 mode, struct vbe_mode_info* modeInfo)
125 {
126 	void* vbeModeInfo = sBIOSModule->allocate_mem(state,
127 		sizeof(struct vbe_mode_info));
128 	if (vbeModeInfo == NULL)
129 		return B_NO_MEMORY;
130 	memset(vbeModeInfo, 0, sizeof(vbe_mode_info));
131 
132 	uint32 physicalAddress = sBIOSModule->physical_address(state, vbeModeInfo);
133 	bios_regs regs = {};
134 	regs.eax = 0x4f01;
135 	regs.ecx = mode;
136 	regs.es  = physicalAddress >> 4;
137 	regs.edi = physicalAddress - (regs.es << 4);
138 
139 	status_t status = sBIOSModule->interrupt(state, 0x10, &regs);
140 	if (status != B_OK) {
141 		dprintf(DEVICE_NAME ": vbe_get_mode_info(%u): BIOS failed: %s\n", mode,
142 			strerror(status));
143 		return status;
144 	}
145 
146 	if ((regs.eax & 0xffff) != 0x4f) {
147 		dprintf(DEVICE_NAME ": vbe_get_mode_info(%u): BIOS returned "
148 			"0x%04" B_PRIx32 "\n", mode, regs.eax & 0xffff);
149 		return B_ENTRY_NOT_FOUND;
150 	}
151 
152 	memcpy(modeInfo, vbeModeInfo, sizeof(struct vbe_mode_info));
153 	return B_OK;
154 }
155 
156 
157 status_t
158 vbe_set_mode(bios_state* state, uint16 mode)
159 {
160 	bios_regs regs = {};
161 	regs.eax = 0x4f02;
162 	regs.ebx = (mode & SET_MODE_MASK) | SET_MODE_LINEAR_BUFFER;
163 
164 	status_t status = sBIOSModule->interrupt(state, 0x10, &regs);
165 	if (status != B_OK) {
166 		dprintf(DEVICE_NAME ": vbe_set_mode(%u): BIOS failed: %s\n", mode,
167 			strerror(status));
168 		return status;
169 	}
170 
171 	if ((regs.eax & 0xffff) != 0x4f) {
172 		dprintf(DEVICE_NAME ": vbe_set_mode(%u): BIOS returned 0x%04" B_PRIx32
173 			"\n", mode, regs.eax & 0xffff);
174 		return B_ERROR;
175 	}
176 
177 	return B_OK;
178 }
179 
180 
181 static uint32
182 vbe_to_system_dpms(uint8 vbeMode)
183 {
184 	uint32 mode = 0;
185 	if ((vbeMode & (DPMS_OFF | DPMS_REDUCED_ON)) != 0)
186 		mode |= B_DPMS_OFF;
187 	if ((vbeMode & DPMS_STANDBY) != 0)
188 		mode |= B_DPMS_STAND_BY;
189 	if ((vbeMode & DPMS_SUSPEND) != 0)
190 		mode |= B_DPMS_SUSPEND;
191 
192 	return mode;
193 }
194 
195 
196 static status_t
197 vbe_get_dpms_capabilities(bios_state* state, uint32& vbeMode, uint32& mode)
198 {
199 	// we always return a valid mode
200 	vbeMode = 0;
201 	mode = B_DPMS_ON;
202 
203 	bios_regs regs = {};
204 	regs.eax = 0x4f10;
205 	regs.ebx = 0;
206 	regs.esi = 0;
207 	regs.edi = 0;
208 
209 	status_t status = sBIOSModule->interrupt(state, 0x10, &regs);
210 	if (status != B_OK) {
211 		dprintf(DEVICE_NAME ": vbe_get_dpms_capabilities(): BIOS failed: %s\n",
212 			strerror(status));
213 		return status;
214 	}
215 
216 	if ((regs.eax & 0xffff) != 0x4f) {
217 		dprintf(DEVICE_NAME ": vbe_get_dpms_capabilities(): BIOS returned "
218 			"0x%04" B_PRIx32 "\n", regs.eax & 0xffff);
219 		return B_ERROR;
220 	}
221 
222 	vbeMode = regs.ebx >> 8;
223 	mode = vbe_to_system_dpms(vbeMode);
224 	return status;
225 }
226 
227 
228 status_t
229 vbe_set_bits_per_gun(bios_state* state, vesa_info& info, uint8 bits)
230 {
231 	info.bits_per_gun = 6;
232 
233 	bios_regs regs = {};
234 	regs.eax = 0x4f08;
235 	regs.ebx = (bits << 8) | 1;
236 
237 	status_t status = sBIOSModule->interrupt(state, 0x10, &regs);
238 	if (status != B_OK) {
239 		dprintf(DEVICE_NAME ": vbe_set_bits_per_gun(): BIOS failed: %s\n",
240 			strerror(status));
241 		return status;
242 	}
243 
244 	if ((regs.eax & 0xffff) != 0x4f) {
245 		dprintf(DEVICE_NAME ": vbe_set_bits_per_gun(): BIOS returned "
246 			"0x%04" B_PRIx32 "\n", regs.eax & 0xffff);
247 		return B_ERROR;
248 	}
249 
250 	info.bits_per_gun = regs.ebx >> 8;
251 	return B_OK;
252 }
253 
254 
255 // We used to read the existing mode set by the bootsplash to avoid setting the same mode
256 // when entering app_server, but this has been disabled. So now there is always a modeset
257 // done when app_server starts.
258 #if 0
259 static status_t
260 vbe_get_vesa_info(bios_state* state, vesa_info& info)
261 {
262 	vbe_info_block* infoHeader = (vbe_info_block*)sBIOSModule->allocate_mem(state, 256);
263 	phys_addr_t physicalAddress = sBIOSModule->physical_address(state, infoHeader);
264 
265 	bios_regs regs = {};
266 	regs.eax = 0x4f00;
267 	regs.es  = physicalAddress >> 4;
268 	regs.edi = physicalAddress - (regs.es << 4);
269 
270 	status_t status = sBIOSModule->interrupt(state, 0x10, &regs);
271 	if (status != B_OK) {
272 		dprintf(DEVICE_NAME ": vbe_get_vesa_info(): BIOS failed: %s\n",
273 			strerror(status));
274 		return status;
275 	}
276 
277 	if ((regs.eax & 0xffff) != 0x4f) {
278 		dprintf(DEVICE_NAME ": vbe_get_vesa_info(): BIOS returned "
279 			"0x%04" B_PRIx32 "\n", regs.eax & 0xffff);
280 		return B_ERROR;
281 	}
282 
283 	info.shared_info->vram_size = infoHeader->total_memory * 65536;
284 	strlcpy(info.shared_info->name, (char*)sBIOSModule->virtual_address(state,
285 			SEGMENTED_TO_LINEAR(infoHeader->oem_string)), 32);
286 
287 	return status;
288 }
289 #endif
290 
291 
292 /*!	Remaps the frame buffer if necessary; if we've already mapped the complete
293 	frame buffer, there is no need to map it again.
294 */
295 status_t
296 remap_frame_buffer(vesa_info& info, addr_t physicalBase, uint32 width,
297 	uint32 height, int8 depth, uint32 bytesPerRow, bool initializing)
298 {
299 	vesa_shared_info& sharedInfo = *info.shared_info;
300 	addr_t frameBuffer = info.frame_buffer;
301 
302 	if (!info.complete_frame_buffer_mapped) {
303 		addr_t base = physicalBase;
304 		size_t size = bytesPerRow * height;
305 		bool remap = !initializing;
306 
307 		if (info.physical_frame_buffer_size != 0) {
308 			// we can map the complete frame buffer
309 			base = info.physical_frame_buffer;
310 			size = info.physical_frame_buffer_size;
311 			remap = true;
312 		}
313 
314 		if (remap) {
315 			area_id area = map_physical_memory("vesa frame buffer", base,
316 				size, B_ANY_KERNEL_ADDRESS, B_READ_AREA | B_WRITE_AREA,
317 				(void**)&frameBuffer);
318 			if (area < 0)
319 				return area;
320 
321 			if (initializing) {
322 				// We need to manually update the kernel's frame buffer address,
323 				// since this frame buffer remapping has not been issued by the
324 				// app_server (which would otherwise take care of this)
325 				frame_buffer_update(frameBuffer, width, height, depth,
326 					bytesPerRow);
327 			}
328 
329 			delete_area(info.shared_info->frame_buffer_area);
330 
331 			info.frame_buffer = frameBuffer;
332 			sharedInfo.frame_buffer_area = area;
333 
334 			// Turn on write combining for the area
335 			vm_set_area_memory_type(area, base, B_MTR_WC);
336 
337 			if (info.physical_frame_buffer_size != 0)
338 				info.complete_frame_buffer_mapped = true;
339 		}
340 	}
341 
342 	if (info.complete_frame_buffer_mapped)
343 		frameBuffer += physicalBase - info.physical_frame_buffer;
344 
345 	// Update shared frame buffer information
346 	sharedInfo.frame_buffer = (uint8*)frameBuffer;
347 	sharedInfo.physical_frame_buffer = (uint8*)physicalBase;
348 	sharedInfo.bytes_per_row = bytesPerRow;
349 
350 	return B_OK;
351 }
352 
353 
354 //	#pragma mark -
355 
356 
357 status_t
358 vesa_init(vesa_info& info)
359 {
360 	frame_buffer_boot_info* bufferInfo
361 		= (frame_buffer_boot_info*)get_boot_item(FRAME_BUFFER_BOOT_INFO, NULL);
362 	if (bufferInfo == NULL)
363 		return B_ERROR;
364 
365 	info.vbe_capabilities = bufferInfo->vesa_capabilities;
366 	info.complete_frame_buffer_mapped = false;
367 
368 	// Find out which PCI device we belong to, so that we know its frame buffer
369 	// size
370 	find_graphics_card(bufferInfo->physical_frame_buffer,
371 		info.physical_frame_buffer, info.physical_frame_buffer_size);
372 
373 	size_t modesSize = 0;
374 	vesa_mode* modes = (vesa_mode*)get_boot_item(VESA_MODES_BOOT_INFO,
375 		&modesSize);
376 	info.modes = modes;
377 
378 	size_t sharedSize = (sizeof(vesa_shared_info) + 7) & ~7;
379 
380 	info.shared_area = create_area("vesa shared info",
381 		(void**)&info.shared_info, B_ANY_KERNEL_ADDRESS,
382 		ROUND_TO_PAGE_SIZE(sharedSize + modesSize), B_FULL_LOCK,
383 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_CLONEABLE_AREA);
384 	if (info.shared_area < 0)
385 		return info.shared_area;
386 
387 	vesa_shared_info& sharedInfo = *info.shared_info;
388 
389 	memset(&sharedInfo, 0, sizeof(vesa_shared_info));
390 
391 	if (modes != NULL) {
392 		sharedInfo.vesa_mode_offset = sharedSize;
393 		sharedInfo.vesa_mode_count = modesSize / sizeof(vesa_mode);
394 
395 		memcpy((uint8*)&sharedInfo + sharedSize, modes, modesSize);
396 	}
397 
398 	sharedInfo.frame_buffer_area = bufferInfo->area;
399 
400 	remap_frame_buffer(info, bufferInfo->physical_frame_buffer,
401 		bufferInfo->width, bufferInfo->height, bufferInfo->depth,
402 		bufferInfo->bytes_per_row, true);
403 		// Does not matter if this fails - the frame buffer was already mapped
404 		// before.
405 
406 	sharedInfo.current_mode.virtual_width = bufferInfo->width;
407 	sharedInfo.current_mode.virtual_height = bufferInfo->height;
408 	sharedInfo.current_mode.space = get_color_space_for_depth(
409 		bufferInfo->depth);
410 
411 	edid1_info* edidInfo = (edid1_info*)get_boot_item(VESA_EDID_BOOT_INFO,
412 		NULL);
413 	if (edidInfo != NULL) {
414 		sharedInfo.has_edid = true;
415 		memcpy(&sharedInfo.edid_info, edidInfo, sizeof(edid1_info));
416 	}
417 
418 	bios_state* state;
419 	status_t status = vbe_call_prepare(&state);
420 	if (status != B_OK)
421 		return status;
422 
423 	void* settings = load_driver_settings("vesa");
424 	bool patchingAllowed = false;
425 	if (settings != NULL) {
426 		patchingAllowed = get_driver_boolean_parameter(settings, "bios_patching",
427 			patchingAllowed, true);
428 		unload_driver_settings(settings);
429 	}
430 
431 	if (patchingAllowed)
432 		vesa_identify_bios(state, &sharedInfo);
433 	else
434 		sharedInfo.bios_type = kUnknownBiosType;
435 
436 	vbe_get_dpms_capabilities(state, info.vbe_dpms_capabilities,
437 		sharedInfo.dpms_capabilities);
438 	if (bufferInfo->depth <= 8)
439 		vbe_set_bits_per_gun(state, info, 8);
440 
441 	vbe_call_finish(state);
442 
443 	dprintf(DEVICE_NAME ": vesa_init() completed successfully!\n");
444 	return B_OK;
445 }
446 
447 
448 void
449 vesa_uninit(vesa_info& info)
450 {
451 	dprintf(DEVICE_NAME": vesa_uninit()\n");
452 
453 	delete_area(info.shared_info->frame_buffer_area);
454 	delete_area(info.shared_area);
455 }
456 
457 
458 status_t
459 vesa_set_display_mode(vesa_info& info, uint32 mode)
460 {
461 	if (mode >= info.shared_info->vesa_mode_count)
462 		return B_ENTRY_NOT_FOUND;
463 
464 	// Prepare BIOS environment
465 	bios_state* state;
466 	status_t status = vbe_call_prepare(&state);
467 	if (status != B_OK)
468 		return status;
469 
470 	// Get mode information
471 	struct vbe_mode_info modeInfo;
472 	status = vbe_get_mode_info(state, info.modes[mode].mode, &modeInfo);
473 	if (status != B_OK) {
474 		dprintf(DEVICE_NAME": vesa_set_display_mode(): cannot get mode info\n");
475 		goto out;
476 	}
477 
478 	// Set mode
479 	status = vbe_set_mode(state, info.modes[mode].mode);
480 	if (status != B_OK) {
481 		dprintf(DEVICE_NAME": vesa_set_display_mode(): cannot set mode\n");
482 		goto out;
483 	}
484 
485 	if (info.modes[mode].bits_per_pixel <= 8)
486 		vbe_set_bits_per_gun(state, info, 8);
487 
488 	// Map new frame buffer if necessary
489 
490 	status = remap_frame_buffer(info, modeInfo.physical_base, modeInfo.width,
491 		modeInfo.height, modeInfo.bits_per_pixel, modeInfo.bytes_per_row,
492 		false);
493 	if (status == B_OK) {
494 		// Update shared frame buffer information
495 		info.shared_info->current_mode.virtual_width = modeInfo.width;
496 		info.shared_info->current_mode.virtual_height = modeInfo.height;
497 		info.shared_info->current_mode.space = get_color_space_for_depth(
498 			modeInfo.bits_per_pixel);
499 	}
500 
501 out:
502 	vbe_call_finish(state);
503 	return status;
504 }
505 
506 
507 status_t
508 vesa_get_dpms_mode(vesa_info& info, uint32& mode)
509 {
510 	mode = B_DPMS_ON;
511 		// we always return a valid mode
512 
513 	// Prepare BIOS environment
514 	bios_state* state;
515 	status_t status = vbe_call_prepare(&state);
516 	if (status != B_OK)
517 		return status;
518 
519 	bios_regs regs = {};
520 	regs.eax = 0x4f10;
521 	regs.ebx = 2;
522 	regs.esi = 0;
523 	regs.edi = 0;
524 
525 	status = sBIOSModule->interrupt(state, 0x10, &regs);
526 	if (status != B_OK) {
527 		dprintf(DEVICE_NAME ": vesa_get_dpms_mode(): BIOS failed: %s\n",
528 			strerror(status));
529 		goto out;
530 	}
531 
532 	if ((regs.eax & 0xffff) != 0x4f) {
533 		dprintf(DEVICE_NAME ": vesa_get_dpms_mode(): BIOS returned "
534 			"0x%" B_PRIx32 "\n", regs.eax & 0xffff);
535 		status = B_ERROR;
536 		goto out;
537 	}
538 
539 	mode = vbe_to_system_dpms(regs.ebx >> 8);
540 
541 out:
542 	vbe_call_finish(state);
543 	return status;
544 }
545 
546 
547 status_t
548 vesa_set_dpms_mode(vesa_info& info, uint32 mode)
549 {
550 	// Only let supported modes through
551 	mode &= info.shared_info->dpms_capabilities;
552 
553 	uint8 vbeMode = 0;
554 	if ((mode & B_DPMS_OFF) != 0)
555 		vbeMode |= DPMS_OFF | DPMS_REDUCED_ON;
556 	if ((mode & B_DPMS_STAND_BY) != 0)
557 		vbeMode |= DPMS_STANDBY;
558 	if ((mode & B_DPMS_SUSPEND) != 0)
559 		vbeMode |= DPMS_SUSPEND;
560 
561 	vbeMode &= info.vbe_dpms_capabilities;
562 
563 	// Prepare BIOS environment
564 	bios_state* state;
565 	status_t status = vbe_call_prepare(&state);
566 	if (status != B_OK)
567 		return status;
568 
569 	bios_regs regs = {};
570 	regs.eax = 0x4f10;
571 	regs.ebx = (vbeMode << 8) | 1;
572 	regs.esi = 0;
573 	regs.edi = 0;
574 
575 	status = sBIOSModule->interrupt(state, 0x10, &regs);
576 	if (status != B_OK) {
577 		dprintf(DEVICE_NAME ": vesa_set_dpms_mode(): BIOS failed: %s\n",
578 			strerror(status));
579 		goto out;
580 	}
581 
582 	if ((regs.eax & 0xffff) != 0x4f) {
583 		dprintf(DEVICE_NAME ": vesa_set_dpms_mode(): BIOS returned "
584 			"0x%04" B_PRIx32 "\n", regs.eax & 0xffff);
585 		status = B_ERROR;
586 		goto out;
587 	}
588 
589 out:
590 	vbe_call_finish(state);
591 	return status;
592 }
593 
594 
595 status_t
596 vesa_set_indexed_colors(vesa_info& info, uint8 first, uint8* colors,
597 	uint16 count)
598 {
599 	bios_regs regs = {};
600 	uint32 shift, physicalAddress;
601 
602 	if (first + count > 256)
603 		count = 256 - first;
604 
605 	// Prepare BIOS environment
606 	bios_state* state;
607 	status_t status = vbe_call_prepare(&state);
608 	if (status != B_OK)
609 		return status;
610 
611 	uint8* palette = (uint8*)sBIOSModule->allocate_mem(state, 256 * 4);
612 	if (palette == NULL) {
613 		status = B_NO_MEMORY;
614 		goto out;
615 	}
616 
617 	shift = 8 - info.bits_per_gun;
618 
619 	// convert colors to VESA palette
620 	for (int32 i = first; i < count; i++) {
621 		uint8 color[3];
622 		if (user_memcpy(color, &colors[i * 3], 3) < B_OK) {
623 			status = B_BAD_ADDRESS;
624 			goto out;
625 		}
626 
627 		// order is BGR-
628 		palette[i * 4 + 0] = color[2] >> shift;
629 		palette[i * 4 + 1] = color[1] >> shift;
630 		palette[i * 4 + 2] = color[0] >> shift;
631 		palette[i * 4 + 3] = 0;
632 	}
633 
634 	// set palette
635 	physicalAddress = sBIOSModule->physical_address(state, palette);
636 	regs.eax = 0x4f09;
637 	regs.ebx = 0;
638 	regs.ecx = count;
639 	regs.edx = first;
640 	regs.es  = physicalAddress >> 4;
641 	regs.edi = physicalAddress - (regs.es << 4);
642 
643 	status = sBIOSModule->interrupt(state, 0x10, &regs);
644 	if (status != B_OK) {
645 		dprintf(DEVICE_NAME ": vesa_set_indexed_colors(): BIOS failed: %s\n",
646 			strerror(status));
647 		goto out;
648 	}
649 
650 	if ((regs.eax & 0xffff) != 0x4f) {
651 		dprintf(DEVICE_NAME ": vesa_set_indexed_colors(): BIOS returned "
652 			"0x%04" B_PRIx32 "\n", regs.eax & 0xffff);
653 		status = B_ERROR;
654 	}
655 
656 out:
657 	vbe_call_finish(state);
658 	return status;
659 }
660