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