xref: /haiku/src/add-ons/kernel/drivers/graphics/vesa/vesa.cpp (revision 4c8e85b316c35a9161f5a1c50ad70bc91c83a76f)
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 	vbe_get_dpms_capabilities(info.vbe_dpms_capabilities,
406 		sharedInfo.dpms_capabilities);
407 	if (bufferInfo->depth <= 8)
408 		vbe_set_bits_per_gun(info, 8);
409 
410 	dprintf(DEVICE_NAME ": vesa_init() completed successfully!\n");
411 	return B_OK;
412 }
413 
414 
415 void
416 vesa_uninit(vesa_info& info)
417 {
418 	dprintf(DEVICE_NAME": vesa_uninit()\n");
419 
420 	delete_area(info.shared_info->frame_buffer_area);
421 	delete_area(info.shared_area);
422 }
423 
424 
425 status_t
426 vesa_set_display_mode(vesa_info& info, uint32 mode)
427 {
428 	if (mode >= info.shared_info->vesa_mode_count)
429 		return B_ENTRY_NOT_FOUND;
430 
431 	// Prepare BIOS environment
432 	bios_state* state;
433 	status_t status = vbe_call_prepare(&state);
434 	if (status != B_OK)
435 		return status;
436 
437 	// Get mode information
438 	struct vbe_mode_info modeInfo;
439 	status = vbe_get_mode_info(state, info.modes[mode].mode, &modeInfo);
440 	if (status != B_OK) {
441 		dprintf(DEVICE_NAME": vesa_set_display_mode(): cannot get mode info\n");
442 		goto out;
443 	}
444 
445 	// Set mode
446 	status = vbe_set_mode(state, info.modes[mode].mode);
447 	if (status != B_OK) {
448 		dprintf(DEVICE_NAME": vesa_set_display_mode(): cannot set mode\n");
449 		goto out;
450 	}
451 
452 	if (info.modes[mode].bits_per_pixel <= 8)
453 		vbe_set_bits_per_gun(state, info, 8);
454 
455 	// Map new frame buffer if necessary
456 
457 	status = remap_frame_buffer(info, modeInfo.physical_base, modeInfo.width,
458 		modeInfo.height, modeInfo.bits_per_pixel, modeInfo.bytes_per_row,
459 		false);
460 	if (status == B_OK) {
461 		// Update shared frame buffer information
462 		info.shared_info->current_mode.virtual_width = modeInfo.width;
463 		info.shared_info->current_mode.virtual_height = modeInfo.height;
464 		info.shared_info->current_mode.space = get_color_space_for_depth(
465 			modeInfo.bits_per_pixel);
466 	}
467 
468 out:
469 	vbe_call_finish(state);
470 	return status;
471 }
472 
473 
474 status_t
475 vesa_get_dpms_mode(vesa_info& info, uint32& mode)
476 {
477 	mode = B_DPMS_ON;
478 		// we always return a valid mode
479 
480 	// Prepare BIOS environment
481 	bios_state* state;
482 	status_t status = vbe_call_prepare(&state);
483 	if (status != B_OK)
484 		return status;
485 
486 	bios_regs regs = {};
487 	regs.eax = 0x4f10;
488 	regs.ebx = 2;
489 	regs.esi = 0;
490 	regs.edi = 0;
491 
492 	status = sBIOSModule->interrupt(state, 0x10, &regs);
493 	if (status != B_OK) {
494 		dprintf(DEVICE_NAME ": vesa_get_dpms_mode(): BIOS failed: %s\n",
495 			strerror(status));
496 		goto out;
497 	}
498 
499 	if ((regs.eax & 0xffff) != 0x4f) {
500 		dprintf(DEVICE_NAME ": vesa_get_dpms_mode(): BIOS returned "
501 			"0x%" B_PRIx32 "\n", regs.eax & 0xffff);
502 		status = B_ERROR;
503 		goto out;
504 	}
505 
506 	mode = vbe_to_system_dpms(regs.ebx >> 8);
507 
508 out:
509 	vbe_call_finish(state);
510 	return status;
511 }
512 
513 
514 status_t
515 vesa_set_dpms_mode(vesa_info& info, uint32 mode)
516 {
517 	// Only let supported modes through
518 	mode &= info.shared_info->dpms_capabilities;
519 
520 	uint8 vbeMode = 0;
521 	if ((mode & B_DPMS_OFF) != 0)
522 		vbeMode |= DPMS_OFF | DPMS_REDUCED_ON;
523 	if ((mode & B_DPMS_STAND_BY) != 0)
524 		vbeMode |= DPMS_STANDBY;
525 	if ((mode & B_DPMS_SUSPEND) != 0)
526 		vbeMode |= DPMS_SUSPEND;
527 
528 	vbeMode &= info.vbe_dpms_capabilities;
529 
530 	// Prepare BIOS environment
531 	bios_state* state;
532 	status_t status = vbe_call_prepare(&state);
533 	if (status != B_OK)
534 		return status;
535 
536 	bios_regs regs = {};
537 	regs.eax = 0x4f10;
538 	regs.ebx = (vbeMode << 8) | 1;
539 	regs.esi = 0;
540 	regs.edi = 0;
541 
542 	status = sBIOSModule->interrupt(state, 0x10, &regs);
543 	if (status != B_OK) {
544 		dprintf(DEVICE_NAME ": vesa_set_dpms_mode(): BIOS failed: %s\n",
545 			strerror(status));
546 		goto out;
547 	}
548 
549 	if ((regs.eax & 0xffff) != 0x4f) {
550 		dprintf(DEVICE_NAME ": vesa_set_dpms_mode(): BIOS returned "
551 			"0x%04" B_PRIx32 "\n", regs.eax & 0xffff);
552 		status = B_ERROR;
553 		goto out;
554 	}
555 
556 out:
557 	vbe_call_finish(state);
558 	return status;
559 }
560 
561 
562 status_t
563 vesa_set_indexed_colors(vesa_info& info, uint8 first, uint8* colors,
564 	uint16 count)
565 {
566 	bios_regs regs = {};
567 	uint32 shift, physicalAddress;
568 
569 	if (first + count > 256)
570 		count = 256 - first;
571 
572 	// Prepare BIOS environment
573 	bios_state* state;
574 	status_t status = vbe_call_prepare(&state);
575 	if (status != B_OK)
576 		return status;
577 
578 	uint8* palette = (uint8*)sBIOSModule->allocate_mem(state, 256 * 4);
579 	if (palette == NULL) {
580 		status = B_NO_MEMORY;
581 		goto out;
582 	}
583 
584 	shift = 8 - info.bits_per_gun;
585 
586 	// convert colors to VESA palette
587 	for (int32 i = first; i < count; i++) {
588 		uint8 color[3];
589 		if (user_memcpy(color, &colors[i * 3], 3) < B_OK) {
590 			status = B_BAD_ADDRESS;
591 			goto out;
592 		}
593 
594 		// order is BGR-
595 		palette[i * 4 + 0] = color[2] >> shift;
596 		palette[i * 4 + 1] = color[1] >> shift;
597 		palette[i * 4 + 2] = color[0] >> shift;
598 		palette[i * 4 + 3] = 0;
599 	}
600 
601 	// set palette
602 	physicalAddress = sBIOSModule->physical_address(state, palette);
603 	regs.eax = 0x4f09;
604 	regs.ebx = 0;
605 	regs.ecx = count;
606 	regs.edx = first;
607 	regs.es  = physicalAddress >> 4;
608 	regs.edi = physicalAddress - (regs.es << 4);
609 
610 	status = sBIOSModule->interrupt(state, 0x10, &regs);
611 	if (status != B_OK) {
612 		dprintf(DEVICE_NAME ": vesa_set_indexed_colors(): BIOS failed: %s\n",
613 			strerror(status));
614 		goto out;
615 	}
616 
617 	if ((regs.eax & 0xffff) != 0x4f) {
618 		dprintf(DEVICE_NAME ": vesa_set_indexed_colors(): BIOS returned "
619 			"0x%04" B_PRIx32 "\n", regs.eax & 0xffff);
620 		status = B_ERROR;
621 	}
622 
623 out:
624 	vbe_call_finish(state);
625 	return status;
626 }
627