xref: /haiku/src/add-ons/kernel/drivers/graphics/vesa/vesa.cpp (revision a4ef4a49150f118d47324242917a596a3f8f8bd5)
1 /*
2  * Copyright 2005-2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
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.h>
17 
18 #include "driver.h"
19 #include "utility.h"
20 #include "vesa_info.h"
21 
22 
23 static uint32
24 get_color_space_for_depth(uint32 depth)
25 {
26 	switch (depth) {
27 		case 4:
28 			return B_GRAY8;
29 				// the app_server is smart enough to translate this to VGA mode
30 		case 8:
31 			return B_CMAP8;
32 		case 15:
33 			return B_RGB15;
34 		case 16:
35 			return B_RGB16;
36 		case 24:
37 			return B_RGB24;
38 		case 32:
39 			return B_RGB32;
40 	}
41 
42 	return 0;
43 }
44 
45 
46 static status_t
47 vbe_get_mode_info(struct vm86_state *vmState, uint16 mode,
48 	struct vbe_mode_info *modeInfo)
49 {
50 	struct vbe_mode_info *vbeModeInfo = (struct vbe_mode_info *)0x1000;
51 
52 	memset(vbeModeInfo, 0, sizeof(vbe_mode_info));
53 	vmState->regs.eax = 0x4f01;
54 	vmState->regs.ecx = mode;
55 	vmState->regs.es  = 0x1000 >> 4;
56 	vmState->regs.edi = 0x0000;
57 
58 	status_t status = vm86_do_int(vmState, 0x10);
59 	if (status != B_OK) {
60 		dprintf(DEVICE_NAME ": vbe_get_mode_info(%u): vm86 failed\n", mode);
61 		return status;
62 	}
63 
64 	if ((vmState->regs.eax & 0xffff) != 0x4f) {
65 		dprintf(DEVICE_NAME ": vbe_get_mode_info(): BIOS returned 0x%04lx\n",
66 			vmState->regs.eax & 0xffff);
67 		return B_ENTRY_NOT_FOUND;
68 	}
69 
70 	memcpy(modeInfo, vbeModeInfo, sizeof(struct vbe_mode_info));
71 	return B_OK;
72 }
73 
74 
75 static status_t
76 vbe_set_mode(struct vm86_state *vmState, uint16 mode)
77 {
78 	vmState->regs.eax = 0x4f02;
79 	vmState->regs.ebx = (mode & SET_MODE_MASK) | SET_MODE_LINEAR_BUFFER;
80 
81 	status_t status = vm86_do_int(vmState, 0x10);
82 	if (status != B_OK) {
83 		dprintf(DEVICE_NAME ": vbe_set_mode(%u): vm86 failed\n", mode);
84 		return status;
85 	}
86 
87 	if ((vmState->regs.eax & 0xffff) != 0x4f) {
88 		dprintf(DEVICE_NAME ": vbe_set_mode(): BIOS returned 0x%04lx\n",
89 			vmState->regs.eax & 0xffff);
90 		return B_ERROR;
91 	}
92 
93 	return B_OK;
94 }
95 
96 
97 //	#pragma mark -
98 
99 
100 status_t
101 vesa_init(vesa_info &info)
102 {
103 	frame_buffer_boot_info *bufferInfo
104 		= (frame_buffer_boot_info *)get_boot_item(FRAME_BUFFER_BOOT_INFO, NULL);
105 	if (bufferInfo == NULL)
106 		return B_ERROR;
107 
108 	size_t modesSize = 0;
109 	vesa_mode *modes = (vesa_mode *)get_boot_item(VESA_MODES_BOOT_INFO,
110 		&modesSize);
111 	info.modes = modes;
112 
113 	size_t sharedSize = (sizeof(vesa_shared_info) + 7) & ~7;
114 
115 	info.shared_area = create_area("vesa shared info",
116 		(void **)&info.shared_info, B_ANY_KERNEL_ADDRESS,
117 		ROUND_TO_PAGE_SIZE(sharedSize + modesSize), B_FULL_LOCK,
118 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_USER_CLONEABLE_AREA);
119 	if (info.shared_area < B_OK)
120 		return info.shared_area;
121 
122 	vesa_shared_info &sharedInfo = *info.shared_info;
123 
124 	memset(&sharedInfo, 0, sizeof(vesa_shared_info));
125 
126 	if (modes != NULL) {
127 		sharedInfo.vesa_mode_offset = sharedSize;
128 		sharedInfo.vesa_mode_count = modesSize / sizeof(vesa_mode);
129 
130 		memcpy((uint8*)&sharedInfo + sharedSize, modes, modesSize);
131 	}
132 
133 	sharedInfo.frame_buffer_area = bufferInfo->area;
134 	sharedInfo.frame_buffer = (uint8 *)bufferInfo->frame_buffer;
135 
136 	sharedInfo.current_mode.virtual_width = bufferInfo->width;
137 	sharedInfo.current_mode.virtual_height = bufferInfo->height;
138 	sharedInfo.current_mode.space = get_color_space_for_depth(
139 		bufferInfo->depth);
140 	sharedInfo.bytes_per_row = bufferInfo->bytes_per_row;
141 
142 	// TODO: we might want to do this via vm86 instead
143 	edid1_info *edidInfo = (edid1_info *)get_boot_item(VESA_EDID_BOOT_INFO,
144 		NULL);
145 	if (edidInfo != NULL) {
146 		sharedInfo.has_edid = true;
147 		memcpy(&sharedInfo.edid_info, edidInfo, sizeof(edid1_info));
148 	}
149 
150 	physical_entry mapping;
151 	get_memory_map((void *)sharedInfo.frame_buffer, B_PAGE_SIZE,
152 		&mapping, 1);
153 	sharedInfo.physical_frame_buffer = (uint8 *)mapping.address;
154 
155 	dprintf(DEVICE_NAME ": vesa_init() completed successfully!\n");
156 	return B_OK;
157 }
158 
159 
160 void
161 vesa_uninit(vesa_info &info)
162 {
163 	dprintf(DEVICE_NAME": vesa_uninit()\n");
164 
165 	delete_area(info.shared_info->frame_buffer_area);
166 	delete_area(info.shared_area);
167 }
168 
169 
170 status_t
171 vesa_set_display_mode(vesa_info &info, unsigned int mode)
172 {
173 	if (mode >= info.shared_info->vesa_mode_count)
174 		return B_ENTRY_NOT_FOUND;
175 
176 	// Prepare vm86 mode environment
177 	struct vm86_state vmState;
178 	status_t status = vm86_prepare(&vmState, 0x20000);
179 	if (status != B_OK) {
180 		dprintf(DEVICE_NAME": vesa_set_display_mode(): vm86_prepare failed\n");
181 		return status;
182 	}
183 
184 	area_id newFBArea;
185 	frame_buffer_boot_info *bufferInfo;
186 	struct vbe_mode_info modeInfo;
187 
188 	// Get mode information
189 	status = vbe_get_mode_info(&vmState, info.modes[mode].mode, &modeInfo);
190 	if (status != B_OK) {
191 		dprintf(DEVICE_NAME": vesa_set_display_mode(): cannot get mode info\n");
192 		goto error;
193 	}
194 
195 	// Set mode
196 	status = vbe_set_mode(&vmState, info.modes[mode].mode);
197 	if (status != B_OK) {
198 		dprintf(DEVICE_NAME": vesa_set_display_mode(): cannot set mode\n");
199 		goto error;
200 	}
201 
202 	// Map new frame buffer
203 	void *frameBuffer;
204 	newFBArea = map_physical_memory("vesa_fb",
205 		(void *)modeInfo.physical_base,
206 		modeInfo.bytes_per_row * modeInfo.height, B_ANY_KERNEL_ADDRESS,
207 		B_READ_AREA | B_WRITE_AREA, &frameBuffer);
208 	if (newFBArea < B_OK) {
209 		status = (status_t)newFBArea;
210 		goto error;
211 	}
212 	delete_area(info.shared_info->frame_buffer_area);
213 
214 	// Turn on write combining for the area
215 	vm_set_area_memory_type(newFBArea, modeInfo.physical_base, B_MTR_WC);
216 
217 	// Update shared frame buffer information
218 	info.shared_info->frame_buffer_area = newFBArea;
219 	info.shared_info->frame_buffer = (uint8 *)frameBuffer;
220 	info.shared_info->physical_frame_buffer = (uint8 *)modeInfo.physical_base;
221 	info.shared_info->bytes_per_row = modeInfo.bytes_per_row;
222 	info.shared_info->current_mode.virtual_width = modeInfo.width;
223 	info.shared_info->current_mode.virtual_height = modeInfo.height;
224 	info.shared_info->current_mode.space = get_color_space_for_depth(
225 		modeInfo.bits_per_pixel);
226 
227 	// Update boot item as it's used in vesa_init()
228 	bufferInfo
229 		= (frame_buffer_boot_info *)get_boot_item(FRAME_BUFFER_BOOT_INFO, NULL);
230 	bufferInfo->area = newFBArea;
231 	bufferInfo->frame_buffer = (addr_t)frameBuffer;
232 	bufferInfo->width = modeInfo.width;
233 	bufferInfo->height = modeInfo.height;
234 	bufferInfo->depth = modeInfo.bits_per_pixel;
235 	bufferInfo->bytes_per_row = modeInfo.bytes_per_row;
236 
237 error:
238 	vm86_cleanup(&vmState);
239 	return status;
240 }
241 
242