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