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